代码随想录算法训练营第一天 | 数组理论基础+LeetCode704. 二分查找,LeetCode27. 移除元素

学习文档:数组理论基础
文章链接:https://programmercarl.com/
状态:已完成


前言

数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标下对应的数据。需要两点注意的是:
1.数组下标都是从0开始的
2.数组内存空间的地址是连续的
正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
因此数组的元素是不能删的,只能覆盖。


提示:以下是本篇文章正文内容,下面案例可供参考

一、LeetCode704. 二分查找

题目要求

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

来源:力扣(LeetCode)链接

解题思路

这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。

// 二分查找方法一:target 是在一个在左闭右闭的区间里,也就是[left, right] 
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;// 定义左边 
        int right = nums.size() - 1;// 定义右边
        while (left <= right) {     // 当left == right 时,区间[ ]依然有效,所以有等号
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {// 说明target在左侧
                right = middle - 1;
            }
            else if (nums[middle] < target){// 说明target在右侧
                left = middle + 1;
            }
            else { 
                return middle;// 数组中找到目标值,直接返回下标
            }
        }
    return -1;
    }
};

时间复杂度:O(log n)
空间复杂度:O(1)
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2 ,更高明

二、LeetCode27. 移除元素

题目要求

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-element

解题思路

思路1:暴力解法

这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。

暴力解法代码

// 解法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--;//size同理
            }
        }
    return size;
    }
};
//时间复杂度:O(n^2)
//空间复杂度:O(1)

思路2: 双指针(快慢指针)

由于题目要求删除数组中等于 val 的元素,因此输出数组的长度一定小于等于输入数组的长度,我们可以把输出的数组直接写在输入数组上。通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

定义快慢指针
快指针fastIndex:指向当前将要处理的元素,寻找新数组的元素 ,新数组就是不含有目标元素的数组。
慢指针slowIndex:指向下一个将要赋值的位置。

如果右指针指向的元素不等于 val,它一定是输出数组的一个元素,我们就将右指针指向的元素复制到左指针位置,然后将左右指针同时右移;

如果右指针指向的元素等于 val,它不能在输出数组里,此时左指针不动,右指针右移一位。

整个过程保持不变的性质是:区间 [0, slowIndex) 中的元素都不等于 val。当左右指针遍历完输入数组以后,slowIndex 的值就是输出数组的长度。

这样的算法在最坏情况下(输入数组中没有元素等于 val),左右指针各遍历了数组一次。
示例

双指针代码

// 解法2 双指针法(快慢指针法)
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); fastIndex++){//快指针只寻找符合要求的
            if(val != nums[fastIndex]){
                nums[slowIndex++] = nums[fastIndex];//慢指针是新数组的更新下标的位置
            }
        }
    return slowIndex;
    }
};
//实现方法并没有改变元素的相对位置

思路3: 双指针优化

思路
若需要移除的元素正好在数组的开头,需要把每一位元素都左移一位,题目中说元素的顺序可以改变,那可以让前后的数组元素交换顺序,则数组需要移动最少元素即可。实现方面,我们依然使用双指针,两个指针初始时分别位于数组的首尾,向中间移动遍历该序列
算法
左指针:leftIndex
右指针:rightIndex
如果左指针 leftIndex 指向的元素等于 val,此时将右指针 rightIndex 指向的元素复制到左指针 leftIndex 的位置,然后右指针 rightIndex 左移一位。如果赋值过来的元素恰好也等于 val,可以继续把右指针 rightIndex 指向的元素的值赋值过来,直到左指针指向的元素的值不等于 val 为止。
当左右指针重合的时候,算法终止,左右指针正好遍历完所有的元素。这样的方法两个指针在最坏的情况下合起来只遍历了数组一次。
与方法一不同的是,方法二避免了需要保留的元素的重复赋值操作。

双指针优化代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int leftIndex = 0, rightIndex = nums.size();
        while (leftIndex < rightIndex) {
            if (nums[leftIndex] == val) {//替换掉左指针
                nums[leftIndex] = nums[rightIndex - 1];//数组元素下标从0开始
                rightIndex--;//右指针左移一位
            } else {
                leftIndex++;
            }
        }
        return leftIndex;
    }
};

总结

看起来不错的算法还是有优化空间,要多思考多联系。双指针优化代码参考了LeetCode官方解法,感觉更为精炼,仅供参考。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值