算法题解之二分法

时间:2022-12-17 04:05:18

Count Complete Tree Nodes          

完全二叉树的节点数

思路:这道题使用暴力法为O(n)会超时。使用二分的思想,首先求出左右子树的深度,如果它们的深度相同,则说明左子树为满树,它的节点数可由公式2^h-1求得;如果不相同,说明右子树为满树,同样可用公式求得它的节点数。然后再递归的去求另一个子树。时间复杂度为O(lgn * lgn)。

这道题的一个优化方法是:每次递归函数都要求2^h,如果调用Math.pow(2, h)会很慢,因此用1<<h来求2^h就能AC了。

算法题解之二分法算法题解之二分法
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);
        }
        
    }
}
View Code

 

 

Divide Two Integers

两个整数相除

思路:将除数不断乘2(左移1位),直到大于被除数,此时的余数作为被除数继续除以除数,直到满足除数大于余数。排除-2147483649/-1和除数为0这两种溢出情况,其余均转化为long类型来做。

算法题解之二分法算法题解之二分法
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;
    }
}
View Code

 

 

Factorial Trailing Zeroes

阶乘结果末尾0的个数

思路:末尾的0只可能由2*5产生,考虑乘式可以分解为a个2和b个5相乘,a和b的最小值就是末尾0的个数。

   2的个数 = 所有因子中能被2整除的个数 + 所有因子中能被4整除的个数 + 所有因子中能被8整除的个数······

   5的个数 = 所有因子中能被5整除的个数 + 所有因子中能被25整除的个数 + 所有因子中能被125整除的个数······

算法题解之二分法算法题解之二分法
 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 }
View Code

 

 

H-Index II

H指数II

思路:本题是H-indexI的follow up。如I中分析,当数组是升序时,要找到第一个满足citations[i] >= N - i的元素,想到用二分search。

算法题解之二分法算法题解之二分法
 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 }
View Code

 

 

Kth Smallest Element in a Sorted Matrix

找二维排序数组中第k小的元素

思路1:用堆。先把第一行加到堆中,然后每弹出一个元素,就把它下面那个元素加到堆中。时间复杂度是O(klgn)。

算法题解之二分法算法题解之二分法
 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 }
View Code

思路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是矩阵中最大数与最小数的差值。

算法题解之二分法算法题解之二分法
 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 }
View Code

 

 

Search a 2D Matrix II

搜索2维矩阵II

思路:本题思想跟二分法类似(但不是二分),即每次根据判断丢弃掉一些范围,将搜索范围缩小。方法是:选取右上角的数作为起点,如果大于target,则丢弃它所在的列;如果小于target,则丢弃掉它所在的行;如果等于target,则计数加1并同时丢弃所在行和列。当右上角超出矩阵范围时结束算法。

算法题解之二分法算法题解之二分法
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;
    }
}
View Code