leetcode刷题 二分查找

作为本科非科班出身的CSer也经历了看到leetcode不知从何刷起的感觉,现在准备重新刷一下leetcode,写博客主要是记录下自己的思路,尽量保持每天两道,这篇主要总结二分查找所遇到的问题以及变种。

打个广告微博 @辣酱一直就这样  欢迎同学私信讨论

先说一下二分查找的模版

1. start + 1 < end //在相邻的时候退出避免死循环
2. start + (end - start) / 2 //找中间值,装逼写法避免mid比Integer.max_value还大导致溢出
3. A[mid] ==, <, >//判断要根据找target是第一次出现还是最后一次出现来决定把mid 给start 还是end
4. A[start] A[end] ? target//最后相邻退出同样要考虑是找target第一次出现还是最后一次出现.
二分查找基本题型,leetcode上面好像没有,我找了lintcode上面一道

http://www.lintcode.com/zh-cn/problem/first-position-of-target/#

这道题就是最基本的二分查找,target有重复,需要找到target第一次出现时所在数组的下标,很简单套模版

class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int binarySearch(int[] nums, int target) {
        //write your code here
        if(nums.length == 0){
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid ;
        while(start + 1 < end){
            mid = start + (end - start) / 2;
            if(nums[mid] == target){
                end = mid;//注意是找第一个出现的位置,我们就认为找到一个他就是最右边的往左找看有没有相同的
            }else if(nums[mid] < target){
                start = mid;
            }else{
                end = mid ;
            }
        }
        if(nums[start] == target){
            return start;
        }
        if(nums[end] ==target){
            return end;
        }
        return -1;
    }
}
接着提高一点leetcode  34 Search for a Range https://leetcode.com/problems/search-for-a-range/description/

这道题就是让你找到target在数组中的起始和结束位置,也不难,思路就是顺着上一题,先二分一次找到target第一次出现的位置,再二分一次找到target最后一次出现的位置

public class Solution {
    public int[] searchRange(int[] nums, int target) {
        int []res = {-1 , -1} ;
        if(nums == null || nums.length == 0){
            return res ; 
        }
        //先找左边的位置;
        int start = 0 ;
        int end = nums.length - 1 ;
        int mid ;
        while(start + 1 < end){
            mid = start + (end - start) / 2;
            if(nums[mid] == target){
                end = mid ;//左边可能还有要往左边找
            }else if(nums[mid] < target){
                start = mid ;
            }else{
                end = mid ;
            }
        }
        if(nums[start] == target)//先判断左边的
        {
            res[0] = start ;
        }else if(nums[end] == target){
            res[0] = end ;
        }
        //右边同左边的全都反过来
        start = 0 ;
        end = nums.length - 1 ;
        while(start + 1 < end){
            mid = start + (end - start) / 2;
            if(nums[mid] == target){
                start = mid ;
            }else if(nums[mid] < target){
                start = mid ;
            }else if(nums[mid] > target){
                end = mid ; 
            }   
        }
        if(nums[end] == target){
            res[1] = end ;
        }else if(nums[start] == target){
            res[1] = start ; 
        }
        
        return res ; 
    }
}

下面这道题是35Search Insert Position

这道题就是给了一个数组把target插入到里面让数组还是有序排列,如果数组里面有这个值就返回数组下标,如果没有的就找到最后一个比他小的数插入到这个数后面,如果没有比target还小的数就直接插入到数组最前面,还是用二分查找,套上面的模板,最后处理end start 有些繁琐,注意就好了

public class Solution {
    public int searchInsert(int[] nums, int target) {
        if(nums == null || nums.length == 0 || nums[0] > target){
            return 0 ;
            }
        int start = 0 ;
        int end = nums.length - 1 ;
        int mid ;
        while(start + 1 < end){
            mid = start + (end - start) / 2 ;
            if(nums[mid] == target){
                return mid ;
            }else if (nums[mid] > target){
                end = mid ;
            }else if (nums[mid] < target){
                start = mid ;
            }
        }
        if(nums[end] == target){
            return end ;
        }else if(nums[end] < target){
            return end + 1 ;
        }else if(nums[start] == target){
            return start ;
        }
        return start + 1 ;
    }
}
接下是 74Search a 2D Matrix 搜索矩阵中是否有目标值 , 矩阵的每一行和每一列都是按顺序排列,每一行的第一个数都比上一行的最后一个数大,思路还是用二分查找,一次先找到所在的行,再一次二分找到所在的列
public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
            return false ;
        }
        int row = matrix.length ;
        int colum = matrix[0].length ;
        int mid ;
        //先找行
        int start = 0 ;
        int end = row - 1 ;
        while(start + 1 < end){
            mid = start + (end - start) / 2;
            if(matrix[mid][0] == target){
                return true ;
            }else if(matrix[mid][0] > target) {
                end = mid ;
            }else {
                start = mid ;
            }
        }
        //处理剩下的start 和 end 
        if(matrix[end][0]  <= target){
            row = end ;
        }else if (matrix[start][0] <= target){
            row = start ;
        }else{
            return false ;
        }
        
        //再找列
        start = 0;
        end = colum - 1 ;
        while(start + 1 < end){
            mid = start + (end - start) / 2;
            if(matrix[row][mid] == target){
                return true ; 
            }else if(matrix[row][mid] < target) {
                start = mid ;
            }else{
                end = mid ;
            }
        }
