【算法题】双指针(二)

一、盛最多水的容器

题目描述:

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

说明:不能倾斜容器。

示例:

  • 输入:height = [1,8,6,2,5,4,8,3,7],输出:49
  • 输入:height = [1,1],输出:1

解题思路:

采用双指针法优化暴力枚举的时间复杂度:

  • 初始化左右指针 left(数组开头)、right(数组末尾),记录最大面积 ret。
  • 容器面积由「短边高度」和「指针间距」决定:面积 = min(height[left], height[right]) * (right - left)。
  • 移动策略:每次移动较短边的指针(若移动长边,间距减小且短边不变/更小,面积必然减小;移动短边可能找到更长的边,从而增大面积)。
  • 遍历过程中持续更新最大面积,直到左右指针相遇。

完整代码:

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

复杂度分析:

  • 时间复杂度 :O(n)O(n)O(n)。仅遍历数组一次,左右指针最多移动 nnn 次。
  • 空间复杂度 :O(1)O(1)O(1)。仅使用常数级额外变量。

二、有效三角形的个数

题目描述:

给定一个包含非负整数的数组 nums,返回其中可以组成三角形三条边的三元组个数。

三角形条件:任意两边之和大于第三边。

示例:

  • 输入:nums = [2,2,3,4],输出:3(有效组合:[2,3,4](两个2分别组合)、[2,2,3])
  • 输入:nums = [4,2,3,4],输出:4

解题思路:

先排序数组,利用三角形的简化性质(排序后,若较小两边之和大于最大边,则必然满足三角形条件):

  1. 排序数组,使 nums[a] ≤ nums[b] ≤ nums[c],只需判断 nums[a] + nums[b] > nums[c]。
  2. 固定最大边 nums[i](从数组末尾往前遍历),用双指针 left=0、right=i-1 找符合条件的较小两边:
    • 若 nums[left] + nums[right] > nums[i],则 right-left 个组合(left 到 right-1 都满足),right 左移。
    • 否则 left 右移,尝试更大的较小边。

完整代码:

cpp 复制代码
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int ret = 0;
        for(int i = nums.size() - 1; i >= 2; i--){
            int left = 0, right = i - 1;
            while(left < right){
                if(nums[left] + nums[right] > nums[i]){
                    ret += right - left;
                    right--;
                }
                else{
                    left++;
                }
            }
        }
        return ret;
    }
};

复杂度分析:

  • 时间复杂度 :O(n2)O(n^2)O(n2)。排序占 O(nlog⁡n)O(n\log n)O(nlogn),固定最大边的循环+双指针遍历占 O(n2)O(n^2)O(n2),总复杂度由后者主导。
  • 空间复杂度 :O(log⁡n)O(\log n)O(logn)。仅排序所需的递归栈/临时空间。

三、查找总价格为目标值的两个商品

题目描述:

购物车内的商品价格按升序记录于数组 price,请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。

示例:

  • 输入:price = [3, 9, 12, 15], target = 18,输出:[3,15] 或 [15,3]
  • 输入:price = [8, 21, 27, 34, 52, 66], target = 61,输出:[27,34] 或 [34,27]

解题思路:

利用数组升序排列的特性,采用双指针法:

  • 初始化 left=0(数组开头)、right=price.size()-1(数组末尾)。
  • 计算当前和 price[left] + price[right]:
    • 若和小于 target,left 右移(需要更大的数)。
    • 若和大于 target,right 左移(需要更小的数)。
    • 若和等于 target,直接返回这两个数。

完整代码:

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        int left = 0, right = price.size() - 1;
        while(left < right){
            if(price[left] + price[right] < target) left++;
            else if(price[left] + price[right] == target) return {price[left], price[right]};
            else right--;
        }
        return {-1, -1};
    }
};

复杂度分析:

  • 时间复杂度 :O(n)O(n)O(n)。仅遍历数组一次,双指针最多移动 nnn 次。
  • 空间复杂度 :O(1)O(1)O(1)。仅使用常数级额外变量。

四、三数之和

题目描述:

给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k,同时满足 nums[i] + nums[j] + nums[k] == 0。请返回所有和为 0 且不重复的三元组(答案中不可包含重复的三元组)。

示例:

  • 输入:nums = [-1,0,1,2,-1,-4],输出:[[-1,-1,2],[-1,0,1]]
  • 输入:nums = [0,1,1],输出:[]

解题思路:

先排序数组,结合固定单元素+双指针的策略,同时处理去重:

  1. 排序数组,便于去重和双指针查找。
  2. 固定第一个元素 nums[i]:
    • 若 nums[i] > 0,直接break(排序后后续数更大,和不可能为0)。
    • 若 nums[i] 与前一个数重复,跳过(避免重复三元组)。
  3. 用双指针 left=i+1、right=nums.size()-1 找 nums[left] + nums[right] = -nums[i]:
    • 找到符合条件的三元组后,left、right 分别跳过重复元素。
    • 否则根据和的大小移动指针。

