LeetCode刷题笔记-查找

51 阅读6分钟

二分查找法的特性:

  • 有序
  • 查找位置:找到返回,找不到返回其邻近值 对于不是明显为查找的问题,如果其结果在一个有限范围内,若符合以上两点,可以转化为用二分查找的方法。 技巧:当target值不在数组中时,循环退出后,low指向了大于target的那个值,high指向了小于target的那个值。

35 Search Insert Position Easy

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You may assume no duplicates in the array.

Example 1: Input: [1,3,5,6], 5 Output: 2

Example 2: Input: [1,3,5,6], 2 Output: 1

Example 3: Input: [1,3,5,6], 7 Output: 4

Example 4: Input: [1,3,5,6], 0 Output: 0

思路:肯定一下子想到二分查找法,关键是不存在的数,返回其应插入的位置怎么做。

肯定就在i,j附近,那用位置i的数比一下:

如果比nums[i]小,就放在i处;

如果比nums[i]大,就放在i+1处。

但是,i有可能不在0~n-1的合法索引之间了。

这时就拿位置j的数比一下,因为i和j都是不合法索引是不可能的。

(其实i只要不超过上界就是合法索引,不需要拿下界0做判断。因为整个过程中i只会往大了移,有超过n-1的可能,但不会往小了移。)

啊啊啊为什么要搞得这么复杂?!其实最后拿mid做比较才是最简便的啊

class Solution {
    public int searchInsert(int[] nums, int target) {
        int i=0, j=nums.length-1;
        int mid = 0;
        while(i<=j) {
            mid = (i+j)/2;
            if(target == nums[mid]) {
                return mid;
            } else if(target < nums[mid]) {
                j = mid-1;
            } else {
                i = mid+1;
            }
        }        
        if(target < nums[mid]) {
            return mid;
        } else {
            return mid+1;
        }      
    }
}

278 First Bad Version Easy

You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad. Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad. You are given an API bool isBadVersion(version) which will return whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

思路:一下子想到二分查找法,关键是最后。

那就举例,然后找规律。

看来类似题目并不都是最后以mid为基准,还是要结合具体问题具体分析。

养成用mid = i+(j-i)/2代替mid=(i+j)/2的习惯!

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int i=1, j=n;        
        while(i<=j) {
            int mid = i+(j-i)/2;    // 这里用(i+j)/2的时候遇到特别大的数会溢出!
            if(isBadVersion(mid)) {
                j = mid-1;
            } else {
                i = mid+1;
            }
        }
        return i;
    }
}

74 Search a 2D Matrix Medium

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • The first integer of each row is greater than the last integer of the previous row.

For example, Consider the following matrix: [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] Given target = 3, return true.

思路:按顺序排列,首先想到是用二分查找。

关键是不要以二维数组来看待,从行、列两个方面分别做二分查找。

而是应该把二维数组的所有元素连起来看成一个List,只需要根据mid来计算出对应的行列下标即可。

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        /* 异常情况:null [] [[]]
        ** 否则matrix[0]会出现空指针的错误
        */
        if (matrix == null || matrix.length == 0) {
            return false;
        }
        if (matrix[0] == null || matrix[0].length == 0) {
            return false;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        int i=0, j=m*n-1;
        while(i<=j) {
            int mid = i+(j-i)/2;
            if(target == matrix[mid/n][mid%n]) {
                return true;
            } else if(target < matrix[mid/n][mid%n]) {
                j = mid-1;
            } else {
                i = mid+1;
            }
        }        
        return false;
    }
}

240 Search a 2D Matrix II Medium

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: Integers in each row are sorted in ascending from left to right. Integers in each column are sorted in ascending from top to bottom.

For example, Consider the following matrix: [ [1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30] ]

Given target = 5, return true.

Given target = 20, return false.

基本思路:遍历行,若发现行首的元素<=target<=行尾的元素,则对该行进行二分查找。时间复杂度O(nlogm)。

巧思:从矩阵的右上角开始,其特点是,左边的数都小于它,下面的数都大于它。

如果target>cur时,那么当前行排除,行数+1;

如果target小于当前数,那么当前列排除,列数-1;遇到边界则结束,返回false。

时间复杂度是O(n+m)。

左下角的数也有类似特点。

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix == null || matrix.length == 0) {
            return false;
        }
        if(matrix[0] == null || matrix[0].length == 0) {
            return false;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        int i = 0, j = n-1;
        while(i<m && j>=0) {
            if(target == matrix[i][j]) {
                return true;
            } else if(target < matrix[i][j]) {
                j--;
            } else {
                i++;
            }
        }
        return false;
    }
}

69 Sqrt(x) Easy

Implement int sqrt(int x). Compute and return the square root of x. x is guaranteed to be a non-negative integer. Example 1: Input: 4 Output: 2 Example 2: Input: 8 Output: 2 Explanation: The square root of 8 is 2.82842..., and since we want to return an integer, the decimal part will be truncated.

思路:二分查找法! 这道题很巧妙的运用了二分查找法的特性,有序,查找pos(在这道题中pos=value),找到返回pos,找不到返回邻近值。

求数x(x>=0) 的平方根,结果一定是小于等于x且大于等于0,所以用二分查找法肯定能搜到结果。

以每一次的mid的平方来与target既数x相比:

如果mid*mid == x,返回mid;

如果mid*mid < x,那么说明mid过小,应让low = mid+1,在右边继续查找

如果mid*mid > x,那么说明mid过大,应让high = mid-1,在左边继续查找

若x无法开整数根号(在上述查找中没有找到),那么我们利用对二分查找法总结的技巧,当target值不在数组中,low指向大于target的那个值,high指向小于target的那个值,由于我们需要向下取整的结果,所以我们返回high指向的值(这里high指向的值和high的值是同一个值),这个值就是所求得最接近其开根号结果的整数值。

因为leetcode的test case x=2147395599,在算mid*mid的时候造成溢出,所以mid不能使用int型来接,要使用long型防止溢出(Java中Integer型的范围:-2147483648 到2147483648)

class Solution {
    public int mySqrt(int x) {
        int low = 0, high = x;
        while(low<=high) {
            long mid = low + (high-low)/2;
            if(mid*mid == x) {
                return (int)mid;
            } else if(mid*mid < x) {
                low = (int)mid + 1;
            } else {
                high = (int)mid - 1;
            }
        }
        return high;
    }
}