Leetcode_01_BinarySearch_笔记总结

摘要

今天的题目全部都是Binary Search,其中35, 33, 81, 153, 154很经典,也有一定的模板可寻, 一定要敲熟练. 其中针对于二分查找中while循环的参数>=问题的讲解.

正文

1. LC29. Divide Two Integers

题目
Divide two integers without using multiplication, division and mod operator.
If it is overflow, return MAX_INT.
使用公司:

难度:
中等


题目与思路分析:
这道题目意思就是要得到两个Integer相除的结果,但是不能使用乘,除以及取模运算. 正常的思路都会想到移位运算. 但是我没有用移位. 首先先弄懂一个问题:被除数/除数=商—>dividend/divisor=res.接下来就是题目中说到如果溢出,返回Interger.MAX_VALUE. 那怎么样才能得到这个溢出呢?如果直接定义int类型,那岂不是直接报错才能知道是溢出?所以这里就需要将他们全部转换为正long类型. 但为什么是正数?因为这样有利于数据的处理. 接着边角问题, 分子分母为0的问题.接下来就处理核心了,原则是这样的:通过2, 4, 8, 16 …倍数不断的接近被除数,然后剩余的值递归调用继续逼近.
比如:20/3

过程递归轮数剩余
3*2=6; 3*4 =12; 3*8>20120-12=8
3*2=6; 3*4>828-6=2
2<3结束递归返回0

直接上代码:

class Solution {
    public int divide(int dividend, int divisor) { 
        int sign = 1;//标记位用于正负
        if((dividend < 0 && divisor > 0) 
        || (dividend > 0 && divisor < 0)){
            sign = -1;
        }
        //取绝对值并强转为long
        long ldividend = Math.abs((long)dividend);
        long ldivisor = Math.abs((long)divisor);
        if(ldividend == 0 || ldividend < ldivisor){
            return 0;
        }
        //分母为0,管你正负都是无敌大
        if(ldivisor == 0){
            return Integer.MAX_VALUE;
        }
        //这个函数就是处理相除的功能,结果就是long类型的正数
       long longRes = ldivide(ldividend, ldivisor);
        //overflow?
        int res;//函数返回值是int,所以必须重新定义
        if(longRes > Integer.MAX_VALUE){
            res = (sign == 1)? 
            Integer.MAX_VALUE : Integer.MIN_VALUE;
        }
        else{
            res = (int)(sign * longRes);
        }
        return res;
    }
    private long ldivide(long ldividend, long ldivisor){
    //这个也是递归的出口
        if(ldividend < ldivisor){
            return 0;
        }
        long sum = ldivisor;
        long count = 1;//计数器用于记录倍数
        while((sum + sum) <= ldividend){
        //这种就是类似于乘以2的指数的形式
            sum += sum;
            count += count;
        }
        return count + ldivide(ldividend - sum, ldivisor);
    }
}
2. LC74. Search a 2D Matrix

题目
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.
使用公司:

难度:
中等


题目与思路分析:
题目是说二维数组,每行都是从左到右递增,每列都是从上到下递增,要快速找到target. 如果你想到的是双for遍历,那就gg了. 时间复杂度O(m*n)有点恐怖. 这里给出O(m+n)的时间复杂度. 首先充分利用给出的两个递增的条件. 我可以选择从最右上角出发, 然后如果target大于current,说明只能是往下走,如果是target小于current,那么说明只能是往左走. 这样循环就能很高效的找到目标了.
直接上代码:

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) { 
       if(matrix == null || matrix.length == 0){
            return false;
       }
       //start with top right
       int rowN = matrix.length;
       int colN = matrix[0].length;
       int x = 0;
       int y = colN - 1;
       while(x < rowN && y >= 0){
            if(matrix[x][y] == target){
                return true;
            }
            if(matrix[x][y] > target){
                y--;//注意行列的变化,不同于坐标
            }
            else{
                x++;
            }
       }
       return false;
    }
}
3. LC35. Search Insert Position

题目
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 1:
Input: [1,3,5,6], 0
Output: 0
使用公司:

难度:
容易


