先来几个入门级别的练练手。

  • 若求的是小于临界值的,return high。因为high最终=mid-1.

  • 若求的是大于临界值的,return low。因为low最终=mid+1.

367. Valid Perfect Square

比较简单,O(log n) 复杂度。

public boolean isPerfectSquare(int num) {
        long low=1, high=num;
        while(low<=high){
            long mid=(low+high)/2;
            if(mid*mid==num)
                return true;
            else if(mid*mid>num)
                    high=mid-1;
            else low=mid+1;
        }
        return false;
    }

另外还有两种解法。

利用数学性质,可被开方的数字一定是1+3+5+7+......一个一个去试就行了。 O(sqrt(n)) 复杂度。

public boolean isPerfectSquare(int num) {
     int i = 1;
     while (num > 0) {
         num -= i;
         i += 2;
     }
     return num == 0;
 }

Newton Method。 https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division

不过感觉意义不大。

public boolean isPerfectSquare(int num) {
        long x = num;
        while (x * x > num) {
            x = (x + num / x) >> 1;
        }
        return x * x == num;
    }

35. Search Insert Position

Binary Search都用非递归写算了,递归beats 31%,非递归beats 78%,差距挺大的。

另外就是注意如果找不到的话,要返回low,因为最后low的位置就是mid+1,即本该出现的位置。或者返回high+1也是一样。

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

475. Heaters

这题看起来容易,但处理corner case挺烦的,G家出的。

因为用Binary Search,所以得先sort heaters。

对于每一个house,算出左边最接近的heater的距离和右边最接近的heater距离,取最小,即为覆盖该house最小的radius。

对于所有house,每个house的最小radius取最大,即为覆盖所有house最小的radius。

O(mlog n)复杂度。

顺带一提,java自带 Arrays.binarySearch()方法。

public class Solution {
    public int findRadius(int[] houses, int[] heaters) {
        Arrays.sort(heaters);
        int max=0;
        for(int i=0;i<houses.length;i++){
            int low=0, high=heaters.length-1;
            int res=bs(low, high, houses[i], heaters);
            max=Math.max(max, res);
        }
        return max;
    }
    public int bs(int low, int high, int target, int[] heaters){
        while(low<=high){
            int mid=(low+high)/2;
            if(heaters[mid]>target)
                high=mid-1;
            else if(heaters[mid]<target)
                low=mid+1;
            else return 0;
        }
        int left= low<heaters.length? Math.abs(heaters[low]-target):Integer.MAX_VALUE;
        int right= high>-1? Math.abs(target-heaters[high]):Integer.MAX_VALUE;
        return Math.min(left, right);
    }
}

69. Sqrt(x)

注意对于x,如果他不能开方的话,返回小于他的可开方的结果。因此最后返回的是high。

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

374. Guess Number Higher or Lower

Trivial.

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        long low=0, high=n;
        while(low<=high){
            long mid=(low+high)/2;
            int res=guess((int)mid);
            if(res==-1)
                high=mid-1;
            else if(res==1)
                    low=mid+1;
            else return (int)mid;
        }
        return 0;
    }
}

278. First Bad Version

Important!!!! 一定要用long啊啊啊啊!!!! Leetcode 每次都会用overflow的值来搞你啊啊啊!!!!每次改好费劲啊啊啊啊!!!!一开始就要用long啊啊啊啊啊!!!!!!!!

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        long low=1, high=n;
        while(low<=high){
            long mid=(low+high)/2;
            if(isBadVersion((int)mid))
                high=mid-1;
            else low=mid+1;
        }
        return (int)low;
    }
}

441. Arranging Coins

这题相当于求最长连续队列 1+2+3+4+.....+x<=n。

用binary search的话就利用求和公式 (x*(x+1))/2 <=n来做。

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

153. Find Minimum in Rotated Sorted Array

注意循环条件low不能等于high,不然找到pivot会死循环至TLE。因为我们是拿high跟mid作为比较,最后high会一直向pivot靠近,直到等于pivot。

public class Solution {
    public int findMin(int[] nums) {
        int low=0, high=nums.length-1;
        if(high==0)
            return nums[high];
        while(low<high){
            int mid=(low+high)/2;
            if(nums[mid]>nums[high]){
                low=mid+1;
            }
            else if(nums[mid]<nums[high]){
                high=mid;
            }
        }
        return nums[high];
    }
}

