数组和双指针框架

  • 快慢指针:有序数组/链表原地去重、数组/链表原地删除
  • 快慢窗口指针:在限定条件下找最长/短的连续子序列/子串/子数组
  • 左右最值指针:缩减一维/二维有序搜索空间



 


快慢指针:有序数组/链表原地去重、数组/链表原地删除

题目:26.删除有序数组中的重复项

数组和双指针框架_算法


核心模式:

  • 数组已经排序,所以重复的元素一定连在一起

初始化一个慢指针和一个快指针都为0,慢指针记录最终的结果,快指针遍历整个数组。

快指针fast从数组第一个元素遍历到数组的最后一个元素,当快指针找到和慢指针不重复的值,就把fast的值赋予slow(不重复的数组添加到最终结果),并且slow指向下一个元素,最后 slow 就是需要的结果。

练习:第 83 题「删除排序链表中的重复元素」

如果给你一个有序的单链表,如何去重呢?

其实和数组去重是一模一样的,唯一的区别是把数组赋值操作变成操作指针而已。
 


题目:27.移除元素

数组和双指针框架_链表_02


原地删除也使用快慢指针。

初始化一个慢指针和一个快指针都为0,慢指针记录最终的结果,快指针遍历整个数组。

快指针fast从数组第一个元素遍历到数组的最后一个元素,当快指针找到的不是需要删除的值,就把fast的值赋予slow(没被删除的元素添加到最终结果),并且slow指向下一个元素,最后 slow 就是需要的结果。

 


快慢窗口指针:在限定条件下找最长/短的连续子序列/子串/子数组

滑动窗口:

  • 3.无重复字符的最长子串
  • 209.长度最小的子数组
  • 219.存在重复元素 II
  • [220].存在重复元素 III
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
    unordered_map<char, int> need, window;
    for (char c : t) need[c]++;
    int left = 0, right = 0;
    int valid = 0;
    while (right < s.size()) {
        char c = s[right];         // c 是将移入窗口的字符
        right++;                   // 右移(增大)窗口
        // 进行窗口内数据的一系列更新
        while (window needs shrink) {
            char d = s[left];      // d 是将移出窗口的字符
            left++;                // 左移(缩小)窗口
        }
    }
}

数组和双指针框架_数据结构_03

 


左右最值指针:缩减一维/二维有序搜索空间

左右指针实现的二分查找,能缩减一维有序数组的搜索空间,每次排除一半的元素来减少比较的次数。

因为一维有序数组的二分查找,太普及了,就不额外写了。

可在二维有序空间中却不能使用二分查找。

二分搜索的中间点,显然应该是矩阵的中心点:

数组和双指针框架_数组_04


这个中心点把矩阵分成的四个部分中,只有左上部分全部小于中心点,只有右下部分全部大于中心点。也就是说做一次比较最多只能削减 1/4 的搜索空间。更大的问题是,删除掉左上部分或者右下部分之后,剩余的形状不再是一个规则的矩形,就无法继续进行二分搜索了。

数组和双指针框架_有序数组_05

二分查找只能做左上、右下的切分,左下、右上不能二分。

其实左右指针的搜索算法,不止可以有一维的二分搜索,还有二维的行列搜索,每次排除一行/列元素来减少比较的次数。

题目:167.俩数之和

数组和双指针框架_算法_06


使用了两个指针,一个只向左移动,一个只向右移动。走完一趟,就得到了答案。

具体解析: