【数据结构】双指针算法:理论与实战

双指针(Two Pointers)是一种经典的算法思想,广泛应用于数组、链表等数据结构的处理。该方法通过设置两个指针,在某种规则下移动指针来实现高效的计算与查找。这种算法相比传统的嵌套循环能显著优化时间复杂度,通常能够达到 O(N) 的时间复杂度,从而大幅提升效率。

如果你还不了解什么是时间复杂度,可参考以下文章:

双指针算法的核心思想

双指针的基本思路是使用两个指针同时操作,以减少重复计算。两个指针可以根据具体情况分为同向指针和对向指针。

  1. 同向指针:两个指针从一端出发,彼此逐步靠近。例如,用于查找数组中满足条件的连续子数组。
  2. 对向指针:两个指针分别从两端出发,逐步相向移动,适用于查找满足条件的元素组合,如求数组中和为特定值的两个数。

通过双指针的移动,我们可以逐步缩小问题的范围,避免重复计算。它的应用场景广泛,通常用于解决有序数组的查找、链表的判环等问题。

算法步骤

  1. 初始化两个指针:通常从数组的两端出发(对向指针)或从数组的同一端出发(同向指针)。
  2. 根据问题的条件决定指针的移动方向。例如,若寻找数组中和为特定值的两数,若当前和大于目标值则移动右指针,反之则移动左指针。
  3. 在每一步迭代中,检查当前指针位置是否满足目标条件,若满足则记录结果或停止算法。
  4. 重复此过程,直到两个指针相遇或无法满足条件为止。

算法模板

以下是双指针算法的基本模板:

cpp 复制代码
int twoPointerExample(const vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            return {left, right};  // 找到目标
        } else if (sum < target) {
            left++;  // 增大和
        } else {
            right--; // 减小和
        }
    }
    return {};  // 无解
}

代码解析

  1. 初始化左右指针 :left 初始化为数组起始位置 0,right 初始化为数组末尾位置 nums.size() - 1。双指针从两端相向移动,逐步缩小搜索范围。

  2. 循环条件 :while (left < right) 保证了左指针位于右指针左侧。当左右指针重叠时,说明已遍历所有可能组合。

  3. 计算当前和并判断 :在每次循环中,计算当前指针位置的和 sum = nums[left] + nums[right],并判断是否等于目标值 target。若相等则直接返回索引,表示找到了满足条件的解。

  4. 调整指针位置 :若 sum < target,表示当前和小于目标值,需要增大和,因此将 left 右移。反之,若 sum > target,则将 right 左移以减小和。通过这种调整,算法在满足条件的前提下高效地缩小搜索范围。

  5. 无解返回 :若循环结束未找到解,返回空数组 return {};。这种处理方式可以避免出现异常情况并确保程序的稳定性。

注意事项

  1. 指针范围与数组有序性:该示例依赖于输入数组的有序性,以确保双指针能够正确缩小范围。若数组无序,则无法正确缩小搜索区间。
  2. 边界情况:若数组为空或仅有一个元素,则算法无需运行或会直接返回无解结果。
  3. 时间复杂度 :由于每次操作仅需移动 left 或 right 指针之一,整个过程的时间复杂度为 O(N),相较于暴力求解的 O(N^2) 更加高效。
  4. 空间复杂度:本算法无需额外空间,空间复杂度为 O(1)。

算法实战:盛最多水的容器

给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。要求找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

输入示例:

cpp 复制代码
height = [1,8,6,2,5,4,8,3,7]

输出:

cpp 复制代码
49

解释 :

在此情况下,最大水容量为 49,选择的两个端点分别在位置 1 和位置 8。

代码实现

cpp 复制代码
class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0, r = height.size() - 1, ans = 0;
        while (l < r) {
            ans = max(ans, (r - l) * min(height[r], height[l]));
            if (height[r] < height[l]) r--;
            else l++;
        }
        return ans; 
    }
};

代码解析

  1. l 和 r 初始化为左右两端的指针。
  2. ans 用于存储最大面积,初始为 0。
  3. while(l < r):当左右指针相遇时停止迭代。
  4. 每次迭代中更新 ans 为当前最大面积。
  5. 比较 height[l] 和 height[r],移动较短的一端的指针,直到 l >= r。

复杂度分析

  • 时间复杂度:O(N),因为只需要遍历数组一次。
  • 空间复杂度:O(1),除了输入外不需要额外的空间。

双指针的拓展

除了经典的求和问题,双指针还可以用于许多其他场景:

  • 链表的环检测:快慢指针用于判断链表是否存在环。
  • 分割数组:例如分割奇偶数组,通过同向双指针将奇数与偶数分开。
  • 字符串处理:用于判断回文串、删除字符串中的某些字符等问题。

注意事项

  1. 指针范围控制:使用双指针时需谨慎控制指针范围,以防止越界。
  2. 初始条件:在一些问题中需设定合适的初始条件,如链表判环问题中的初始 slow 和 fast 指针。

总结

双指针算法是一种简洁而高效的算法思路,广泛应用于数组和链表的处理。理解双指针的原理和不同应用场景,可以帮助我们在算法竞赛和日常编程中解决复杂问题。通过合理控制两个指针的移动,可以极大地降低时间复杂度,避免不必要的重复计算。

相关推荐
小羊没烦恼!6 分钟前
在Scrum中实施敏捷建模
java·开发语言·windows·算法·c#
weixin_3077791312 分钟前
基于睿擎工业开发平台的预训练视觉模型轻量化适配与低代码部署优化
开发语言·算法
hold?fish:palm1 小时前
45 二叉树的右视图
开发语言·c++·算法
粤鼎恒业1 小时前
惠州工厂电子料回收厂家推荐:从交接单反推筛选标准
大数据·数据库·算法·硬件架构·硬件工程·pcb工艺·材料工程
小爷毛毛(卓寿杰)2 小时前
【Agent 意图识别】输出协议、评估与置信度
人工智能·算法·大模型·prompt·大语言模型·agent
优质酸酸乳2 小时前
力扣100题之21合并两个有序链表
算法·leetcode·链表
旖旎夜光2 小时前
LeetCode 137 : 只出现一次的数字 II(位运算) —— 题解
c++·笔记·学习·算法·leetcode·力控
yexianglunbai2 小时前
Redis 详解:从核心数据结构到高可用架构实战
数据结构
旋生万物2 小时前
Agent System Prompt 注入螺旋公理:让 AI 推理不跑偏的可复制模板(附完整 Prompt)
人工智能·算法
深圳雨林凯AI2 小时前
图案裂变的空间关系原理:元素放大为何必然挤压负空间,以及三个可调变量|雨林凯AI技术拆解
人工智能·算法