154. Find Minimum in Rotated Sorted Array II

允许duplicate,因此追加个操作,当nums[mid]=nums[high]的时候,因为不知道pivot在左边还是右边,索性就往左挪,high--。

public class Solution {
    public int findMin(int[] nums) {
        int low=0, high=nums.length-1;
        while(low<high){
            int mid=(low+high)/2;
            if(nums[mid]<nums[high])
                high=mid;
            else if(nums[mid]>nums[high])
                low=mid+1;
            else high--;
        }
        return nums[high];
    }
}

33. Search in Rotated Sorted Array

有了上一题铺垫,先找到rotate的pivot,找到之后就没什么好讲的了,老套路。

找到pivot后,用(mid+offset)%n来找出该元素在原队列的位置。

public class Solution {
    public int search(int[] nums, int target) {
        int low=0, high=nums.length-1;
        while(low<high){
            int mid=(low+high)/2;
            if(nums[mid]>nums[high])
                low=mid+1;
            else high=mid;
        }
        int offset=low;
        low=0;
        high=nums.length-1;
        while(low<=high){
            int mid=(low+high)/2;
            int realmid=(mid+offset)%nums.length;
            if(nums[realmid]==target)
                return realmid;
            else if(nums[realmid]<target)
                low=mid+1;
            else high=mid-1;
        }
        return -1;
    }
}

34. Search for a Range

题目要求解区间,因此需要2个binary search,一个出上限,一个出下限,O(2log n) 的复杂度。或者也可以用递归写到一起,看个人喜好。

public class Solution {
    public int[] searchRange(int[] nums, int target) {
        int low=0, high=nums.length-1;
        int[] res=new int[2];
        res[0]=bs(low, high, nums, target, 0, Integer.MAX_VALUE);
        res[1]=bs(low, high, nums, target, 1, Integer.MIN_VALUE);
        return res;
    }
    public int bs(int low, int high, int[] nums, int target, int mode, int left){
        if(mode==0){
            while(low<=high){
                int mid=(low+high)/2;
                if(nums[mid]==target&&mid<left){
                    left=mid;
                    high=mid-1;
                }
                else if(nums[mid]<target)
                        low=mid+1;
                else high=mid-1;
            }
            if(left==Integer.MAX_VALUE)
                left=-1;
        }
        if(mode==1){
            while(low<=high){
                int mid=(low+high)/2;
                if(nums[mid]==target&&mid>left){
                    left=mid;
                    low=mid+1;
                }
                else if(nums[mid]<target)
                        low=mid+1;
                else high=mid-1;
            }
            if(left==Integer.MIN_VALUE)
                left=-1;
        }
        return left;
    }
}

74. Search a 2D Matrix

先来常规解法,对每排第一个数用一次binary search确定数在哪一排,然后对该排用一次binary search找出该数。因为每排第一个数是该排最小,所以第一次binary search后取high即为所在排。

另外注意如果high<0,则不存在该数。

public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m=matrix.length;
        if(m==0)
            return false;
        int n=matrix[0].length;
        if(n==0)
            return false;
        int low=0, high=m-1;
        while(low<=high){
            int mid=(low+high)/2;
            if(matrix[mid][0]==target)
                return true;
            else if(matrix[mid][0]>target)
                    high=mid-1;
            else low=mid+1;
        }
        int row=high;
        if(row<0)
            return false;
        low=0; high=n-1;
        while(low<=high){
            int mid=(low+high)/2;
            if(matrix[row][mid]==target)
                return true;
            else if(matrix[row][mid]<target)
                    low=mid+1;
            else high=mid-1;
        }
        return false;
    }
}

第二个解法,将整个2D Matrix看做一个sorted 数列,则第i位数对应 nums[i/n][i%n]。然后正常用binary search就行。

public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix.length==0)
            return false;
        if(matrix[0].length==0)
            return false;
        int low=0, high=matrix.length*matrix[0].length-1;
        while(low<=high){
            int mid=(low+high)/2;
            if(matrix[mid/matrix[0].length][mid%matrix[0].length]==target)
                return true;
            else if(matrix[mid/matrix[0].length][mid%matrix[0].length]>target)
                    high=mid-1;
            else low=mid+1;
        }
        return false;
    }
}

