【超详细~】双指针算法的使用

1.使用场景:

一般在需要取数组某一段元素时,可以快慢指针并行

双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。

2.类型:

快慢指针,头指针与尾指针,滑动窗口 等

3.使用方法

(1)for循环里面的j就相当于一个指针

然后可以定义一个全局变量i作为另一个指针,在for里面用i++移动指针

let i = 0
for (let j = 0; j < arr.length; j++){
    
    
    arr[i++] = arr[j]
}

(2)有时是for循环里面直接定义两个指针

for (let i = 1, j = 0; arr[i] < arr[j]){
    
    
    
}

4.应用场景

(1)数组移除元素

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

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

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

思路:快指针寻找新数组的元素(不等于val的元素)

慢指针标识新数组当前能够添加新元素的位置(实际上是覆盖旧数组的元素)

// 时间复杂度: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;
    }
};

(2)有序数组的平方(非递减顺序的数组,有正有负)

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

思路:一个头指针,一个尾指针

比较两者当前所指元素的平方的值,放入结果集中

class Solution {
public:
    vector<int> sortedSquares(vector<int>& A) {
        int k = A.size() - 1; //指向新数组,从尾部开始移动
        vector<int> result(A.size(), 0); //新数组
        for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
            if (A[i] * A[i] < A[j] * A[j])  {
                result[k--] = A[j] * A[j];
                j--;
            }
            else {
                result[k--] = A[i] * A[i];
                i++;
            }
        }
        return result;
    }
};

(3)长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

思路:滑动窗口也可以理解为双指针法的一种!

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口起始位置
        int subLength = 0; // 滑动窗口的长度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) { //一旦和超过了目标值
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength; //如果出现了更短的,就把结果改成更短的
                sum -= nums[i++]; // 移除第一个元素。这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

猜你喜欢

转载自blog.csdn.net/m0_58768224/article/details/129797030