算法:二分查找和双指针

408 阅读3分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

220906-二分查找

1. 前提条件

  • 元素唯一
  • 有序

2.* 区间定义的两种方法*(还需体会)

  1. [left, right] 左闭右闭

    • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
    • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
  2. [left, right] 左闭右开

    • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的

    • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

      出了while循环后只可能是left == right的情况

3. CPP代码

int  search(vector<int>&  nums, int  target) {
    int x = 0;
    int y = nums.size() - 1;
    while(x <= y) {
int m = (x+y)/2;
        if(target == nums[m]) return m;
        if(target < nums[m]) y = m-1;
        if(target > nums[m]) x = m+1;
    }
    return -1;
}

二分查找的时间复杂度为O(logn)。

4. 相关题目

704. 二分查找

278. 第一个错误的版本

35. 搜索插入位置

需要额外考虑插入相同元素的情况

5. 额外学到

  • 不能用(left+right)/2形式,当left和right都是int,两个值的初始值都超过int限定大小的一半,那么left+right就会发生溢出,所以应该用left+(right-left)/2来防止求中值时候的溢出。

220907-双指针

1. 相关题目

977. 有序数组的平方

进阶要求:时间复杂度为O(n)——>使用双指针

189. 轮转数组

至少3种方法 空间复杂度为O(1)原地解决?

  1. 使用额外的数组

    for(int i = 0; i <= nums.size() - 1 ; i++) {
       arr[(i+k)%nums.size()] = nums[i];
    }
    

    缺点是需要建立一个新的数组。

    时间复杂度:O(n);空间复杂度O(n)。

  2. reverse方法

    基本思想是整个数组全部翻转后,再分段翻转,好处是不用设置额外的数组,但要小心k>=nums.size()和k==0的情况,所以多写了一句k = k % nums.size();

    void rotate(vector<int>& nums, int k) {
       k = k % nums.size();
       reverse(nums, 0, nums.size()-1);
       reverse(nums, 0, k - 1);
       reverse(nums, k, nums.size()-1);
    }
    

    这里是自己写了一个reverse函数,但后来了解到cpp中有**reverse(first, last)**函数,所以改成了:

    void rotate(vector<int>& nums, int k) {
       k = k % nums.size();
       reverse(nums.begin(), nums.end());
       reverse(nums.begin(), nums.begin()+k);
       reverse(nums.begin()+k, nums.end());
    }
    

    其中,reverse函数翻转的范围是[first, last)。vector::begin()返回指向第一个元素的迭代器,vector::end()返回指向最后一个元素之后的元素的迭代器。

    时间复杂度:O(n);空间复杂度O(1)。

  3. 环状替换(想不通啊啊啊)

2. 额外学到

  • 新建vector数组vector<int> arr(nums.size(), 0);
  • nums.assign(newArr.begin(), newArr.end());填充值,并适配到相应大小

本周总结

        本周是开始刷力扣的第一周,以二分查找和双指针为起点渐渐回忆起考研时学习的基本算法,后续还需要继续复习与学习。与考研或平时作业不同的是,上机实践还需要考虑设计算法的时间和空间复杂度,希望以后能把这种意识带到每一道题里。

  • 还需深入体会的:二分查找区间定义的两种方法。

  • 薄弱的地方:与数组相关的空间复杂度为O(1)的问题,

    • 其中“环状替换法”还不理解。