双指针法(移动零 && 盛最多水的容器)

双指针比较灵活,可以大大降低时间复杂度,可用在数组,单链表等数据结构中。

在对数组使用双指针法时,通常使用数组下标充当指针。

题一. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

像这样进行数组划分,数组分块的题目可以用双指针法解决。

两个指针的作用:

  • right:从左到右扫描遍历数组。
  • left:已处理的区间中,非零元素的最后一个位置。

处理的方法:

right在向后遍历的过程中:

  • 遇到0元素,right++;
  • 遇到非0元素:left++ , 交换数组 left 与 right 这两个下标处的数据 , right++;

数组被分为三个区间

非0区 : [0,left] , 0区 : [left+ 1 , right - 1] , 未处理区 : [right , n - 1]

代码具体实现:

class Solution1 {
public:
    void moveZeroes1(vector<int>& nums) {//暴力直接交换
        int i = 0;

        for (int num = 0; num < nums.size(); num++) {
            if (i > 0 && nums[i - 1] == 0)
                i--;
            if (nums[i] == 0) {

                for (int j = i; j < nums.size() - 1; j++) {
                    swap(nums[j], nums[j + 1]);
                }
            }
            i++;
        }
    }

    void moveZeroes2(vector<int>& nums) {//双指针法
        int left = -1,right = 0;
        while(right<nums.size())
        {
            //if (nums[right] == 0) {
            //    right++;
            //}
            //else
            //{
            //    left++;
            //    swap(nums[right], nums[left]);
            //    right++;
            //}
            if (nums[right] != 0) {
                left++;
                swap(nums[right], nums[left]);
            }
            right++;
        }
    }
};

void Test01() {

    Solution1 s1;
    vector<int> v1{ 1,0,2,2,3,0,4,6,7 };
    s1.moveZeroes2(v1);

    for (auto e : v1) {
        cout << e << " ";
    }
    cout << endl;
}

题二. 盛最多水的容器

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

首先我们可以想到用两个循环,遍历求出每种情况下的的装水量,再取最大值。

但是这种方法时间复杂度高,所以我们可以用双指针法。

再解决这个题目前,我们需要了解:
假设这样一个容器(6 4 3 7 2),取最左边与最右边计算装水量,那么再次以较小一边固定,遍历其他的边,计算出的数据都会更小。
因为就算遍历到的这个数比原本左边与右边中较大的数更大,因为装水量取决于短板,并且宽度下降,总体装水量会更少。
遍历到的数更小,装水量当然会更少。

所以,我们在计算最大的装水量时,只需先取两边的下标 left 与 right ,计算这种情况下的装水量,再比较数组下标 left , right 的数的大小,在较小的那一端指针向中移。

class Solution {
public:
    int maxArea(vector<int>& height) {
        vector<int> nums;
        int left = 0, right = height.size() - 1;
        //6 4 3 7 2
        //假设这样一个容器,取最左边与最右边计算装水量,那么再次以较小一边固定,遍历其他的边,计算出的数据都会更小
        //因为就算遍历到的这个数比原本左边与右边中较大的数更大,因为装水量取决于短板,并且宽度下降,总体装水量会更少
        //遍历到的数更小,装水量当然会更少
        while (left < right) {

            if (height[left] < height[right]) {
                nums.push_back((right - left) * height[left]);
                left++;
            }
            else
            {
                nums.push_back((right - left) * height[right]);

                right--;
            }
        }

        int max = nums[0];
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] > max)
                max = nums[i];
        }

        return max;
    }
};

void Test_04() {
    Solution s1;
    vector<int> v1{6,4,3,7,2 };
    cout << s1.maxArea(v1) << endl;


}

(如有问题,烦请指出)

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值