双指针及其延伸问题汇总

第二部分:双指针及其延伸问题汇总

1、主要涉及的一些思想

(1)双指针主要是用于遍历数组,二叉树,甚至是图等结构,两个指针指向不同的元素,协同完成任务,当然也可以延伸到多指针的使用。

(2)当两个指针指向同一个数组,遍历方向相同且不会相交,移动的速度相同,也就是说这两个指针的相对位置不会发生改变,这也就是滑动窗口问题。

(3)两个指针指向同一个数组,遍历方向相同,但是移动的速度不同,这也就是快慢指针问题,可以快速找到链表的中点,三分点等等分点

(4)两个指针指向同一个数组但是方向相反,这样的问题一般针对的都是排好序的问题,但是也不能完全这样套,对于复杂度高且无序的初始问题,我们采用sort函数来排序,可以自己设立比较的规则。

(5)两个指针指向不同的数组,分别按照一定的规则来进行移动比较。

(6)结合多指针方法和其他的数据结构。

2、具体的一些例子

注:这里有的是给出leetcode的例题,有的仅仅是口述

滑动窗口

简单版:比如给出这样的一个例子,对于一个链表,你不知道它的长度,请找到这个链表的倒数第二个节点,这里就可以设置两个指针,一个指向头节点,一个指向头节点的下一个节点,两指针不断向后遍历直到第二个指针到达链表的尾部节点,此时第一个指针指向的就是倒数第二个节点。

进阶版:leetcode 76
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

class Solution {
public:
    unordered_map <char, int> ori, cnt;

    bool check() {
        for (const auto &p: ori) {
            if (cnt[p.first] < p.second) {
                return false;
            }
        }
        return true;
    }

    string minWindow(string s, string t) {
        for (const auto &c: t) {
            ++ori[c];
        }

        int l = 0, r = -1;
        int len = INT_MAX, ansL = -1, ansR = -1;

        while (r < int(s.size())) {
            if (ori.find(s[++r]) != ori.end()) {
                ++cnt[s[r]];
            }
            while (check() && l <= r) {
                if (r - l + 1 < len) {
                    len = r - l + 1;
                    ansL = l;
                }
                if (ori.find(s[l]) != ori.end()) {
                    --cnt[s[l]];
                }
                ++l;
            }
        }

        return ansL == -1 ? string() : s.substr(ansL, len);
    }
};

快慢指针

简单版:比如给出这样的一个例子,对于一个链表,你不知道它的长度,请找到这个链表的中部节点,这里就可以设置两个指针,开始都指向头节点,但是两个指点的移动速度不同,一个每次移动一个节点,一个每次移动两个节点,两指针不断向后遍历直到第二个指针到达链表的尾部节点,此时第一个指针指向的就是中部节点。

变化版:针对于链表找环路问题,有一个通用的解法-Floyd判圈法。

相反方向的双指针

:这里主要是提到的是针对于排序的数组,或者说你可以自己去排序的数组,典型题:Two Sum 及其变种问题

Leetcode 167:Two Sum

给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。

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

典型解法:这里是已经排好序的数组,不需要我们进行排序的相关操作,所以在这里直接设置left,right两个指针,分别指向升序数组的头部和尾部,判断两者的加和是否等于目标值,如果大了,那就使得right向左移动,减小整体的值,如果小了,那就使得left向右移动,增大整体的值,可以证明最终可以得到问题的解,当然要是有解的话。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
     int cur1 = 1,cur2 = numbers.size();
     vector<int>v;
     while(cur1<cur2)
     {
      if(numbers[cur1-1]+numbers[cur2-1]<target)
      {
          cur1++;
      }
      else if(numbers[cur1-1]+numbers[cur2-1]>target)
      {
          cur2--;
      }
      else
        {
        v.push_back(cur1);
        v.push_back(cur2);
        break;
        }
     }
     return v;
    }
};

Leetcode 680:Two Sum 变种

给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

示例 1:

输入: “aba”
输出: True

class Solution {
public:
    bool palindrome(const std::string& s, int i, int j)
    {
        for ( ; i < j && s[i] == s[j]; ++i, --j);
        return i >= j;
    }

    bool validPalindrome(string s) {
        int i = 0, j = s.size() - 1;
        for ( ; i < j && s[i] == s[j]; ++i, --j);        
        return palindrome(s, i, j - 1) || palindrome(s, i + 1, j);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值