双指针

一、双指针介绍

一般来说是使用两个指针(大多数情况是使用的两个变量)来对一个对象进行处理,两个指针指向不同的元素,然后进行比较从而解决问题

可以用于优化暴力算法的时间复杂度

例如:给你一个已近排好序的数组v,现在需要你删除数组中的重复元素,并输出删除后的数组的长度

当然你可以的直接使用unique直接去重然后输出输出数组的大小,这里我们使用不同的方法去做并且保证O(n)的复杂度。

思路:我们只需要在遍历数组的时候不断记录长度,当我们遇到相同的值的时候直接跳过就行,可以这样做的原因是题目给出了数组是已经排好序的了

这里我们可以定义两个变量 i  = 1和 j = 0,一个在前一个在后那么对于数组 v 就是v[i]和v[j],通过比较这两个值是否相等来确定是否要跳过,不跳过就更新v[j]的值,最后输出 j + 1 即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()

{
    vector<int> v = {0, 0, 1, 1, 1, 2, 2, 3, 3, 4};
    //先确定数组长度
    int len = v.size();
    int j = 0;
    for (int i = 1; i < len ; i++)//开始遍历
    {
        if(v[i] != v[j]){//不断的比较一前一后的值是否相等
          j++;
         v[j] = v[i];
        }
    }
    cout << j + 1;
}    

通过这个题我们可以体会到,i 和 j就像两个指针指向的是数组v的不同位置,然后通过比较得到结果

二、相向双指针

定义的两个指针王同一个方向走,就比如一个数组nums,我们有L和R两个指针分别对应数组的头和尾,然后他们都要往中间走,就是相向双指针。

例如:leetcode163

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1  index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

思路:如果你采用的是暴力,那么时间复杂度是O(n2),我们可以重点利用一下这个“非递减顺序排列”,对于一个排好序的数组采用双指针是可以将时间复杂度优化到O(n)

具体的就是定义两个指针L = 0和R  = number.length() - 1 对应数组的头和尾,然后开始遍历进行判断

1.numbers[l] + numbers[r] == target直接就break出去得到答案

2.numbers[l] + numbers[r] > target,因为数组是升序排列的,那么就说明最大的加最小的比目标值大,所以我们要将最大值减小,即R -= 1

3.numbers[l] + numbers[r] < target,同上,但是这次发现的是比目标值还小,所以我们要将最小值增大,即L += 1

代码如下:

class Solution
{
public:
    std::vector<int> twoSum(std::vector<int> &numbers, int target)
    {
        std::vector<int> ans;
        int l = 0, r = numbers.size() - 1;
        while(l <r){
            if(numbers[l] + numbers[r] == target)
            break;
            if (numbers[l] + numbers[r] > target)
                r -= 1;
            if (numbers[l] + numbers[r] < target)
                l += 1;
        }
    //这里答案加一是因为题目要求的是下标从1开始
        ans.push_back(l + 1);
        ans.push_back(r + 1);
        return ans;
    }
};

 三、滑动窗口

它属于双指针中比较重要的一个概念。顾名思义,对于一个数组我们定义两个指针那么这两个指针之间的数就类似被包含在一个窗口中,接下来我们不断地移动左右指针,使得这个窗口可以向右向左滑动。

例如:我们给出一个长度为N的数组和一个整数K,现在让你从数组中找出一个长度为K的子数组,并且要求这个子数组和的值是最大的。

N = {1,9,8,6,4,7,8} , k = 2,那么答案就是{9,8}

思路:我们分别定义两个指针L 和 R ,这个时候我们的窗口就定义好了即[ L , R]。现在我们需要滑动这个窗口,每次向右滑动一位这个窗口就变为了[ L + 1 , R + 1],那么对于这个题而言,初始的窗口就是

[0, k - 1]。sum就是这个窗口内的和,当我们向右滑动的时候,窗口变为了[1, k],sum增加了N[k],减小了N[0],依次类推,最后我们就可以得到答案

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()

{
    vector<int> v = {1, 9, 8, 6, 4, 7, 8};
    int index = 0;//用index来记录下我们最大子数组的左边界
    int n = v.size(), k = 2;
    int sum = 0,total = 0;
    for (int i = 0; i < n; i++){
        sum += v[i];//从0这个位置开始滑动,不断增加v[i]
        if(i >= k - 1){//当到达K - 1的时候就表示确定了一个长度为K的子数组
            if(sum > total){//判断一下当前的子数组的和是不是最大的
                total = sum;//更新子数组的和
                index = i - k + 1;//记录下边界
            }
            sum -= v[i - k + 1];//更新滑动后sum的值
        }
    }
    while(k--){
        cout << v[index++] << " ";    
    }
    return 0;
}

 

以上就是基本的双指针的使用。

 

posted @ 2023-11-19 14:42  clinx000  阅读(123)  评论(0编辑  收藏  举报