面试题53_1:在排序数组中查找数字
因数组有序,可以考虑二分查找,但二分查找后还需顺序查找第一个k和最后一个k,可以在二分查找的过程中对是否为第一个或最后一个k进行判断。最终的复杂度控制在o(logn); |
public static int getApperanceNumber(int[] nums,int k){ if (nums == null || nums.length == 0){ return 0; } if (nums.length == 1){ if (k == nums[0]){ return 1; }else { return 0; } } int firstindex = getFirstIndex(nums,k); int lastindex = getLastIndex(nums,k); if (firstindex != -1 && lastindex!=-1){ int reback = lastindex-firstindex+1; if (reback>= 0){ return reback; }else { return 0; } } return 0; } public static int getFirstIndex(int[] nums,int k){ int low = 0; int high = nums.length -1; while (low <= high){ int mid = (low+high)/2; if (k == nums[mid]){ for (int x = mid;x>=0;x--){ if (nums[x] != k){ return x+1; } if (x == 0){ return 0; } } } if (k < nums[mid]){ high = mid-1; } if (k > nums[mid]){ low = mid+1; } } return -1; } public static int getLastIndex(int[] nums,int k){ int low = 0; int high = nums.length -1; while (low <= high){ int mid = (low+high)/2; if (k == nums[mid]){ for (int x = mid;x<=nums.length-1;x++){ if (nums[x] != k){ return x-1; } if (x == nums.length-1){ return x; } } } if (k < nums[mid]){ high = mid-1; } if (k > nums[mid]){ low = mid+1; } } return -1; }
53_2:0-(n-1)中的数字
求出0-(n-1)原本的和,再求数组的和,相减过后得到差得那个数,o(n)但没有利用数组按顺序排列的特点 |
缺失的那个数下标与原数不同,且之前相同,之后不同;利用二分来找到第一个下标不同的数,o(logn) |
public static int getMissingNum(int[] nums){ if (nums == null || nums.length == 0){ return -1; } int low = 0; int high = nums.length-1; while (low <= high){ int mid = (low+high)/2; if (nums[mid] != mid){ if (mid == 0){ return mid; } if (nums[mid-1] != mid-1){ high = mid-1; }else { return mid; } }else { low = mid +1; } } return nums.length; }
面试题53_3:数组中数值和下标相等的元素
数组有序,则可以用二分查找来优化。o(logn) |
public static Integer getIndexSameNum(int[] nums){ if (nums == null || nums.length == 0){ return null; } if (nums.length == 1){ if (nums[0] == 0){ return 0; }else { return null; } } int low = 0; int high = nums.length-1; while (low <= high){ int mid = (low+high)/2; if (nums[mid] == mid){ return mid; }else if (nums[mid] > mid){ high = mid-1; }else { low = mid+1; } } return null; }
面试题54:二叉搜索树的第k大节点
中序遍历 |
//递归中传递参数:当返回类型,函数参数都不好用时,直接设置一个大小为1的数组进行值得传递;
public static Node getKmaxNum(Node root,int k){ if (root == null || k <= 0){ return null; } int[] temp = {k}; return getKmaxNum_core(root,temp); } public static Node getKmaxNum_core(Node root,int[] temp){ Node reback = null; if (root.left != null){ reback = getKmaxNum_core(root.left,temp); } if (reback == null){ if (temp[0] == 1){ reback = root; } temp[0]--; } if (reback == null && root.right!= null){ reback = getKmaxNum_core(root.right,temp); } return reback; }
面试题55_1:二叉树的深度
二叉树的深度可以依靠左右子树的深度递归求解 |
public static int getTreeDeep(Node root){ if (root == null){ return 0; } int left = getTreeDeep(root.left); int right = getTreeDeep(root.right); return left > right ? left+1:right+1; }
面试题55_2:平衡二叉树
求出左右子树的深度,判断,再递归判断左右子树是否为平衡的,存在重复判断的情况 |
后续遍历,先检查左右子树,并记录深度,从叶子节点开始检查 |
public static boolean judgeBlanceTree(Node root){ if (root == null) { return false; } judgeBlanceTree_core(root); return isBlanced; } public static int judgeBlanceTree_core(Node root){ if (root == null){ isBlanced = true; return 0; } int left = judgeBlanceTree_core(root.left); int right = judgeBlanceTree_core(root.right); int depth =( left > right?left:right)+1; if (Math.abs(left-right)<=1){ isBlanced = true; }else { isBlanced = false; } return depth; }
面试题56_1:数组中数字出现的次数
任何一个数字异或它自己 = 0;遍历数组,若只有一个数出现一次,通过异或的结果可以得到此数 存在两个只出现一次的数时需要将数组分为两个,使每个只包含一个; 分数组的方式为:遍历异或数组后的值为两个孤儿的异或值,因不相同,则至少有1位为1,取最低的1位,在此位两个数相反,故按此位是否为0,将原数组划分为2组,再对两组分别求孤儿; |
//无法判断数组中是否有重复的,或有多于2个的重复;如果需要判断此步,在第一次遍历过程中需要用hashmap将出现次数存起来。但也就得到出现次数为1的了
public static int[] notDuplicateNums(int[] nums){ int[] reback = new int[2]; if (nums == null || nums.length <= 1){ return reback; } if (nums.length == 2){ if (nums[0] == nums[1]){ return reback; }else { reback[0] = nums[0]; reback[1] = nums[1]; return reback; } } int exclusive = countYH(nums); int firstdigt = getFirstDigt(exclusive); for (int x :nums){ if (isBit1(x,firstdigt) == 1){ reback[0] = reback[0]^x; }else { reback[1] = reback[1]^x; } } return reback; } public static int countYH(int[] nums){ int exclusive = 0; for (int x : nums){ exclusive = exclusive^x; } return exclusive; } public static int getFirstDigt(int num){ int indexBit = 0; while (((num&1) == 0 )&& (indexBit < 32)){ num = num>>1; indexBit++; } return indexBit; } public static int isBit1(int num,int firstdigt){ num = num>>firstdigt; return num&1; }
面试题56_2:数组中唯一只出现一次的数字
hashmap存储出现次数 |
位运算:每位的和加起来应是3的倍数,若不是,则只出现1次的数此位为1;正负无影响 |
public static int getNoDuplicated(int[] nums){ if (nums == null || nums.length == 0){ error = true; } int[] bitsum = new int[32]; for (int x = 0; x<=nums.length-1;x++){ int bitmash = 1; for (int j = 31;j>=0;j--){ int bit = nums[x] & bitmash; if (bit != 0){ bitsum[j] += 1; } bitmash = bitmash<<1; } } int result = 0; for (int x =0 ;x<32;x++){ result = result<<1; result += bitsum[x]%3; } return result; }
面试题57_1:和为s的数字
从头开始遍历数组,将当前数与剩下的数进行比较,寻找和相等的 | o(n[2]) |
由于数组有序,设置两个指针,一个指向第一个元素,一个指向最后一个元素,根据和调整这两个指针 | o(n) |
public static int[] judgeSum(int[] nums,int sum){ if (nums == null || nums.length<2 ){ return null; } int[] reback = new int[2]; int start = 0; int end = nums.length - 1; while (start < end){ int temp = nums[start]+nums[end]; if (temp == sum){ reback[0] = nums[start]; reback[1] = nums[end]; return reback; }else if (temp < sum){ start++; }else { end -- ; } } return null; }
面试题57_2:和为s的连续正数序列
由于是等差数列,可以化简为找到头尾两个数,第一个指针指向第一个元素,第二个指针指向第二个元素,因为必须含两个元素,故直到较小元素到达(1+s)/2为止 |
public static void FindContinusSequence(int sum){ if (sum <= 0){ return; } int start = 1; int end = 2; while (start < (1+sum)/2){ float temp = ((float)(start + end))*(end-start+1)/2; if (temp == sum){ for (int x = start;x<=end;x++){ System.out.print(x); } System.out.print("\n"); end++; start++; }else if (temp < sum){ end++; }else { start++; } } return; }
面试题58_1:翻转字符串
先将字符串全部翻转,再依据空格将每个单词翻转回来。也可以使用java中的split函数来先进行分割,再在词的基础上进行翻转。额外需要用空间; |
public static String ReverseString(String st){ if (st == null || st.length() == 0){ return ""; } if (st.length() == 1){ return st; } String[] st_li = st.split(" "); int start = 0; int end = st_li.length-1; StringBuilder stb = new StringBuilder(); while (start < end) { stb.append(st_li[end]); st_li[end] = st_li[start]; st_li[start] = stb.substring(0,stb.length()); stb.delete(0,stb.length()); start++; end--; } return String.join(" ",st_li); }
面试题58_2:左旋转字符串
StringBuilder的思路更简洁 |
public static String LeftRotate(String st ,int index){ if (st == null||st.length() == 0||index>st.length()-1||index<0){ return ""; } if (index == 0 || index == st.length()){ return st; } StringBuilder stb = new StringBuilder(); stb.append(st); stb.append(stb.subSequence(0,index)); stb.delete(0,index); return stb.substring(0,stb.length()); }
面试题59_1:滑动窗口的最大值
顺序遍历及比较 | o(nk) |
用双端开口的队列进行辅助 | o(n) |
public static ArrayList<Integer> MaxOfWindow(int[] nums ,int k){ if (nums == null || nums.length < k || k<=0 ){ return null; } ArrayDeque<Integer> arrayDeque = new ArrayDeque<>(); ArrayList<Integer> reback = new ArrayList<>(); for (int x = 0;x<k;x++){ while (!arrayDeque.isEmpty() && nums[x] >= nums[arrayDeque.peekFirst()]){ arrayDeque.pollFirst(); } arrayDeque.addFirst(x); } for (int x=k;x<nums.length;x++){ reback.add(nums[arrayDeque.peekLast()]); while (!arrayDeque.isEmpty() && nums[x]>=nums[arrayDeque.peekFirst()]){ arrayDeque.pollFirst(); } if (!arrayDeque.isEmpty() && arrayDeque.peekLast() <= (x-k)){ arrayDeque.pollLast(); } arrayDeque.addFirst(x); } reback.add(nums[arrayDeque.peekLast()]); return reback; }
59_2:队列的最大值
相当于将整个队列设置为滑动窗口 |
class Deque_max{ class Data{ int value; int index; Data(int value,int index){ this.value = value; this.index = index; } } ArrayDeque<Data> arrayDeque_data = new ArrayDeque<>(); ArrayDeque<Data> arrayDeque_max = new ArrayDeque<>(); public static int currentIndex; public void push_back(int num){ while (!arrayDeque_max.isEmpty() && num >= arrayDeque_max.peekLast().value){ arrayDeque_max.pollLast(); } Data newdata = new Data(num,currentIndex); arrayDeque_data.addLast(newdata); arrayDeque_max.addLast(newdata); currentIndex++; } public int pop_front() throws Exception{ if (arrayDeque_data.isEmpty()){ throw new Exception("no numbers"); } if (arrayDeque_data.peekFirst().index == arrayDeque_max.peekFirst().index){ arrayDeque_max.pollFirst(); } return arrayDeque_data.pollFirst().value; } public int max() throws Exception{ if (arrayDeque_max.isEmpty()){ throw new Exception("no max"); } return arrayDeque_max.peekFirst().value; } }
面试题60:n个骰子的点数
求出n个骰子的点数和,用递归进行求解 |
用两个数组存储,第一个数组存储和为n出现的次数,第二个数组n为前一个数组n-1,n-2,n-3到n-6之和 |
public static void Prosibility(int numOfGm){ if (numOfGm<1){ return; } int maxSum = 6*numOfGm; int[] prosibilitys = new int[maxSum-numOfGm+1]; Probability(numOfGm,prosibilitys); int total = (int)Math.pow(6,numOfGm); for (int i = numOfGm;i<= maxSum;i++){ System.out.println(prosibilitys[i-numOfGm]/(float)(total)); } } public static void Probability(int numOfGm,int[] prosibilitys){ for (int x = 1;x<=6;x++){ Prosibility_core(numOfGm,numOfGm,x,prosibilitys); } } public static void Prosibility_core(int original,int current,int sum,int[] prosibilitys){ if (current == 1){ prosibilitys[sum-original]++; }else { for (int i = 1;i<=6;i++){ Prosibility_core(original,current-1,i+sum,prosibilitys); } } }
public static void Prosibility(int numOfGm){ if (numOfGm<1){ return; } int[][] pProbabilities = new int[2][]; pProbabilities[0] = new int[6*numOfGm+1]; pProbabilities[1] = new int[6*numOfGm+1]; int flag = 0; for (int i = 1;i<=6;i++){ pProbabilities[flag][i] = 1; } for (int k = 2;k<=numOfGm;k++){ for (int x = 0;x<k;x++){ pProbabilities[1-flag][x] = 0; } for (int x = k;x<=6*k;x++){ pProbabilities[1-flag][x] = 0; for (int j = 1;j<=x &&j<=6;j++){ pProbabilities[1-flag][x] += pProbabilities[flag][x-j]; } } flag = 1-flag; } int total = (int)Math.pow(6,numOfGm); for (int x = numOfGm;x<=6*numOfGm;x++){ System.out.println(pProbabilities[flag][x]/(float)(total)); } }
面试题61:扑克牌中的顺子
将扑克看做数字,大小王视作0 。需要将数组排序,统计数组中0的个数,判断空缺数,若空缺数<=0的个数也是有序的。非0数字连续出现则数组无序。 |
public static boolean isContinues(int[] nums){ if (nums == null || nums.length != 5){ return false; } Arrays.sort(nums); int numOfzero = 0; for (int x:nums){ if (x == 0){ numOfzero++; } } if (numOfzero>2){ return false; } int gap = 0; for (int x = 0;x<nums.length-1;x++){ if ( nums[x] != 0){ if (nums[x+1] == nums[x]){ return false; }else { gap += nums[x+1]-nums[x]-1; } } } return numOfzero >= gap ? true:false; }
面试题62:圆圈中最后剩下的数字(约瑟夫环问题)
用头尾相连的链表来模拟约瑟夫环,每次删除第m个 |
数学推导得到递推公式:f(n,m) = (f(n-1,m)+m)%n |
class Circle { class Node{ int value; Node next; Node(int value){ this.value = value; } } Node head; Node tail; public void addNode(int value){ Node now = new Node(value); if (head == null){ head = now; tail = now; }else { tail.next = now; tail = tail.next; tail.next = head; } } public Node delNode(int index,Node start){ Node temp = start; for (int x = 1;x<index-1;x++){ temp = temp.next; } int reback = temp.next.value; temp.next = temp.next.next; return temp.next; } public int getRemain(int[] nums,int index) throws Exception{ if (nums == null || nums.length == 0 || index<=0){ throw new Exception("error"); } for (int x:nums){ addNode(x); } Node temp = head; while (temp != temp.next){ temp = delNode(index,temp); } return temp.value; } }
面试题63:股票的最大利润
直接求出所有数对,取差值最大的一个 | o(n[2]) |
扫描数字,记录前面出现过的最小值及差值的最大值 | o(n) |
public static int MaxD(int[] nums) throws Exception{ if (nums == null || nums.length <= 1){ throw new Exception("error"); } int minNum = nums[0]; int MaxD = 0; for (int x = 1;x<nums.length;x++){ if (nums[x] - minNum > MaxD){ MaxD = nums[x]-minNum; } if (nums[x]<minNum){ minNum = nums[x]; } } return MaxD; }
面试题64:求1-n的和
利用&&的短路效果结束递归 |
利用反射的技术 |
public static int getSum(int num){ int sum = num; boolean ans = (num>0) && ((sum += getSum(num-1))>0); return sum; }
class count{ public int terminator(int n){ return 0; } public int sum(int num) throws IllegalAccessException,InvocationTargetException { List<Boolean> li = new ArrayList<>(); li.add(false); li.add(true); Method methods[] = this.getClass().getMethods(); int index = li.indexOf( num == 0); return num+(int)methods[index].invoke(this,(--num)); } }
面试题65_1:不用加减乘除做加法
用位运算代替:异或,求进位,再异或直至不产生进位 |
public static int Sum(int num1,int num2){ int sum = num1 ^ num2; int carry = (num1 & num2)<<1; while (carry != 0){ int temp = sum; sum = sum ^ carry; carry = (temp & carry)<<1; } return sum; }
面试题65_2:不使用心得变量,交换两个变量的值
//基于加法
public static void Turn(int num1,int num2){
num1 = num1+num2; num2 = num1-num2; num1 = num1 - num2; System.out.println(num1); System.out.println(num2); }public static void Turn(int num1,int num2){ num1 = num1^num2; num2 = num1^num2; num1 = num1^num2; System.out.println(num1); System.out.println(num2); }
面试题66:构建乘积数组
直接遍历相乘 | o(n[2]) |
下一个要求的元素的两部分与上一个有关联,故可以在前一个的基础上求得下一个 | o(n) |
public static int[] getSecondArray(int[] nums,int[] target){ if (nums == null||target == null||nums.length != target.length || nums.length == 0){ return null; } //每次计算出的要进入数组。 target[0] = 1; for (int x = 1;x<nums.length;x++){ target[x] = target[x-1]*nums[x-1]; } int temp = 1; for (int x = nums.length-2;x>=0;x--){ temp *= nums[x+1]; target[x] *= temp; } return target; }