240. Search a 2D Matrix II

300. Longest Increasing Subsequence

只写binary search做法。

用一个数组big装前n个连续增长数列的最大值,也就是连续增长数列的末尾那个数。

比如对于数列[1,2,5,4,3,6,7],先装第一个数, big[0]=1。第二个数2,大于big[0],则big[1]=2,接着big[2]=5,到第四个数4,小于big[2],则将big[2]更新,因为到big[2]增长中断,将big[2]赋值为4。下一个数3,小于big[2],不增长,则又将big[2]更新,以便于重新算增长的长度。碰到下一个数6,大于big[2],于是big[3]=6。

因此每读取一个数,都要在big数组里进行binary search,如果大于所有big[1...n],则size+1并更新big[n+1]。最后返回size。O(nlogn)复杂度。

public class Solution {
    public int lengthOfLIS(int[] nums) {
        int[] big=new int[nums.length];
        int count=0;
        for(int s:nums){
            int low=0, high=count;
            while(low<high){
                int mid=(low+high)/2;
                if(big[mid]<s)
                    low=mid+1;
                else high=mid;
            }
            big[high]=s;
            if(low==count)
                count++;
        }
        return count;
    }
}

354. Russian Doll Envelopes

俄罗斯套娃嘛,大的套小的。有了上一题的基础,这题就十分好做了,信手拈来。

首先把数组排序,可以根据width排序,排序好之后就是 longest increasing subsequence问题了。

有个细节要注意,这个width可以是重复值,因此对于[3, 3]和[3, 4]来说,[3, 4]是不能套[3, 3]的,但是算法会判定能套,因为4>3。所以在排序的时候,对于width相同的,要把height大的排在前面,这样就不会出错了。

public class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        Arrays.sort(envelopes, new Comparator<int[]>(){
            public int compare(int[] num1, int[] num2){
                if(num1[0]==num2[0])
                    return num2[1]-num1[1];
                else return num1[0]-num2[0];
            }
        }
        );
        int[] tail=new int[envelopes.length+1];
        int count=0;
        for(int[] num:envelopes){
            int low=0, high=count;
            while(low<high){
                int mid=(low+high)/2;
                if(num[1]>tail[mid])
                    low=mid+1;
                else high=mid;
            }
            tail[high]=num[1];
            if(low==count)
                count++;
        }
        return count;
    }
}

162. Find Peak Element

和rotated sorted array类似,但是在处理方法只能mid和前一个或后一个比,因为不能保证mid前都是sorted或者mid后都是sorted。这里是判断小于mid+1,所以最后high位置为所求。如果判断大于mid+1,则low位置为所求。

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

436. Find Right Interval

这题做起来巨tm烦,逻辑太绕了。

首先我们必须sort intervals才能用binary search,因此用个hashmap记录原本的index。由于start不重复,就用start来标记。由于要保留intervals原本的顺序(不然涉及很多map.get操作巨烦),又要新建一个数组来sort,这里我们sort start。接下来就是照常的binary search。

public class Solution {
    public int[] findRightInterval(Interval[] intervals) {
        Map<Integer, Integer> map=new HashMap<>();
        int[] starts=new int[intervals.length];
        int[] res=new int[intervals.length];
        for(int i=0;i<intervals.length;i++){
            map.put(intervals[i].start, i);
            starts[i]=intervals[i].start;
        }
        Arrays.sort(starts);
        for(int i=0;i<intervals.length;i++){
            int low=0, high=starts.length-1;
            while(low<high){
                int mid=(low+high)/2;
                if(starts[mid]>=intervals[i].end)
                    high=mid;
                else low=mid+1;
            }
            if(starts[high]<intervals[i].end)
                res[i]=-1;
            else res[i]=map.get(starts[high]);
        }
        return res;
    }
}

483. Smallest Good Base

4. Median of Two Sorted Arrays

这题貌似很经典啊,序号为4的hard题,频率是绿条,各大公司都考过,那必须做到倒背如流。

