leetcode:二分查找、移除元素

1、二分查找

很经典的一道题,直接用二分模板即可,但有几点需要注意:

  • 不是传统的二分模板,l 和 r 是永远不会交错的,只会到分界线处相邻,即 r - l == 1
  • 心中要清楚,l 分区和 r 分区在题目中分别代表的性质
  • 循环结束后,如果需要使用 l、r 判断某条件时,要考虑到可能分区根本没有移动的情况,此时 l / r 是在数组合法范围外的!

二分模板:

详细说明,往期博客:二分专题----如何优雅的写出二分

cpp 复制代码
l=-1, r=N  //在(l,r)的数组索引开区间内,数组元素都是灰色的
 
while l+1≠r  //l+1=r时,蓝红区域接触
 
	m = ⌊(l+r)/2⌋  //开区间中间元素
 
	if IsBlue(m)
 
		l=m  //蓝色区域向右拓展到中间元素
 
	else
 
		r=m  //红色区域向左拓展到中间元素
 
//根据实际情况返回l或r,但要注意:返回谁就要对谁做越界判断!
return l or r  

本题代码:

cpp 复制代码
class Solution {
public:
    int search(vector<int>& nums, int target) 
    {
        int n = nums.size();
        int l = -1, r = n;
        while(r - l > 1) // l + 1 != r
        {
            int mid = (l + r) >> 1;
            if(nums[mid] < target)
                l = mid;
            else
                r = mid;
        }
        int ret = -1;
        if(r <n && nums[r] == target) // 先判断r是否合法
            ret = r;
        
        return ret;
    }
};

2、移除元素

本题类似于实现vector中的erase方法,只不过标准库的erase是只能用迭代器删除元素的:

而本题要实现的是,指定vector中的一个值val,将数组中所有val值都删除,并且返回删除后的有效元素个数,且可以打乱原有的顺序。

本题的数据范围很小,最多只有100个元素,很直接的我们可以想到暴力做法:

1、双重循环:外层找出val,内层移动后续元素覆盖时间复杂度是O(n),空间复杂度也是O(1)。

2、空间换时间:新开一个vector,遍历原vector,将不是val的值插入新的vector中,再赋值给原vector,这样时间复杂度是O(n),空间复杂度也是O(n)。


第二种方法并不符合题意,题目要求原地修改数组 ,那么除了第一种暴力,还有更优化的做法吗?-------双指针,数组操作中很常见的优化方法

1、同向双指针:定义快慢指针,slow在原数组中更新新数组,fast遍历数组元素,将不是val的元素更新到slow

2、对撞指针:定义左右指针,左指针找出等于val的位置,右指针找出不等于val的指针,然后将右指针指向的值赋值给左指针处,直到左右指针错过。

对撞指针中有很多细节,循环中取等问题、赋值时的条件判断

代码如下:

暴力代码1:双重循环:

cpp 复制代码
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
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--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                size--; // 此时数组的大小-1
            }
        }
        return size;

    }
};

暴力代码2:空间换时间(不满足原地修改数组的题意!)

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) 
    {
        int n = nums.size();
        vector<int> arr;
        for(int i = 0; i < n; i++)
        {
            if(nums[i] != val)
                arr.push_back(nums[i]);
        }
        nums = arr;

        return arr.size();
    }
};

双指针代码1:同向双指针

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) 
    {
        int n = nums.size();
        int fast, slow = 0;

        //fast遍历所有元素,忽略val,其余赋值到slow处
        //slow指向待更新的新数组处,slow之前的全部是合法元素
        for(fast = 0; fast < n; fast++)
        {
            if(nums[fast] != val)
            {
                nums[slow++] = nums[fast];
            }
        }

        return slow;
    }
};

双指针代码2:对撞指针

cpp 复制代码
class Solution {
public:

    int removeElement(vector<int>& nums, int val) 
    {
        int n = nums.size();
        int l = 0, r = n - 1;

        //必须取等,如果不取等,l、r相遇的位置还没有判断
        //如果是合法值,不取等的话,l不会右移,最终结果会遗漏
        while(l <= r)
        {
            // l找出等于val的值
            while(l <= r && nums[l] != val) l++;
            // r找出不等于val的值
            while(l <= r && nums[r] == val) r--;
            //上面的寻找中,l、r可能已经错过了,需要判断
            if(l < r) nums[l++] = nums[r--];
        }

        //l代表:l之前的全是合法元素(全都是经过l判断过的)
        return l;
    }
};
相关推荐
靠沿1 分钟前
【优选算法】专题十八——BFS解决拓扑排序问题
算法·宽度优先
cui_ruicheng4 分钟前
C++数据结构进阶:哈希表实现
数据结构·c++·算法·哈希算法·散列表
li星野15 分钟前
[特殊字符] 模拟试卷一:C++核心与系统基础(90分钟)答案版
开发语言·c++·算法
二进制星轨29 分钟前
leecode-283-移动零-算法题解
算法
老鼠只爱大米36 分钟前
LeetCode经典算法面试题 #215:数组中的第K个最大元素(快速选择、堆排序、计数排序等多种实现方案详解)
算法·leetcode·堆排序·快速选择·topk·数组中的第k个最大元素
2301_8166512243 分钟前
C++中的享元模式变体
开发语言·c++·算法
逆境不可逃1 小时前
LeetCode 热题 100 之 35. 搜索插入位置 74. 搜索二维矩阵 34. 在排序数组中查找元素的第一个和最后一个位置
数据结构·算法·leetcode
m0_583203131 小时前
C++中的访问者模式变体
开发语言·c++·算法
浅念-1 小时前
C ++ 智能指针
c语言·开发语言·数据结构·c++·经验分享·笔记·算法
不染尘.1 小时前
最小生成树算法
开发语言·数据结构·c++·算法·图论