LeetCode 热题100之双指针

1.移动零

在这里插入图片描述
思路分析1(纯模拟)

  • 定义指针j,用来收集不是0的数;
  • 收集完毕之后,再把剩下位置处置为0即可。

具体实现代码(详解版):

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size(); // 获取数组的大小
        int j = 0; // j 用于记录非零元素的位置

        // 第一次遍历,移动非零元素
        for (int i = 0; i < n; i++) {
            if (nums[i] != 0) {
                nums[j++] = nums[i]; // 将非零元素放到前面,并递增 j
            }
        }

        // 第二次遍历,将剩余的位置填充为零
        for (int i = j; i < n; i++) {
            nums[i] = 0; // 将 j 之后的所有位置设置为零
        }
    }
};

思路分析2(交换0)

  • 首先记录第一个元素为0的位置;
  • 定义双指针l和r,r一直往后走,l只有当交换元素的时候才前进
    在这里插入图片描述
    具体实现代码(详解版):
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int l = 0; 
        for (int r = 0; r < nums.size(); r ++) {
            if (nums[r] == 0) continue; // 如果当前元素是零,跳过
            swap(nums[l ++], nums[r]); // 交换非零元素到前面
        }
    }
};

以上两种解法的时间复杂度都是 O ( n ) O(n) O(n),n是数组中元素的个数。

2.盛最多水的容器

在这里插入图片描述
思路分析1(暴力解法超时)

  • 外层循环遍历所有柱子的索引 i。
  • 内层循环从 i 开始遍历,确保每一对柱子只计算一次(即避免重复)
  • 计算面积
    • 对于每对柱子 i 和 j,计算它们之间的高度 t,取两者的最小值。
    • 计算这两柱之间的面积 t * (j - i),并将其与 res 进行比较,更新最大面积。

具体实现代码(详解版):

class Solution {
public:
    int maxArea(vector<int>& height) {
        int res = 0; // 用于存储最大面积
        // 外层循环遍历第一个柱子
        for (int i = 0; i < height.size(); i++) {
            // 内层循环遍历第二个柱子
            for (int j = i; j < height.size(); j++) {
                int t = min(height[i], height[j]); // 计算当前两柱之间的高度
                res = max(res, t * (j - i)); // 计算面积并更新最大面积
            }
        }
        return res; // 返回最大面积
    }
};

时间复杂度为 O ( n 2 ) O(n^2) O(n2),不能解决本题。还是要用双指针进行优化。
思路分析2(双指针算法)

  • 初始化指针:设置两个指针,分别指向数组的两端,一个指向左边(left),一个指向右边(right)。
  • 计算面积:在每一步中,计算当前两指针之间的水容器的面积,使用 min(height[left], height[right]) 作为容器的高度,宽度为 right - left。
  • 移动指针:为了找到可能更大的面积,移动指向较矮柱子的指针。因为容器的面积是由较矮的柱子限制的,移动较矮的柱子有可能找到更高的柱子,从而增加面积。
  • 重复步骤:继续移动指针并计算面积,直到两个指针相遇。
在这里插入代码片

具体实现代码(详解版):

class Solution {
public:
    int maxArea(vector<int>& height) {
        int left = 0; // 左指针
        int right = height.size() - 1; // 右指针
        int maxArea = 0; // 最大面积

        while (left < right) {
            // 计算当前面积
            int currentArea = min(height[left], height[right]) * (right - left);
            maxArea = max(maxArea, currentArea); // 更新最大面积

            // 移动指针
            if (height[left] < height[right]) {
                left++; // 移动左指针
            } else {
                right--; // 移动右指针
            }
        }

        return maxArea; // 返回最大面积
    }
};

3.三数之和

在这里插入图片描述
思路分析(排序+双指针)

  • 排序:首先对数组进行排序,这样可以方便地处理重复元素和使用双指针法;
  • 遍历:使用一个循环遍历每个元素,作为三元组中的第一个元素;
  • 跳过重复:在遍历过程中,如果当前元素与前一个元素相同,则跳过,以避免生成重复的三元组。
  • 双指针法:对于每个固定的第一个元素,使用两个指针(left 和 right)分别从当前元素的下一个位置和数组的末尾开始向中间移动,寻找另外两个元素,使得三者的和为零。
  • 更新指针
    • 如果三者的和小于零,移动左指针向右,增大和。
    • 如果三者的和大于零,移动右指针向左,减小和。
    • 如果三者的和等于零,将三元组加入结果,并移动两个指针,同时跳过重复元素。
  • 返回结果

具体实现代码(详解版):

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        for(int i = 0 ; i < nums.size() ; i ++){
            //跳过重复的元素
            if(i > 0 && nums[i] == nums [i - 1]) continue;
            int l = i + 1 ,r = nums.size() - 1;

            while(l < r){
                int sum = nums[i] + nums[l] + nums[r];
                
                if(sum < 0) l ++;//和小于0,左指针右移
                else if(sum > 0) r --;//和大于0,右指针左移
                else{
                    //找到一个三元组
                    res.push_back({nums[i],nums[l],nums[r]});
                    //跳过重复的元素
                    while(l < r && nums[l] == nums[l + 1]) l ++;
                    while(l < r && nums[r] == nums[r - 1]) r --;
                    //移动指针继续查找
                    l ++;
                    r --;
                }
            }
        }
        return res;
    }
};

4.接雨水

在这里插入图片描述
思路分析(双指针算法):

  • 初始化指针和变量:
    • 使用两个指针 l 和 r,分别指向数组的两端。
    • 初始化 lmax和 rmax,用于存储当前左右两边的最大高度。
    • 初始化一个变量 res 来记录接雨水的总量。
  • 遍历数组(高度图)
    • 使用 while 循环,当 l 小于 r 时继续执行。
    • 比较 height[l]和 height[r]:
      • 如果 height[l]小于 height[r]:
        • 说明在 left 指向的位置,水的高度受到 lmax 的限制
        • 如果 height[l] 小于 lmax,则可以计算在该位置接的雨水量,并更新 res = lmax - height[l]。
        • 否则,更新 lmax 为 height[l],然后向右移动 l 指针。
      • 如果 height[l]大于 height[r]:
        • 类似地,处理 r 指向的位置,计算接雨水量或更新 rmax,然后向左移动 r指针。
  • 返回结果。

具体实现代码(详解版):

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.empty()) return 0;//如果数组为空,返回0

        int l = 0, r = height.size() - 1;//双指针,分别指向数组的两端
        int lmax =  0, rmax = 0;//记录左侧和右侧的最大柱子高度
        int res = 0;

        while(l < r){
            if(height[l] <= height[r]){
                if(height[l] >= lmax) lmax = height[l];//更新左侧最大柱子高度
                else res += lmax - height[l];//计算雨水量
                l ++;//指针右移
            }
            else{
                if(height[r] >= rmax) rmax = height[r];//更新右侧最大柱子高度
                else res += rmax -height[r];//计算雨水量
                r --;//指针左移
            }
        }
        return res;

    }
};

另外一种双指针的写法:

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.empty()) return 0;

        int res = 0;
        int l = 0,r = height.size() - 1;
        int lmax = 0,rmax = 0;

        while(l <= r){
            lmax = max(lmax,height[l]);
            rmax = max(rmax,height[r]);
            if(lmax < rmax){
                res += lmax - height[l];
                l ++;
            }
            else{
                res += rmax - height[r];
                r --;
            }
        }   
        return res;
    }
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值