* 面试题53:在排序数组中查找数字
* 题目一:数字在排序数组中出现的次数
* 统计一个数字在排序数组中出现的次数。
* 例如:输入排序数组{1,2,3,3,3,3,4,5}和数字3,
* 由于3在这个数组中出现4次,因此输出4
*
* 二分查找/折半查找 https://www.cnblogs.com/yedushusheng/p/5524166.html
*
* 思路: 时间复杂度O(logn) 空间复杂度O(1)
* 因为查找的是有序表,左移确定重复出现的第一个k和最后一个k的位置,然后就能够得到出现次数了
*使用二分查找法的思想进行查找
* 在数组中找到第一个k:
* 二分查找中就是在与中间位置进行比较,所以首先计算中间索引值,然后得到数组的中间值,
* 判断与k的关系: 若等于,看其是否为第一个k,是的情况:(两种情况:根据前一个元素是否存在区分,一种是没有前一个元素,则索引为0;一种是有前一个元素,索引大于0,且前一个值不等于k),则返回中间值即可
* 否的情况,则将结束节点赋值为中间的前一位
* 若小于,则将起始位赋值为中间位置的后一位
* 若大于,则将结束位赋值为中间位置的前一位
* 然后接着判断被分割后的部分是怎样的情况(也就是与k的关系,直到找到第一个k为止)
*
* 在数组中找到最后一个k:同找第一个k类似
package Test;
public class No53GetNumberOfK {
/*
* 面试题53:在排序数组中查找数字
* 题目一:数字在排序数组中出现的次数
* 统计一个数字在排序数组中出现的次数。
* 例如:输入排序数组{1,2,3,3,3,3,4,5}和数字3,
* 由于3在这个数组中出现4次,因此输出4
*
* 二分查找/折半查找 https://www.cnblogs.com/yedushusheng/p/5524166.html
*
* 思路: 时间复杂度O(logn) 空间复杂度O(1)
* 因为查找的是有序表,左移确定重复出现的第一个k和最后一个k的位置,然后就能够得到出现次数了
*使用二分查找法的思想进行查找
* 在数组中找到第一个k:
* 二分查找中就是在与中间位置进行比较,所以首先计算中间索引值,然后得到数组的中间值,
* 判断与k的关系: 若等于,看其是否为第一个k,是的情况:(两种情况:根据前一个元素是否存在区分,一种是没有前一个元素,则索引为0;一种是有前一个元素,索引大于0,且前一个值不等于k),则返回中间值即可
* 否的情况,则将结束节点赋值为中间的前一位
* 若小于,则将起始位赋值为中间位置的后一位
* 若大于,则将结束位赋值为中间位置的前一位
* 然后接着判断被分割后的部分是怎样的情况(也就是与k的关系,直到找到第一个k为止)
*
* 在数组中找到最后一个k:同找第一个k类似
*
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No53GetNumberOfK g = new No53GetNumberOfK();
int[] array = {1,2,3,3,3,3,3,4,5};
int k = 3;
System.out.println("数组中"+k+"出现了"+g.GetNumberOfK(array,k)+"次!!!");
}
//统计数字k出现的次数
private int GetNumberOfK(int[] array, int k) {
// TODO Auto-generated method stub
int number = 0;
if(array != null && array.length != 0) {
int first = GetFirstNumberOfK(array,k,0,array.length - 1);
int last = GetLastNumberOfK(array,k,0,array.length - 1);
if(first > -1 && last > -1)
number = last-first+1;
}
return number;
}
//获取最后一个k的索引
private int GetLastNumberOfK(int[] array, int k, int start, int end) {
// TODO Auto-generated method stub
if(start > end)
return -1;
int midIndex = (start+end)/2;
int midData = array[midIndex];
if(midData == k) {
if(array[midIndex+1] != k && midIndex < array.length-1 || midIndex == array.length-1)
return midIndex;
else
start = midIndex+1;
}
else if(midData > k)
end = midIndex -1;
else
start = midIndex +1;
return GetLastNumberOfK(array,k,start,end);
}
//获得第一个k的索引
private int GetFirstNumberOfK(int[] array, int k, int start, int end) {
// TODO Auto-generated method stub
if(start > end)
return -1;
int midIndex = (start+end)/2;
int midData = array[midIndex];
//根据二分查找方法
if(midData == k) {
//若是第一个k值(有-1的位置) 或者 已经到达了数组头部(也是第一个看的含义 没有了-1的位置) 则直接返回即可
if(midIndex > 0 && array[midIndex-1] != k || midIndex == 0)
return midIndex;
//因为要求第一个k 所以要将范围缩小在前半部分
else
end = midIndex - 1;
}
// midData > k 表示在左边
else if(midData > k) {
end = midIndex - 1;
}
// midData < k 表示在右边
else
start = midIndex + 1;
return GetFirstNumberOfK(array,k,start,end);
}
}
* 题目二:0~n-1中缺失的数字
* 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。
* 在范围0~n-1内的 n个数字中有且只有一个 数字不在该数组中,请找出这个数字
*
*
* 思路:时间复杂度O(logn) 空间复杂度O(1)
* 因为0~n-1这些数字都是排序的,所以在数组中开始是从0,1,2,3,。。。,刚开始是与索引值相等的
* 但是若某一值m缺失了,那么,m索引处就不同了,不再是m,并且m+1位置也不同了,。。。
*
* 因此,可以通过二分查询的算法对其进行判断:
* 判断中间位进行判断,判断这个值是否与索引值相等:
* 若不想等,则判断前一位是否相等,若想等,则表示这就是缺失的那一位;若不想等,则表示缺失的数字在数组的左侧
*若相等,则缺失的数字在右侧,则继续判断右侧的数字
package Test;
public class No53Second_GetMissingNumber {
/*
* 面试题53:在排序数组中查找数字
* 题目二:0~n-1中缺失的数字
* 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。
* 在范围0~n-1内的 n个数字中有且只有一个 数字不在该数组中,请找出这个数字
*
*
* 思路:时间复杂度O(logn) 空间复杂度O(1)
* 因为0~n-1这些数字都是排序的,所以在数组中开始是从0,1,2,3,。。。,刚开始是与索引值相等的
* 但是若某一值m缺失了,那么,m索引处就不同了,不再是m,并且m+1位置也不同了,。。。
*
* 因此,可以通过二分查询的算法对其进行判断:
* 判断中间位进行判断,判断这个值是否与索引值相等:
* 若不想等,则判断前一位是否相等,若想等,则表示这就是缺失的那一位;若不想等,则表示缺失的数字在数组的左侧
*若相等,则缺失的数字在右侧,则继续判断右侧的数字
*
*
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No53Second_GetMissingNumber s = new No53Second_GetMissingNumber();
int[] array = {0,1,2,3,-1,4,5};
System.out.println("数组中缺失的数字是: "+s.GetMissingNumber(array));
}
private int GetMissingNumber(int[] array) {
// TODO Auto-generated method stub
if(array == null || array.length == 0)
return -1;
int left = 0;
int right = array.length - 1;
while(left <= right) {
int mid = (left + right)/2;
if(mid == array[mid])
left = mid + 1;
else {
if(mid-1 == array[mid-1])
return mid;
else
right = mid -1;
}
}
//若left指向最后 则表示最后一位是缺失的数字
if(left == array.length)
return array.length;
//无效输入:数组不是按要求排序的、有数字不在0~n-1范围内
return -1;
}
}
* 面试题53:在排序数组中查找数字
* 题目三:数组中数值和下表相等的元素
* 假设一个单调递增的数组里的每一个元素都是整数并且是唯一的。
* 请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。
* 例如:在数组{-3,-1,1,3,5}中,数字3它的下标相等。
*
* 思路:同样运用二分查询的算法进行查找
* 取中间值m和下标i进行比较:
* 若m>i,数字大于下标,则可能相等的数字 在左侧
* 若m<i,数字小于下标,则可能相等的数字 在右侧
* 若m==i,找到数字与下标相等
package Test;
public class No53Third_GetNumberSameAsIndex {
/*
* 面试题53:在排序数组中查找数字
* 题目三:数组中数值和下表相等的元素
* 假设一个单调递增的数组里的每一个元素都是整数并且是唯一的。
* 请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。
* 例如:在数组{-3,-1,1,3,5}中,数字3它的下标相等。
*
* 思路:同样运用二分查询的算法进行查找
* 取中间值m和下标i进行比较:
* 若m>i,数字大于下标,则可能相等的数字 在左侧
* 若m<i,数字小于下标,则可能相等的数字 在右侧
* 若m==i,找到数字与下标相等
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No53Third_GetNumberSameAsIndex g = new No53Third_GetNumberSameAsIndex();
int[] array = {-3,-1,1,3,5};
//其中下标是从0~n-1,并且返回的是下标值 ********
System.out.println("数组中数字和其下表相等的位置是:"+g.GetNumberSameAsIndex(array));
}
private int GetNumberSameAsIndex(int[] array) {
// TODO Auto-generated method stub
if(array == null || array.length == 0)
return -1;
int left = 0;
int right = array.length -1;
while(left <= right) {
int mid = (left + right)/2;
if(mid == array[mid])
return mid;
else if(mid > array[mid])
left = mid+1;
else
right = mid-1;
}
return -1;
}
}
* 面试题54:二叉搜索树的第K大节点
* 题目:给定一个二叉搜索树,请找出其中第K大的节点。
* 例如:在5->(left)3 5->(right)7 的二叉搜索树中,
* 3->(left)2 3->(right)4
* 7->(left)6 7->(right)8
* 按节点数值大小排序,第三大节点的值是4
*
*
* 思路:使用中序遍历二叉树的方法,进行遍历。
* {2,3,4,5,6,7,8}则将其以递增的形式输出,则此数组中第3个就是要找的
package Test;
import Test.No32First_PrintBinaryTreeFromTopToBottom.BinaryTreeNode;
public class No54KthNode {
/*
* 面试题54:二叉搜索树的第K大节点
* 题目:给定一个二叉搜索树,请找出其中第K大的节点。
* 例如:在5->(left)3 5->(right)7 的二叉搜索树中,
* 3->(left)2 3->(right)4
* 7->(left)6 7->(right)8
* 按节点数值大小排序,第三大节点的值是4
*
*
* 思路:使用中序遍历二叉树的方法,进行遍历。
* {2,3,4,5,6,7,8}则将其以递增的形式输出,则此数组中第3个就是要找的
*
*
* */
static class BinaryTreeNode{
int val;
BinaryTreeNode left;
BinaryTreeNode right;
BinaryTreeNode(int val){
this.val = val;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
No54KthNode k = new No54KthNode();
BinaryTreeNode root1 = new BinaryTreeNode(5);
BinaryTreeNode one = new BinaryTreeNode(3);
BinaryTreeNode two = new BinaryTreeNode(7);
BinaryTreeNode three = new BinaryTreeNode(2);
BinaryTreeNode four = new BinaryTreeNode(4);
BinaryTreeNode five = new BinaryTreeNode(6);
BinaryTreeNode six = new BinaryTreeNode(8);
root1.left = one;
root1.right = two;
one.left = three;
one.right = four;
two.left = five;
two.right = six;
three.left = null;
three.right = null;
four.left = null;
four.right = null;
five.left = null;
five.right = null;
six.left = null;
six.right = null;
int index = 3;
System.out.println("按照节点数值大小进行排序,数组中第"+index+"大节点的值是:"+k.GetKthNode(root1,index).val);
}
int count = 0;
private BinaryTreeNode GetKthNode(BinaryTreeNode root1, int index) {
// TODO Auto-generated method stub
if(root1 == null || index <= 0)
return null;
//中序遍历二叉树 左 根 右
//左
BinaryTreeNode node = GetKthNode(root1.left,index);
//在中序遍历时,总是走到了最左边的叶子节点 然后 叶子节点的根 (若存在) 然后根的右子节点
if(node != null)//返回给上一级调用这个函数的地方 其父节点
return node;
//根 类似于中序遍历时,打印根节点的值一样的作用 然后当找到了第index大的元素时,则返回给原函数
count ++;
if(count == index)
return root1;
//右
node = GetKthNode(root1.right,index);
if(node != null)
return node;
return null;
}
}
* 面试题55:二叉树的深度
* 题目一:二叉树的深度
* 输入一棵二叉树的根节点,求树的深度。
* 从根节点到叶节点依次经过的节点(含根、叶节点)
* 形成树的一条路径,最长路径的长度为树的深度。
*
*
* 思路:计算树的深度有以下几种几种情况:
* 1>若只有根节点,则深度为1
* 2>若根节点只有右子树,则根节点深度=右子树深度+1
* 3>若根节点只有左子树,则根节点深度=左子树深度+1
* 4>若根节点有左、右子树,则根节点深度=max{左子树深度,右子树深度} +1
package Test;
import Test.No54KthNode.BinaryTreeNode;
public class No55First_TreeDepth {
/*
* 面试题55:二叉树的深度
* 题目一:二叉树的深度
* 输入一棵二叉树的根节点,求树的深度。
* 从根节点到叶节点依次经过的节点(含根、叶节点)
* 形成树的一条路径,最长路径的长度为树的深度。
*
*
* 思路:计算树的深度有以下几种几种情况:
* 1>若只有根节点,则深度为1
* 2>若根节点只有右子树,则根节点深度=右子树深度+1
* 3>若根节点只有左子树,则根节点深度=左子树深度+1
* 4>若根节点有左、右子树,则根节点深度=max{左子树深度,右子树深度} +1
*
* */
static class BinaryTreeNode{
int val;
BinaryTreeNode left;
BinaryTreeNode right;
BinaryTreeNode(int val){
this.val = val;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
No55First_TreeDepth f = new No55First_TreeDepth();
BinaryTreeNode root1 = new BinaryTreeNode(5);
BinaryTreeNode one = new BinaryTreeNode(3);
BinaryTreeNode two = new BinaryTreeNode(7);
BinaryTreeNode three = new BinaryTreeNode(2);
BinaryTreeNode four = new BinaryTreeNode(4);
BinaryTreeNode five = new BinaryTreeNode(6);
BinaryTreeNode six = new BinaryTreeNode(8);
root1.left = one;
root1.right = two;
one.left = three;
one.right = four;
two.left = null;
two.right = six;
three.left = null;
three.right = null;
four.left = five;
four.right = null;
five.left = null;
five.right = null;
six.left = null;
six.right = null;
System.out.println("二叉树的深度为:"+f.TreeDepth(root1));
}
//因为是从为null的节点开始计数,叶节点是由为空的节点+1的来,以此类推
private int TreeDepth(BinaryTreeNode root1) {
// TODO Auto-generated method stub
if(root1 == null)
return 0;
int left = TreeDepth(root1.left);
int right =TreeDepth(root1.right);
return (left > right)?(left+1):(right+1);
}
}
* 面试题55:二叉树的深度
* 题目二:平衡二叉树
* 输入一棵二叉树的根节点,判断该树是不是平衡二叉树。
* 如果某二叉树中二年以节点的左右子树的深度相差不超过1,
* 那么它就是一颗平衡二叉树
*
* 思路:
* 方法一:每个节点会遍历多次
* 在求二叉树深度,对其进行比较保证其左右子树差不超过1
*优点:简洁
*缺点:某一个节点会被重复遍历多次,时间效率低
*
* 方法二;每个节点遍历一次
* 可以采用后序遍历 左右根的顺序
* 遍历每个节点的时候记录其深度,
* 这样就可以一边遍历,一边判断深度的大小了
package Test;
import Test.No55First_TreeDepth.BinaryTreeNode;
public class No55Second_isBalanced {
/*
* 面试题55:二叉树的深度
* 题目二:平衡二叉树
* 输入一棵二叉树的根节点,判断该树是不是平衡二叉树。
* 如果某二叉树中二年以节点的左右子树的深度相差不超过1,
* 那么它就是一颗平衡二叉树
*
* 思路:
* 方法一:每个节点会遍历多次
* 在求二叉树深度,对其进行比较保证其左右子树差不超过1
*优点:简洁
*缺点:某一个节点会被重复遍历多次,时间效率低
*
* 方法二;每个节点遍历一次
* 可以采用后序遍历 左右根的顺序
* 遍历每个节点的时候记录其深度,
* 这样就可以一边遍历,一边判断深度的大小了
*
*
*
* */
static class BinaryTreeNode{
int val;
BinaryTreeNode left;
BinaryTreeNode right;
BinaryTreeNode(int val){
this.val = val;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
No55Second_isBalanced s = new No55Second_isBalanced();
BinaryTreeNode root1 = new BinaryTreeNode(5);
BinaryTreeNode one = new BinaryTreeNode(3);
BinaryTreeNode two = new BinaryTreeNode(7);
BinaryTreeNode three = new BinaryTreeNode(2);
BinaryTreeNode four = new BinaryTreeNode(4);
BinaryTreeNode five = new BinaryTreeNode(6);
BinaryTreeNode six = new BinaryTreeNode(8);
root1.left = one;
root1.right = two;
one.left = three;
one.right = four;
two.left = null;
two.right = six;
three.left = null;
three.right = null;
four.left = five;
four.right = null;
five.left = null;
five.right = null;
six.left = null;
six.right = null;
System.out.println("方法一:");
if(s.isBalanced_Solution1(root1))
System.out.println("该二叉树是平衡二叉树!");
else
System.out.println("该二叉树不是平衡二叉树!!!!");
System.out.println("方法二:");
if(s.isBalanced_Solution2(root1))
System.out.println("该二叉树是平衡二叉树!");
else
System.out.println("该二叉树不是平衡二叉树!!!!");
}
//方法二;每个节点仅遍历一次
private boolean isBalanced_Solution2(BinaryTreeNode root1) {
// TODO Auto-generated method stub
if(root1 == null)
{
return true;
}
int res = treeDepth(root1);
if(res == -1)
return false;
return true;
}
//计算深度
private int treeDepth(BinaryTreeNode root1) {
// TODO Auto-generated method stub
if(root1 == null)
return 0;
int leftDepth = treeDepth(root1.left);
if(leftDepth == -1)
return -1;
int rightDepth = treeDepth(root1.right);
if(rightDepth == -1)
return -1;
//判断该节点是否平衡
int diff = leftDepth - rightDepth;
if(diff > 2 || diff < -2)
return -1;
//返回根结点的长度 : 左右子树中较大值+1
return (leftDepth>rightDepth)?(leftDepth+1):(rightDepth+1);
}
//方法一:重复遍历节点
private boolean isBalanced_Solution1(BinaryTreeNode root1) {
// TODO Auto-generated method stub
if(root1 == null)
return true;
int left = TreeDepth(root1.left);
int right = TreeDepth(root1.right);
int diff = left - right;
if(diff < -1 || diff >2)
return false;
return isBalanced_Solution1(root1.left) && isBalanced_Solution1(root1.right);
}
//计算树的深度
private int TreeDepth(BinaryTreeNode root1) {
// TODO Auto-generated method stub
if(root1 == null)
return 0;
int left = TreeDepth(root1.left);
int right =TreeDepth(root1.right);
return (left > right)?(left+1):(right+1);
}
}