完整代码:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ret;
        sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); ) {
            if(nums[i] > 0) break;
            int left = i + 1, right = nums.size() - 1, target = -nums[i];
            while(left < right) {
                if(nums[left] + nums[right] < target) left++;
                else if (nums[left] + nums[right] > target) right--;
                else {
                    ret.push_back({nums[i], nums[left++], nums[right--]});
                    while(left < right && nums[left] == nums[left - 1]) left++;
                    while(left < right && nums[right] == nums[right + 1]) right--;
                }
            }
            i++;
            while(i < nums.size() && nums[i] == nums[i - 1]) i++;
        }
        return ret;
    }
};

复杂度分析:

  • 时间复杂度 :O(n2)O(n^2)O(n2)。排序占 O(nlog⁡n)O(n\log n)O(nlogn),固定元素+双指针遍历占 O(n2)O(n^2)O(n2)。
  • 空间复杂度 :O(log⁡n)O(\log n)O(logn)(排序所需空间),存储结果的空间不计入额外复杂度。

五、四数之和

题目描述:

给你一个由 n 个整数组成的数组 nums 和一个目标值 target,请找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]:

  • 0≤a,b,c,d<n0 ≤ a, b, c, d < n0≤a,b,c,d<n 且互不相同
  • numsa+numsb+numsc+numsd==targetnumsa + numsb + numsc + numsd == targetnumsa+numsb+numsc+numsd==target
    答案可以按任意顺序返回。

示例:

  • 输入:nums = [1,0,-1,0,-2,2], target = 0,输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
  • 输入:nums = [2,2,2,2,2], target = 8,输出:[[2,2,2,2]]

解题思路:

在三数之和的基础上扩展为固定双元素+双指针,同时注意数值溢出问题:

  1. 排序数组,便于去重和双指针查找。
  2. 固定前两个元素 nums[i]、nums[j]:
    • 若 nums[i] 与前一个数重复,跳过;若 nums[j] 与前一个数(且 j > i+1)重复,跳过。
  3. 用双指针 left=j+1、right=nums.size()-1 找四数之和等于 target:
    • 计算和时用 long long 避免整数溢出。
    • 找到符合条件的四元组后,left、right 分别跳过重复元素。
    • 否则根据和的大小移动指针。

完整代码:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        int n=nums.size();
        vector<vector<int>> ret;
        for(int i=0;i<n-3;)
        {
            for(int j=i+1;j<n-2;)
            {
                int left=j+1,right=n-1;
                while(left<right)
                {  
                    long long sum=(long)nums[i]+nums[j]+nums[left]+nums[right];
                    if(sum>target)
                        right--;
                    else if(sum<target)
                        left++;
                    else
                    {
                        ret.push_back({nums[i],nums[j],nums[left],nums[right]});
                        left++;right--;
                        while(left<right&&nums[left]==nums[left-1]) left++;
                        while(left<right&&nums[right]==nums[right+1]) right--;
                    }
                }
                ++j;
                while(j<n&&nums[j]==nums[j-1]) j++;
            }
            ++i;
            while(i<n&&nums[i]==nums[i-1]) i++;
        }
        return ret;
    }
};

复杂度分析:

  • 时间复杂度 :O(n3)O(n^3)O(n3)。排序占 O(nlog⁡n)O(n\log n)O(nlogn),两层固定循环+双指针遍历占 O(n3)O(n^3)O(n3)。
  • 空间复杂度 :O(log⁡n)O(\log n)O(logn)(排序所需空间),存储结果的空间不计入额外复杂度。
相关推荐
zyl76514 小时前
FlashAttention 详解:从 GPU 硬件、普通 Attention 到分块计算与 Online Softmax
算法
爱读书的小胖18 小时前
LeetCode 122:买卖股票的最佳时机 II(C)解题分析
算法
结局无敌18 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
OPEN-F18 小时前
ROS2系列教程:上位机建图与导航(原理综合)
算法·机器人
weixin_3077791318 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
tomwang032219 小时前
路径优化建模、优化算法分类及工程化小结
算法·数学建模·分类
小羊没烦恼!19 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
by2099919 小时前
哈希表——快速找到数据:冲突、删除与扩容的实现
数据结构·c++·哈希算法·散列表
2501_9269783319 小时前
人机通信协议--人和机器之间的交互想象
人工智能·经验分享·算法·ai写作
行者全栈架构师19 小时前
【鸿蒙心迹】互动卡片全链路——把核心功能放到桌面(FormKit 实战)(HarmonyOS 7.x)
前端·算法·架构