//         只剩两个地方没处理,只需要判断是否等于就好
        if(matrix[row][start] == target){
            return true ;
        }else if(matrix[row][end] == target){
            return true ;
        }
        return false ;
        
    }
}


240Search a 2D Matrix II

这道题是上一题的提高版,还是一个二维矩阵,但是不保证下一行的数都比上一行的数大,只保证每行中后面比前面大, 每列中,下面比上面大,暴力搜索就是时间复杂度n2,肯定是不行的,我们来看这张图

先找到第一列的最后一个,他是最后一行最小的,如果比target大就说明最后一行都比target大,就往上挪一个,如果比target小,由于他是第一列最大的,就说明第一列都比target小,就往右挪一个,这样如图,最差要用O(n + m)时间就可以找到,以下是代码

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

还有一道题278First Bad Version没什么说的,找到第一个坏的版本号,基本的二分查找

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int start = 0 ; 
        int end = n ; 
        int mid ; 
        while(start + 1 < end){
            mid = start + (end - start) / 2 ;
            if(isBadVersion(mid)){
                end = mid ;
            }else{
                start = mid ;
            }
        }
        if(isBadVersion(start)){
            return start ;
        }
        return end ;
    }
}

162Find Peak Element 没什么说的注意下细节就行了

class Solution {
    public int findPeakElement(int[] nums) {
        int start = 0 ;
        int end = nums.length - 1 ;
        int mid ;
        while(start + 1 < end){
            mid = start + (end - start) / 2 ;
            //     mid -1 < mid > mid + 1  说明mid就是峰值
            if(nums[mid] > nums[mid + 1] && nums[mid] > nums[mid - 1]){
                return mid ; 
            //    mid - 1 < mid < mid + 1 说明 peak在mid右边
            }else if(nums[mid] < nums[mid + 1] && nums[mid] > nums[mid - 1]){
                start = mid ;
            //剩下两种情况 凹的话 取左右哪边都一样,递减的话只能取左边,然后取交集 让end = mid 
            }else{
                end = mid ;    
            }
        }
        if(nums[end] > nums[start]){
            return end ;
        }
        return start ;
    }
}
26Remove Duplicates from Sorted Array没什么说的相等的留下不等的跳过

class Solution {
    public int removeDuplicates(int[] nums) {
        //不能有新的空间意思就是不能建立新的数组来存储
        if(nums == null || nums.length == 0){
            return 0 ;
        }
        int size = 0 ;
//         注意不能nums.length - 1 要不然最后一个没有遍历到
        for(int i = 0 ; i < nums.length  ; i++){
//             相等就跳过不相等留下
            if(nums[size] != nums[i]){
                size++  ;
                nums[size] = nums[i] ;
                // nums[++size] = nums[i]
            }
        }
        return size + 1 ;
    }
}

80Remove Duplicates from Sorted Array II
这个就是要做一个计数器记录是否达到了重复两次,没有就留下有就跳过

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0 ;
        }
        int size = 0 ; 
        int count = 1 ;
//         注意i为什么要从1开始? i直接找的第二个数不是第一个 因为第一个nums[size] 和nums[i]肯定是相等的
        for(int i = 1 ; i < nums.length ; i ++){
//             如果相等看有没有达到重复两次
            if(nums[size] == nums[i]){
//                 所以这里也要注意,并不是才加了一遍, 而是如果这里相等代表前面已经相等过一次了
                if(count < 2){
                    size ++ ;
                    nums[size] = nums[i] ;
                    count ++ ;
                }
            }else{
                    size ++ ;
                    nums[size] = nums[i] ;
//                 注意要把计数器复原,也可以看作是第一次相等
                    count = 1 ;
            }
        }
        return size + 1 ;
    }
}
88Merge Sorted Array考验基本功的一题没什么难度

