Day 01 | 二分查找算法 双指针算法 及题目分析

前提

1、有序序列
2、无重复元素值

两个注意点

1、时刻记住自己的区间定义,区间定义关系到自己的代码该怎么写
2、如何判断right = mid 还是 right = mid - 1

区间定义通常有两种:

  • [left, right]
  • [left, right)

题目:leetcode 704 二分查找

题意:给定一个数组,查找目标值是否在数组中,存在则返回数组小标,否则返回-1

思路 1:暴力搜索 - 复杂度O(n) 直接遍历整个数组,判断是否存在目标值即可

class Solution {
public:
    int search(vector<int>& nums, int target) {
        for (int i  = 0; i < nums.size(); i++) {
            if (nums[i] == target) {
                return i;
            } 
        }
         return -1;
    }
};

思路 2: 二分查找
第一种二分: 区间定义为[left, right]
这种定义方式意味着 while循环条件 为while(left <= right),因为两边都是闭区间,可以取到right, 同时可以知道此时right初始值为right = nums.size() - 1

size函数是返回容器中元素的个数,而数组下标是从0开始的,所以right = nums.size() - 1

接下来进行判断,if(nums[mid] > target),说明区间过了,下次查询应该在左区间中,其中左区间的right为right = mid - 1

原因:由刚刚的if条件判断知道,当前的nums[mid]肯定不是我们要找的target,所以在下次查询是要排除它,又因为区间是左闭右闭的,所以right= mid - 1

接下来判断if(nums[mid] < target),同样说明区间过了,下次查询应该在右区间,其中右区间的left为left = mid + 1

原因:同样当前的nums[mid]肯定不是我们要找的target,所以在下次查询是要排除它,又因为区间是左闭右闭的,所以left = mid + 1

最后判断if(nums[mid] == target),那么返回mid值return mid;,此时的mid 即为目标值的下标

完整代码如下:

第一种二分
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1; 
        
        while (left <= right) {
            int mid = left + ((right - left) / 2);
            if (nums[mid] > target) {
                right = mid - 1;
            } else if(nums[mid] < target) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
};

第二种二分: 区间定义为[left, right)
这种定义方式意味着 while循环条件 为while(left < right),因为right是开区间,取不到right ,同时可以知道,此时right初始值为right = nums.size()
接下来进行判断,if(nums[mid] > target),说明区间过了,下次查询应该在左区间中,其中左区间的right为right = mid

原因:由刚刚的if条件判断知道,当前的nums[mid]肯定不是我们要找的target,所以在下次查询是要排除它,又因为区间是左闭右开的,所以right= mid

接下来判断if(nums[mid] < target),同样说明区间过了,下次查询应该在右区间,其中右区间的left为left = mid + 1

原因:同样当前的nums[mid]肯定不是我们要找的target,所以在下次查询是要排除它,又因为区间是左闭右闭的,所以left = mid + 1

最后判断if(nums[mid] == target),那么返回mid值return mid;,此时的mid 即为目标值的下标

第二种二分
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        
        while (left < right) {
            int mid = left + ((right - left) / 2);
            if (nums[mid] > target) {
                right = mid ;
            } else if(nums[mid] < target) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
};

写在最后

第一次写二分的时候把int mid = left + ((right - left) / 2);写到while循环外面了,导致超时,因为mid值一直没更新
总结:二分查找重要的就是时刻记住自己的区间定义,是左闭右闭还是左闭右开,确定了区间定义后,后面的while循环条件以及right的值right = mid 还是 right = mid - 1都可以迎刃而解

leetcode27 移除元素

题意:给定一个数组,一个目标值,删除数组中存在的目标值,返回数组的的大小 思路1:暴力方法 —— 时间复杂度O(n2)

两层 for 循环,第一层循环遍历整个数组,条件判断nums[i] == val,第二层 for 循环,将数组中所有元素往前挪一位,结束后i--的原因是
i原来所在的位置被新的数覆盖了,这个新的数也要判断是不是等于目标值,即i++ 和 i--抵消了

完整代码如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for(int i = 0; i < size; i++) {
            if (nums[i] == val) {
                for(int j = i + 1;  j < size; j++) {
                    nums[j - 1] = nums[j];
                }
                i--;
                size--;
            }
        }
        return size;
    }
};

思路2:双指针算法——时间复杂度O(n)

fastIndex :寻找新数组元素,即不等于目标值的元素
slowIndex :指向新数组最后一个元素的位置

一层for循环让快指针fastIndex动起来 ,条件判断nums[fastIndex] != val,因为当前指向的值与目标值不相等,所以赋值nums[slowIndex] = nums[fastIndex] ,slowIndex++,这里当出现指向的值与目标值相等的时候,fastIndex还会向前走slowIndex则不动,然后把fastIndex指向的值覆盖slowIndex指向的值

完整代码如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); fastIndex++){
            if (nums[fastIndex] != val){
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
            
        }
        return slowIndex;
    }
};

总结:

今天对二分查找方法相比之前又清楚了很多,主要归功于第一次开始写博客,可以把自己的理解写下来供自己以后来看。

移除元素题第一眼暴力解法有些问题,看了题解后可以很顺利地写出来,双指针求解还需要再多消化消化

注明:该博客同样分享在我的掘金账号Day 01| 二分查找算法及题目分析 - 掘金

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值