唯一要解决的重要点就是,怎么找到合适的位置。Median的位置便是将AB等量分开的位置,即A[left]+B[left]的长度等于A[right]+B[right]。同时,Median左侧的最大元素要小于右侧的最小元素。

所以做法就是,先求得整个数组的中点 bigMid,然后对A数组二分,每次找个mid,相应的B数组里就是bigMid-mid位置。这样就能保证A[left]+B[left]恒为一半。如果A小于B,mid就要增加,反之mid减小,最终贴近的位置就是Median位置。

找到Median位置后,接着就找出左侧的最大元素和右侧的最小元素,即得Median。

public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n=nums1.length;
        int m=nums2.length;
        if(n>m)
            return findMedianSortedArrays(nums2, nums1);
        int bigMid=(n+m-1)/2;
        int low=0, high=Math.min(bigMid, n);
        while(low<high){
            int mid1=(low+high)/2;
            int mid2=bigMid-mid1;
            if(nums1[mid1]<nums2[mid2])
                low=mid1+1;
            else high=mid1;
        }
        int left=Math.max( low>0? nums1[low-1]:Integer.MIN_VALUE, bigMid-low>=0? nums2[bigMid-low]:Integer.MIN_VALUE);
        int right=Math.min( low<n? nums1[low]:Integer.MAX_VALUE, bigMid-low+1<m? nums2[bigMid-low+1]:Integer.MAX_VALUE);
        if((n+m)%2==1)
            return (double)left;
        return (left+right)/2.0;
    }
}

410. Split Array Largest Sum

这道题还是挺好想的,我们可以先设个lower bound和upper bound,然后对bound进行二分,每次出来的mid我们都进行验证,如果满足要求,降低upper bound,不满足,提高lower bound。验证的话,拿个count计数,每次和大于mid时,另开个区间,count++。如果最后count不大于m,就满足要求。

public class Solution {
    public int splitArray(int[] nums, int m) {
        long low=0, high=0;
        for(int num:nums){
            low=Math.max(low, num);
            high+=num;
        }
        while(low<=high){
            long mid=(low+high)/2;
            if(helper(mid, nums, m))
                high=mid-1;
            else low=mid+1;
        }
        return (int)low;
    }
    public boolean helper(long mid, int[] nums, int m){
        long sum=0;
        int count=1;
        for(int num:nums){
            sum+=num;
            if(sum>mid){
                count++;
                sum=num;
            }
            if(count>m)
                return false;
        }
        return true;
    }
}

302. Smallest Rectangle Enclosing Black Pixels

给的是其中一个黑点的坐标( x, y ),因此根据这个坐标找出4个边界就可以算出面积。假设矩阵维度m x n。

  • 左边能到达的最远列
  • 右边能到达的最远列
  • 上边能到达的最远排
  • 下边能到达的最远排

对于这四个边界,分别用二分来搞就行。

  • low=0, high=y.
  • low=y, high=n-1.
  • low=0, high=x.
  • low=x, high=m-1.

判断是否valid就是算出当前列/行是不是至少含有一个1.

public class Solution {
    int m,n;
    public int minArea(char[][] image, int x, int y) {
        m=image.length;
        n=image[0].length;
        int left_col=bs(0, y, image, 0, 0);
        int right_col=bs(y, n-1, image, 0, 1);
        int up_row=bs(0, x, image, 1, 0);
        int down_row=bs(x, m-1, image, 1, 1);
        return (down_row-up_row)*(right_col-left_col);
    }
    public int bs(int low, int high, char[][] image, int mode, int mode2){
        while(low<=high){
            int mid=(low+high)/2;
            if(isValid(mid, image, mode)){
                if(mode2==0)
                    high=mid-1;
                else low=mid+1;
            }
            else {
                if(mode2==0)
                    low=mid+1;
                else high=mid-1;
            }
        }
        return low;
    }
    public boolean isValid(int mid, char[][] image, int mode){
        if(mode==0){
            for(int i=0;i<m;i++)
                if(image[i][mid]=='1')
                    return true;
        }
        else {
            for(int i=0;i<n;i++)
                if(image[mid][i]=='1')
                    return true;
        }
        return false;
    }
}

results matching ""

    No results matching ""