题目与思路分析:
题目意思是说给定一个排序了的数组,如果在里面找到target就返回index,如果没有找到target,那么就返回它插入的索引.这道题开始会觉得稍微会有些复杂情况考虑等,但是通过接下来的几道题就会锻炼出一个二分的套路来. 首先要清楚的是target小于等于当前的值都是返回的index就是插入的index. 这个题肯定要用到的是二分查找,保证O(logn). 大家在做二分的时候肯定头疼一件事就是while循环的时候的这个边界问题. 这里以后都记住,为了防止死循环死,我们要留下left和right,然后在循环外单独判断left和right的值.即while(start < end - 1),这样做能保证mid肯定不是和left,right相同,这样做会将思路很清晰.但是这样写时必须保证数组是三个以上元素的时候才有意义,所以元素为1 2的提前判断一下. 再一个是循环中left = mid加不加1的问题, 我自己总结,如果你有target进行比较,一般都会加1,因为在你的循环中肯定先判断nums[mid] == target,所以mid的值已经做了预判了,可以left直接跳过mid就行. 带着这个思路,这个题就很简单了.
直接上代码:

class Solution {
    public int searchInsert(int[] nums, int target) {
        if(nums == null || nums.length == 0){   
            return -1;
        }
        //把数组是1和2的长度的处理一下
        if(nums.length == 1){
            if(nums[0] >= target){
                return 0;
            }else{
                return 1;
            }
        }
        if(nums.length == 2){
            if(nums[0] >= target){
                return 0;
            }
            else if(nums[1] < target){
                return 2;
            }else{
                return 1;
            }
        }
        int left = 0;
        int right = nums.length - 1;
        while(left < right - 1){
            //这样写是防止数组长度巨大而溢出
            int mid = left + (right - left) / 2;
            if(target == nums[mid]){
                return mid;
            }
            if(target < nums[mid]){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        //处理循环停下的left和right
        if(nums[left] >= target){
            return left;
        }
        else if(nums[right] >= target){
            return right;
        }
        return right + 1;
    }
}
4. LC33. Search in Rotated Sorted Array

题目
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
使用公司:
Facebook, Microsoft, Bloomberg, Uber, Linkedln
难度:
中等


题目与思路分析:
题目是说给定了一个roated数组然后看有没有target,有的话返回index. 首先什么是roated 数组, 比如123456变为roated就是456123或561234等等.就类似于坐标轴的第二和第四象限的递增的类型。
这里写图片描述
但是要考虑清楚123456也是roated类型哦!!!, 接下来的思路就是先不断的逼近判断出递增的序列来,如果不是递增的序列那么就再次循环.这道题不用考虑重复元素的问题. 然后判断target是在mid的左面还是右面。这种roated的入手点都是left sorted,具体的模板参考上一题.
直接上代码:

class Solution {
    public int search(int[] nums, int target) {
        if(nums == null || nums.length == 0){
            return -1;
        }
        //处理长度为1和2的
        if(nums.length == 1){
            return nums[0] == target ? 0 : -1;
        }
        if(nums.length == 2){
            if(nums[0] == target){
                return 0;
            }else if(nums[1] == target){
                return 1;
            }else{
                return -1;
            }
        }
        int left = 0;
        int right = nums.length - 1;
        while(left < right - 1){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return mid;
            }
            //mid往左半部分肯定是sorted
            if(nums[mid] > nums[left]){
                //注意这里的条件是两种可能
                if(target > nums[mid] || target < nums[left]){
                    left = mid + 1;
                    //因为上面已经判断了mid和target,所以+1
                }
                else{
                    right = mid - 1;
                }
            }
            //逼近
            else{
                if(target < nums[mid] || target > nums[right]){
                    right = mid - 1;
                }
                else{
                    left = mid + 1;
                }
            }
        }
        //处理遗留left和right
        if(nums[left] == target){
            return left;
        }
        else if(nums[right] == target){
            return right;
        }
        return -1;
    }
}
5. LC81. Search in Rotated Sorted Array II

题目
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
Write a function to determine if a given target is in the array.
The array may contain duplicates.
使用公司:

难度:
中等


题目与思路分析:
这道题和上面的题是很类似的,唯独多了个字符串中有重复.这种情况下,我们就得考虑如何去掉这个重复而达到能和上面同样的处理效果.但是由于我们是不清楚这个重复值到底在左面右面还是中间,所以只能通过判断,当nums[mid]=nums[left]的时候说明left此刻是重复的,把left右移一位,然后继续循环.总之,这种rotated的题目都是在left上做文章,包括判断哪块是sorted.再者,找target的这种问题是不需要考虑是不是一条序列的,比如1234567。
直接上代码:

class Solution {
    public boolean search(int[] nums, int target) {
       if(nums.length == 0 || nums == null){
            return false;
       }
        if(nums.length == 1){
            return nums[0] == target ? true : false;
        }
        if(nums.length == 2){
            if(nums[0] == target || nums[1] == target){
                return true;
            }else{
                return false;
            }
        } 
        int left = 0;
        int right = nums.length - 1;
        while(left < right - 1){
            int mid = left + (right - left) / 2;
            if(nums[mid] == target){
                return true;
            }
            if(nums[mid] > nums[left]){
                if(nums[mid] < target || nums[left] > target){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
            else if(nums[mid] < nums[left]){
                if(target < nums[mid] || target > nums[right]){
                    right = mid - 1;
                }else{
                    left = mid + 1;
                }
            }
            else{
                left++;//这样导致最坏结果的时间复杂度O(n)
            }
        }
        //判断剩余Left和right
        if(target == nums[left] || target == nums[right]){
            return true;
        }
        return false;
    }
}
6. LC153. Find Minimum in Rotated Sorted Array

题目
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
Find the minimum element.
You may assume no duplicate exists in the array.
使用公司:
Microsoft
难度:
中等


题目与思路分析:
这种题目的分析思路与做题模板应该通过上面的理解与练习差不多了解了,这道题不一样的点是,不是找target,而是寻找最小值.这样的话思路稍微变化一下就是要不断的逼近最中间,最终的结果肯定在最后的Left和right中,但是这个题就需要比上面的比target的题目多一种情况考虑就是如果12345678这种没有roated的roated数组.因为这种情况不能逼近,最小的就是nums[0]. 只要通过left是不是小于right就能判断了.然后这道题是不需要考虑重复元素的情况,想必重复元素的情况也很好解了.
直接上代码:

class Solution {
    public int findMin(int[] nums) {
        if(nums.length == 0 || nums == null){
            return -1;
        }
        if(nums.length == 1){
            return nums[0];
        }
        if(nums.length == 2){
            return Math.min(nums[0], nums[1]);
        }
        int left = 0;
        int right = nums.length - 1;
        while(left < right - 1){
            int mid = left + (right - left) / 2;
            if(nums[left] < nums[right]){//no rotated
                return nums[left];
            }
            //left is sorted
            if(nums[left] < nums[mid]){
                left = mid;
                //这里不进行+1就是因为上面没有判断过mid的值    
            }else{
                right = mid;
            }
        }
        return Math.min(nums[left], nums[right]);
    }
}
7. LC154. Find Minimum in Rotated Sorted Array II

题目
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
Find the minimum element.
The array may contain duplicates.
使用公司:

难度:


题目与思路分析:
虽然标着是个hard的题目,但是通过上面的练习,这完全可以算个简单题了. 注意处理非rotated的rotated数组,注意left = mid加不加1的问题.在一个处理nums[left]和nums[mid]相等时进行left右移.
直接上代码:

class Solution {
    public int findMin(int[] nums) {
        if(nums.length == 0 || nums == null){
            return -1;
        }
        if(nums.length == 1){
            return nums[0];
        }
        if(nums.length == 2){
            return Math.min(nums[0], nums[1]);
        } 
        int left = 0;
        int right = nums.length - 1;
        while(left < right - 1){
            int mid = left + (right - left) / 2;
            if(nums[left] < nums[right]){
                return nums[left];
            }
            if(nums[left] < nums[mid]){
                left = mid;
            }
            else if(nums[left] > nums[mid]){
                right = mid;
            }else{
                left++;
            }
        }
        return Math.min(nums[left], nums[right]);
    }
}

总结

今天主要处理的就是roated的问题,按照这种套路模板能比较清晰的处理这些问题. 注意while()循环的参数大于等于的控制问题. 这样写出来思路不会因为题目的不同而混乱掉. 二分查找还会继续, 但把握这个做题思路应该都不会太难.今天还有的即使2D矩阵的快速搜索,一定要充分利用题中给出行递增,列递增的条件,然后想到总右上开始行动.欢迎大家提意见.


联系方式: reyren179@gmail.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值