public class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
            int a = m - 1 ;
            int b = n - 1 ;
            int k = m + n - 1 ;
            while(a >= 0 && b >= 0){
                if(nums1[a] > nums2[b]){
                    nums1[k--] = nums1[a--];
                }else{
                    nums1[k--] = nums2[b--];
                }
            }
            while(a >= 0){
                nums1[k--] = nums1[a--] ;
            }
            while(b >= 0){
                nums1[k--] = nums2[b--] ;
            }
        
        
        
        
        
        
        
        
        
        
        

    }
}

4Median of Two Sorted Arrays这个题很重要是重点题

 题解:

首先我们先明确什么是median,即中位数。 

引用Wikipedia对中位数的定义:

计算有限个数的数据的中位数的方法是:把所有的同类数据按照大小的顺序排列。如果数据的个数是奇数,则中间那个数据就是这群数据的中位数;如果数据的个数是偶数,则中间那2个数据的算术平均值就是这群数据的中位数。

因此,在计算中位数Median时候,需要根据奇偶分类讨论。

解决此题的方法可以依照:寻找一个unioned sorted array中的第k大(从1开始数)的数。因而等价于寻找并判断两个sorted array中第k/2(从1开始数)大的数。

特殊化到求median,那么对于奇数来说,就是求第(m+n)/2+1(从1开始数)大的数。

而对于偶数来说,就是求第(m+n)/2大(从1开始数)和第(m+n)/2+1大(从1开始数)的数的算术平均值。


那么如何判断两个有序数组A,B中第k大的数呢?

我们需要判断A[k/2-1]和B[k/2-1]的大小。

如果A[k/2-1]==B[k/2-1],那么这个数就是两个数组中第k大的数。

如果A[k/2-1]<B[k/2-1], 那么说明A[0]到A[k/2-1]都不可能是第k大的数,所以需要舍弃这一半,继续从A[k/2]到A[A.length-1]继续找。当然,因为这里舍弃了A[0]到A[k/2-1]这k/2个数,那么第k大也就变成了,第k-k/2个大的数了。

如果 A[k/2-1]>B[k/2-1],就做之前对称的操作就好。

 这样整个问题就迎刃而解了。

 

当然,边界条件页不能少,需要判断是否有一个数组长度为0,以及k==1时候的情况。

以上是百度的别人的博客,思路就是这个思路,变形就是让你找第K小元素的时候别不会找了
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
         int len = nums1.length + nums2.length;
        if (len % 2 == 1) {
            return findKth(nums1, 0, nums2, 0, len / 2 + 1);
        }
        return (
            findKth(nums1, 0, nums2, 0, len / 2) + findKth(nums1, 0, nums2, 0, len / 2 + 1)
        ) / 2.0;
    }

    // find kth number of two sorted array
    public static int findKth(int[] nums1, int A_start,
                              int[] nums2, int B_start,
                              int k){    	
		if (A_start >= nums1.length) {
			return nums2[B_start + k - 1];
		}
		if (B_start >= nums2.length) {
			return nums1[A_start + k - 1];
		}

		if (k == 1) {
			return Math.min(nums1[A_start], nums2[B_start]);
		}
		
		int A_key = A_start + k / 2 - 1 < nums1.length
		            ? nums1[A_start + k / 2 - 1]
		            : Integer.MAX_VALUE;
		int B_key = B_start + k / 2 - 1 < nums2.length
		            ? nums2[B_start + k / 2 - 1]
		            : Integer.MAX_VALUE; 
		
		if (A_key < B_key) {
			return findKth(nums1, A_start + k / 2, nums2, B_start, k - k / 2);
		} else {
			return findKth(nums1, A_start, nums2, B_start + k / 2, k - k / 2);
		}
    }
}

151Reverse Words in a String这个也比较简单

public class Solution {
    public String reverseWords(String s) {
        if(s == null || s.length() == 0){
            return "" ;
        }
        String[] a = s.split(" ") ;
        StringBuilder sb = new StringBuilder() ;
        for(int i = a.length - 1 ; i >= 0 ; i --){
            //注意这里不要写 == 
            if(!a[i].equals("")){
                sb.append(a[i]).append(" ") ;
            }
        }
        return sb.length() == 0 ? "" :sb.substring(0 , sb.length() - 1) ;
    }
}
下面出一个思考题,如何把一个Rotated sort array复原?

答:三步翻转法









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值