有序数组双指针问题

有序数组双指针问题:简单问题及其变种

  • [问题 1: 有序数组使用双指针快速搜索](#问题 1: 有序数组使用双指针快速搜索)
    • [步骤 1 :分析并使用双指针](#步骤 1 :分析并使用双指针)
    • [步骤 2 :实现](#步骤 2 :实现)
    • 注意
  • [问题 2: 变种问题(三数之和)](#问题 2: 变种问题(三数之和))
    • [步骤 1 :分析并排序](#步骤 1 :分析并排序)
    • [步骤 2 :分析并使用双指针](#步骤 2 :分析并使用双指针)
    • [步骤 3 :优化](#步骤 3 :优化)
    • [步骤 4 :实现](#步骤 4 :实现)

问题 1: 有序数组使用双指针快速搜索

我们以 力扣167. 两数之和 II - 输入有序数组 为例对该问题进行求解

步骤 1 :分析并使用双指针

1、重要信息:numbers已经"非递减顺序排列"!!!!,我们一定要利用这个信息。

2、我们将双指针分别指向数组的头和尾,如果此时头尾数的和比target大,因为数组是非递减排序,并且我此时需要一个更大的数,所以我们只需要将左指针右移,此时和必然会增加。

3、如果此时头尾数的和比target小,同理,我们只需要将右指针左移,就可以减小两个数的和。

4、用以上的办法逐渐进行逼近,直到逼近到正确的双指针位置。

步骤 2 :实现

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int n = numbers.size();
        int i = 0, j = n - 1;//双指针初始化
        while (i < j) {
            if (numbers[i] + numbers[j] == target) {//因为答案要从1开始
                return { i + 1,j + 1 };
            }
            else if (numbers[i] + numbers[j] > target) {
                j--;
            }
            else {
                i++;
            }
        }
        return {};
    }
};

注意

复制代码
值得注意的是,以上双指针解法的前提都是,随着左右指针的移动,两数之和能严格增长或者下降,也就是满足数组的非递减特性才可以这么做。

问题 2: 变种问题(三数之和)

我们以 力扣15. 三数之和 为例对该问题进行求解

步骤 1 :分析并排序

由题意可知,nums是一个乱序的数组,并且最终的答案和数组中原本的顺序是无关的,所以这种题,第一反应必须是::排序。

因为数据顺序不影响答案,所以执行排序,这样重整之后,更方便我们选择合适的算法

步骤 2 :分析并使用双指针

1、我们观察:numsi + numsj + numsk = 0。切记一旦发现左侧是个常数C的时候,我们就要将之视作一个"空位",所以要将左式中的一个变量移动到空位这边,这种转换思路一定要会。其实所谓难题,就是基础模板之上添加了许多转换。

2、当问题转化成,找到合适的i、j、k,让numsj + numsk = -numsi,很明显,就是问题1,只不过我们需要在最外层加一个遍历i就可以了。

3、注意,题目中要求,不能出现重复答案,也就是说,当指针遇到重复元素的时候,我们一定要跳过,要不肯定会出现重复元素。

步骤 3 :优化

1、当numsi + numsi+1 + numsi+2 > 0 时候,这代表了有序数组最左侧最小的三个元素和都是大于0的,所以以后必不可能出现=0的情况,直接结束。

2、当numsi+ numsn-1 + numsn-2 < 0 时候,说明当前这个numsi不可能会存在答案,但是之后可能会有答案,所以我们跳过。

步骤 4 :实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> ans;
        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];
            if (i && x == nums[i - 1]) {//当遇到重复元素,这个nums[i]就是无效的,继续循环
                continue;
            }
            if (x + nums[i + 1] + nums[i + 2] > 0) {//优化
                break;
            }
            if (x + nums[n - 1] + nums[n - 2] < 0) {//优化
                continue;
            }
            int j = i + 1, k = n - 1;//开始双指针
            while (j < k) {
                if (x + nums[j] + nums[k] > 0) {
                    k--;
                }
                else if (x + nums[j] + nums[k] < 0) {
                    j++;
                }
                else {
                    ans.push_back({ x,nums[j],nums[k] });
                    //找到一个答案后,可能还存在多个,所以j++\k--,继续按照规则找,记得跳过重复元素
                    j++;
                    while (j < k && nums[j] == nums[j - 1]) {//跳过重复元素
                        j++;
                    }
                    k--;
                    while (k > j && nums[k] == nums[k + 1]) {//跳过重复元素
                        k--;
                    }
                }
            }
        }
        return ans;
    }
};
相关推荐
aqiu1111116 小时前
【算法刷题】蓝桥杯:怎么倒茶(贪心算法 + 结构体排序)
贪心·排序
OuO-26 小时前
笔试强训 Day 43:kotori 和抽卡(二)、ruby 和薯条、循环汉诺塔
数学·前缀和·二分查找·动态规划·排序·滑动窗口·循环汉诺塔
琥珀色糖1 天前
leetcode hot100题(持续更新)移动零(双指针)
算法·leetcode·职场和发展·双指针·移动零
旖旎夜光4 天前
LeetCode 202:快乐数(双指针问题) —— 题解
数据结构·c++·算法·leetcode·双指针
Tisfy5 天前
LeetCode 3731.找出缺失的元素:哈希 / 排序
算法·leetcode·哈希算法·排序·哈希表
旖旎夜光5 天前
LeetCode 11:盛最多水的容器(双指针问题) —— 题解
数据结构·c++·算法·leetcode·双指针
Tisfy14 天前
LeetCode 3016.输入单词需要的最少按键次数 II:排序(贪心)
linux·算法·leetcode·题解·贪心·排序·计数
Tisfy17 天前
LeetCode 3517.最小回文排列 I:排序(Python两行版) / 计数(O(n)时间+O(C)空间+字符串原地修改)
c语言·python·leetcode·字符串·排序·计数排序·回文
txzrxz19 天前
拓补排序讲解
c++·算法·图论·排序
良木林25 天前
滑动窗口 - LeetCode hot 100
javascript·算法·leetcode·双指针·滑动窗口