面试题53:在排序数组中查找数字
题目一:数字在排序数组中出现的次数。
解题方案1:常规解法,从头遍历;
解题方案2:二分查找法,若数字小于中间数字然后在左边继续查找,若大于在右边继续查找,若相等就可以向前向后遍历出现的次数了。
题目二:0~n-1中缺失的数字。
解题方案1:汇总数组中所有数字之和,汇总0~n-1中所有数字之和,两者之差就是缺失值。
解题方案2:二分查找,若中间数字不等于下标,判断它的前一个数字是否和下标相等,相等则此下标就是缺失值,不等继续在左边查找;若中间数字等于下标在右边继续查找。
题目三:数组中数值和下标相等的元素。
解题方案:二分查找,若中间数字等于下标则找到了元素,若大于在左边查找,若小于在右边查找。
面试题54:二叉搜索树的第K大节点
解题方案:中序遍历到K个元素。
面试题55:二叉树的深度
题目一:二叉树的深度。
解题方案:前序遍历。
private int getNodeLength1(TreeNode node) {
if (node == null) {
return 0;
}
int left = getNodeLength1(node.getLeft());
int right = getNodeLength1(node.getRight());
return left > right ? left + 1 : right + 1;
}
题目二:平衡二叉树,输入根节点,判断该树是不是平衡二叉树。
解题方案1:递归
public void fiftyFifth1() {
TreeNode rootNode = TreeNode.createSelectTree();
System.out.println(isBalance(rootNode));
}
private boolean isBalance(TreeNode node) {
if (node == null) {
return true;
}
int left = getNodeLength1(node.getLeft());
int right = getNodeLength1(node.getRight());
if (Math.abs(left - right) > 1) {
return false;
}
return isBalance(node.getLeft()) && isBalance(node.getRight());
}
private int getNodeLength1(TreeNode node) {
if (node == null) {
return 0;
}
int left = getNodeLength1(node.getLeft());
int right = getNodeLength1(node.getRight());
return left > right ? left + 1 : right + 1;
}
解题方案2:非递归
public void fiftyFifth2() {
TreeNode rootNode = TreeNode.createSelectTree();
getNodeLength2(rootNode);
System.out.println(flag);
}
private static boolean flag = true;
private static int getNodeLength2(TreeNode node) {
if (node == null) {
return 0;
}
int left = getNodeLength2(node.getLeft());
if (flag) {
int right = getNodeLength2(node.getRight());
if (flag) {
flag = Math.abs(left - right) <= 1;
return left > right ? left + 1 : right + 1;
}
}
return 0;
}
面试题56:数组中数字出现的次数
题目一:数组中只出现一次的两个数字,其他数字均出现两次,要求时间复杂度为O(n),空间复杂度为O(1)。
解题方案:将所有数字进行一次异或,可以得到这两个数字的异或值。找到这个异或值转二进制后的低位1的位置,将数组按着这个低位1的位置是否为1分为两个子数组,然后对这两个数组分别进行异或,即可得到两个只出现一次的数字。
public void fiftySixth() {
int[] arrays = new int[]{1, 2, 3, 4, 55, 66, 55, 66, 5, 1, 2, 3};
findNumberApperOnce(arrays);
}
private void findNumberApperOnce(int[] arrays) {
if (arrays == null) {
return;
}
int result = 0;
for (int i = 0; i < arrays.length; i++) {
// 异或
result ^= arrays[i];
}
// result为两个数异或的结果,从低位开始找到第一次出现的1所在的位
int bit = 1;
while ((result & bit) == 0 && bit <= result && bit > 0) {
bit = bit << 1;
}
int number1 = 0;
int number2 = 0;
for (int i = 0; i < arrays.length; i++) {
if ((arrays[i] & bit) == bit) {
number1 ^= arrays[i];
} else {
number2 ^= arrays[i];
}
}
System.out.println(number1);
System.out.println(number2);
}
题目二:数组中唯一只出现一次的数字,其他数字均出现三次,要求时间复杂度为O(n),空间复杂度为O(1)。
解题方案:把所有数字按照数字转二进制的位数分别进行累加,每位的和如能被三整除说明那个数字在这一位是0,否则是1。
面试题57:和为S的数字
题目一:和为S的两个数字,输入一个递增排序的数组,输出任意一对即可。
解题方案:定义一个指针指向开头,定义另一个指针指向末尾,两个指针逐渐向中间靠拢。
题目二:和为S的连续正整数序列,输入一个正整数,打印所有和为S的连续正数序列(至少包含两个数字)
解题方案:也是定义两个指针,一个指向1,一个指向2,判断两个指针之间数字之和,小于S将指针2向后移动一位,大于将指针1向后移动一位,相等就打印并将指针1向后移动一位继续判断,一直将指针向后移动到(S+1)/2为止。
public void fiftySeventh() {
findNumber(9);
System.out.println("--------");
findNumber(15);
}
private void findNumber(int sum) {
if (sum < 3) {
return;
}
int mid = sum >> 1;
int begin = 1;
int end = 2;
int temp = begin + end;
while (begin <= mid) {
if (temp == sum) {
printNum(begin, end);
end++;
temp += end;
} else if (temp < sum) {
end++;
temp += end;
} else if (temp > sum) {
temp -= begin;
begin++;
}
}
}
private void printNum(int begin, int end) {
for (int i = begin; i <= end; i++) {
System.out.print(i);
}
System.out.println();
}
结果:
234
45
12345
456
78
面试题58:翻转字符串
题目一:翻转单词顺序,输入“I am a student.” 输出 “student. a am I”
解题方案:先反转整个句子,然后再以空格隔开句子,分别再次翻转单词。
题目二:左旋转字符串,输入“abcdefg”,左旋两位,输出“cdefgab”。
解题方案:前两位翻转,后面的数字也翻转,得到“bagfedc”,再翻转这个字符串。
面试题59:队列的最大值
题目一:滑动窗口的最大值。利用一个辅助双向队列存储数组的下标,向右移动一格,将新数据入队列,并移除队列中的元素(超过窗口值的元素、小于新数据的元素)
题目二:队列的最大值,要求push、pop、max时间复杂度都是O(1)。
解题方案:利用一个辅助队列,push时将队列中所有小于插入值的元素出辅助队列,然后把插入值入辅助队列;pop时,若辅助队列尾值等于pop值,就将尾值出辅助队列。
面试题64:求1+2+3+…+n
题目:不能使用循环等条件表达式。
解题方案:递归循环,无法解决输入值小于0的情况
public void sixthForth() {
System.out.println(getSum(100));
}
private int getSum(int num) {
int sum = num;
boolean flag = num > 1 && (sum += getSum(num - 1)) > 0;
return sum;
}