算法题解之二分法
Count Complete Tree Nodes
完全二叉树的节点数
思路:这道题使用暴力法为O(n)会超时。使用二分的思想,首先求出左右子树的深度,如果它们的深度相同,则说明左子树为满树,它的节点数可由公式2^h-1求得;如果不相同,说明右子树为满树,同样可用公式求得它的节点数。然后再递归的去求另一个子树。时间复杂度为O(lgn * lgn)。
这道题的一个优化方法是:每次递归函数都要求2^h,如果调用Math.pow(2, h)会很慢,因此用1<<h来求2^h就能AC了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Solution { public int countNodes(TreeNode root) { if (root == null) { return 0; } int left_h = 0; int right_h = 0; TreeNode cur = root.left; while (cur != null) { cur = cur.left; left_h++; } cur = root.right; while (cur != null) { cur = cur.left; right_h++; } if (left_h != right_h) { return (1 << right_h) + countNodes(root.left); } else { return (1 << left_h) + countNodes(root.right); } } }
Divide Two Integers
两个整数相除
思路:将除数不断乘2(左移1位),直到大于被除数,此时的余数作为被除数继续除以除数,直到满足除数大于余数。排除-2147483649/-1和除数为0这两种溢出情况,其余均转化为long类型来做。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Solution { public int divide(int dividend, int divisor) { if (divisor==0 || (dividend == Integer.MIN_VALUE && divisor == -1)) { return Integer.MAX_VALUE; } long result = 0; long dd = Math.abs((long)dividend); long dr = Math.abs((long)divisor); while (dd >= dr) { long tmp = dr; long re = 1; while (dd >= tmp) { tmp <<= 1; re <<= 1; } tmp = tmp >> 1; re = re >> 1; dd = dd - tmp; result = result + re; } if ((dividend > 0 && divisor<0) || (dividend < 0 && divisor > 0)) return (int)(-result); return (int)result; } }
Factorial Trailing Zeroes
阶乘结果末尾0的个数
思路:末尾的0只可能由2*5产生,考虑乘式可以分解为a个2和b个5相乘,a和b的最小值就是末尾0的个数。
2的个数 = 所有因子中能被2整除的个数 + 所有因子中能被4整除的个数 + 所有因子中能被8整除的个数······
5的个数 = 所有因子中能被5整除的个数 + 所有因子中能被25整除的个数 + 所有因子中能被125整除的个数······
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int trailingZeroes(int n) { 3 long twobase = 2; 4 long fivebase = 5; 5 long twoNum = 0; 6 long fiveNum = 0; 7 while (twobase <= n) { 8 twoNum += n / twobase; 9 twobase *= 2; 10 } 11 while (fivebase <= n) { 12 fiveNum += n / fivebase; 13 fivebase *= 5; 14 } 15 16 return (int)Math.min(twoNum, fiveNum); 17 } 18 }
H-Index II
H指数II
思路:本题是H-indexI的follow up。如I中分析,当数组是升序时,要找到第一个满足citations
[i] >= N - i的元素,想到用二分search。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int hIndex(int[] citations) { 3 if (citations == null || citations.length == 0) { 4 return 0; 5 } 6 int N = citations.length; 7 int start = 0; 8 int end = N - 1; 9 while (start + 1 < end) { 10 int mid = (start + end) / 2; 11 if (citations[mid] >= N - mid) { 12 end = mid; 13 } else { 14 start = mid; 15 } 16 } 17 if (citations[start] >= N - start) { 18 return N - start; 19 } 20 if (citations[end] >= N - end) { 21 return N - end; 22 } 23 return 0; 24 } 25 }
Kth Smallest Element in a Sorted Matrix
找二维排序数组中第k小的元素
思路1:用堆。先把第一行加到堆中,然后每弹出一个元素,就把它下面那个元素加到堆中。时间复杂度是O(klgn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int kthSmallest(int[][] matrix, int k) { 3 4 int n = matrix.length; 5 PriorityQueue<Element> q = new PriorityQueue<Element>(); 6 for (int i = 0; i < n; i++) { 7 q.offer(new Element(0, i, matrix[0][i])); 8 } 9 for (int i = 1; i < k; i++) { 10 Element cur = q.poll(); 11 if (cur.x < n - 1) { 12 q.offer(new Element(cur.x + 1, cur.y, matrix[cur.x + 1][cur.y])); 13 } 14 } 15 16 return q.poll().val; 17 } 18 } 19 20 class Element implements Comparable<Element> { 21 int x, y, val; 22 Element(int x, int y, int val) { 23 this.x = x; 24 this.y = y; 25 this.val = val; 26 } 27 public int compareTo(Element e) { 28 return val - e.val; 29 } 30 }
思路2:二分法。要查找的范围是m~n,m是矩阵左上角的数,n是矩阵右下角的数。对于m和n的中间数mid,计算矩阵中小于mid的数的个数是count。如果count < k,则mid左边的数一定不会是要找的第K个数(这里可以用反证法),因此丢弃mid左边;如果count >= k,同样丢弃mid的右边。最后剩下两个数。
这里计算矩阵中小于num的数的个数使用的方法lowerCountInMatrix(),与seach a 2D matrixII一样,即从右上角开始。
时间复杂度是O(nlgX),X是矩阵中最大数与最小数的差值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int kthSmallest(int[][] matrix, int k) { 3 int start = matrix[0][0]; 4 int end = matrix[matrix.length - 1][matrix.length - 1]; 5 while (start + 1 < end) { 6 int mid = (end - start) / 2 + start; 7 int count = lowerCountInMatrix(mid, matrix); 8 if (count < k) { 9 start = mid; 10 } else { 11 end = mid; 12 } 13 } 14 15 int start_count = lowerCountInMatrix(start, matrix); 16 int end_count = lowerCountInMatrix(end, matrix); 17 18 if (end_count >= k) { 19 return start; 20 } 21 return end; 22 } 23 24 public int lowerCountInMatrix(int num, int[][] matrix) { 25 int count = 0; 26 int i = 0, j = matrix.length - 1; 27 28 while (i < matrix.length && j >= 0) { 29 if (matrix[i][j] < num) { 30 count += j + 1; 31 i++; 32 } else { 33 j--; 34 } 35 } 36 return count; 37 } 38 }
Search a 2D Matrix II
搜索2维矩阵II
思路:本题思想跟二分法类似(但不是二分),即每次根据判断丢弃掉一些范围,将搜索范围缩小。方法是:选取右上角的数作为起点,如果大于target,则丢弃它所在的列;如果小于target,则丢弃掉它所在的行;如果等于target,则计数加1并同时丢弃所在行和列。当右上角超出矩阵范围时结束算法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Solution { /** * @param matrix: A list of lists of integers * @param: A number you want to search in the matrix * @return: An integer indicate the occurrence of target in the given matrix */ public int searchMatrix(int[][] matrix, int target) { // write your code here if (matrix == null || matrix.length == 0) { return 0; } if (matrix[0] == null || matrix[0].length == 0) { return 0; } int m = matrix.length; int n = matrix[0].length; int i = 0; int j = n - 1; int count = 0; while (i < m && j >= 0) { if (matrix[i][j] > target) { j--; } else if (matrix[i][j] < target) { i++; } else { count++; i++; j--; } } return count; } }