双指针(7题)

目录

1.移动0

2.复写0

3.快乐数

4.盛水最多的容器

5.有效三角形的个数

6.三数之和

7.四数之和


1.移动0

. - 力扣(LeetCode)

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int cur = 0;
        int des = 0;
        int size = nums.size();
        while(cur < size)
        {
            if(nums[cur] == 0)cur++;
            else
            {
                swap(nums[cur],nums[des]);
                cur++;
                des++;
            }
        }
    }
};

         cur 用于遍历数组

        des 中储存的下标指向 已经遍历过区域中  从左到右第一个0.

        当nums[cur]为0时,cur++,des不变。

        不为0时,先交换des和cur所指向的数组中的值,然后再让cur++,des++。

        使得des储存的下标仍然指向 已经遍历过区域中  从左到右第一个0.

2.复写0

. - 力扣(LeetCode)

class Solution {
public:
    void duplicateZeros(vector<int>& nums) {
        int end = nums.size()-1;
        int startpos;
        int endpos;
        for(endpos = -1,startpos = 0;endpos < end;)
        {
            if(nums[startpos] == 0)
            {
                startpos++;
                endpos = endpos + 2;
            }
            else
            {
                startpos++;
                endpos++;
            }
        }
        startpos--;
        if(endpos != end)
        {
            startpos--;
            nums[--endpos] = 0;
            --endpos;
        }
        {
             while(startpos >=0)
            {
                if(nums[startpos] == 0)
                {
                    nums[endpos] = nums[endpos-1] = nums[startpos];
                    endpos = endpos - 2;
                    startpos--;
                }
                else
                {
                    nums[endpos] = nums[startpos];
                    endpos--;
                    startpos--;
                }
            }
        }
    }
};

        因为从左往右赋值会出现覆盖,因此我们选择从右向左赋值。

        先进行一次遍历得到从右往左开始遍历位置的下标startpos。

        变量endpos与数组最后一位的下标end比较,用于判断是否填满了数组。

        因为开始的时候startpos为0,endpos为-1,因此遍历结束时,startpos指向的是真正开始遍历的位置的下标+1.因此startpos--;

        因为有可能出现最后一位的值为0,但是所剩余的空格只有1,endpos+2后会出现越界,因此我们要进行判断,如果越界那么我们先进行处理。

        最后从右往左非0赋值一位,0赋值两位即可

3.快乐数

. - 力扣(LeetCode)

class Solution {
public:
    int happy(int n)
    {
        int ret = 0;
        while(n)
        {
            ret += (n%10) *(n%10);
            n /= 10;
        }
        return ret;
    }
    bool isHappy(int n) {
        int slow = happy(n);
        int fast = happy(happy(n));
        while(slow != fast)
        {
            slow = happy(slow);
            fast = happy(happy(fast));
        }
        if (slow == 1)return true;
        else return false;
    }
};

        原理类似判断链表是否有环,

        但是这个是一定有环,第一种情况是交点处不为1,一种是交点处为1,

        利用快慢指针寻找到交点处的值即可

4.盛水最多的容器

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

         3 2 9  5 7

        即选取两个位置,因为木桶效应,以较小的那个值为高,两者间的距离为宽。 

        情况有 3 72 79 7 , 5 7

                    3 52 59 5

                    3 9 2 9

                    3 2

        我们一开始选取最左边和最右边的板子,下标记为left和right

        一开始是一个宽 w为5的木桶。高h为3.

        我们现在要将木桶的宽缩小,如果我们去掉更高的那个板子7。

        以3为一边的木桶,剩余的情况为 3 2 , 3 9 , 3 5

        当另一边的板高大于等于3 时,h不变,w减小,总体体积减小

        当另一边的板高小于3时,h减小,w减小,总体体积减小。

        因此以3为一边板的情况下,3  7为最大情况

        因此我们只有把短板3去掉,才有可能出现一个比3大的板子,使h增大,才可能在w减小的情况下使得总体积增大

        因此我们每次都将两板中较短的板去掉,才有可能得到一个更大的体积。

        将每次取得的体积进行比较,取出最大值即可

        我们可以在图中看到,这个过程就是以一个值为起点,向后或者向下进行比较的结果,(最大的组合加粗)但是它只能保证这次比较的结果中它是最大,所以还需要和它之前的小组中最大值值进行比较

5.有效三角形的个数

. - 力扣(LeetCode)

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

如下一个用例

4 6 5 4 3 2 

如果我们使用暴力解法

for(int i = 0; i < size; i++)

        for(int j = i + 1; j < size; j++)

                for(int k = j+1; k < size ;k++)

                              判断函数(i, j,k) 

判断函数: i + j > k    i + k > j , j + k > i 判断三次

所以总的时间复杂度为3N^3

所以我们对它进行优化。

首先我们先进行排序,使得我们只需要比较较小的两个数相加是否大于最大数,只需要判断一次

同时我们再对算法进行优化

 2 3 4 4 5 6 

我们先取6为最大数,然后取left ->,right-> 5 为两个较小数

当left ->2 + right -> 5大于最大数 6时

因为我们是按照大小排序的left为左端最小值,因此left右的任意一个数 + right ->都大于最大数

 2 + 5  > 6

 3 + 5 > 6

 4 + 5 > 6

 4 + 5 > 6

因此我们增加的情况即 right - left

此时以5为一个较小数的情况被遍历,right左移

此时

left->2 right->4 ,等于最大数6

此时left 与 right 左端的任意一个值相加都小于等于 最大数

2+4  <= 6

2+4 <= 6

2+3  <= 6

此时以2 为一个较小数的情况被遍历,left右移

增加的情况为0.

当left == right 时结束循环。

以5为最大数开始遍历

此方法的时间复杂度仅为N^2

6.三数之和

. - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
          sort(nums.begin(),nums.end());
          vector<vector<int>> ret;
          int size = nums.size();
          for(int i = 0; i < size - 2; )
          {
            int aim = -nums[i];
            if(aim < 0)break;//一个小优化,当nums[i]>0,就找不到两数之和等于它的相反数了
            int left =  i + 1;
            int right = size - 1;
            while(left < right)
            {
                if(nums[left] + nums[right] > aim)right--;
                else if (nums[left] + nums[right] < aim) left++;
                else
                {
                    ret.push_back({nums[i],nums[left],nums[right]});
                    left++;
                    right--;
                    while(left < right&& nums[left] == nums[left-1])
                    {
                       left++;
                    }
                    while(right> left &&   nums[right] == nums[right+1])
                    {
                      right--;
                    }
                }
            }
            i++;
            while(i < size - 2 && nums[i] == nums[i-1])i++;
          }
          return ret;
    }
};

        我们仍然是先进行排序

        用例[-1,0,1,2,-1,-4]

                -4 -1 -1 0 1 2 

        我们先固定-4,从左到右遍历到0处即可。

        再定义left和right变量,来指向右侧数组最左侧和最右侧的数。

        当nums[left] + nums[right] > aim 时,说明太大,right--

           nums[left] + nums[right]  < aim 时,说明太小,left--

        相等时,将三个数存入二维数组即可。

        同时我们要去重,如果left指向的数与上一个位置相同,那么left继续加

        right同理,一直减

        并且要注意范围,left < right ,不能越界

        处理过一个i之后,i向右移动,也要去重,原理同上。

7.四数之和

. - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> ans;
        int n=nums.size();
        sort(nums.begin(),nums.end());
        for(int first=0;first<n-3;first++){
            if(first>0&&nums[first]==nums[first-1])continue;
            
            if(first>=n-3)break;
            for(int second=first+1;second<n-2;second++){
                if(second>=n-2)break;
                int fourth = n -1;
                for(int third=second+1;third<n-1;third++){
                    if(third>=fourth)break;
                    double all=0.0+nums[first]+nums[second]+nums[third]+nums[fourth];
                    if(all==target){
                        bool test=true;
                        for(int i=0;i<ans.size();i++){
                            vector<int> test1={nums[first],nums[second],nums[third],nums[fourth]};
                            if(ans[i]==test1)test=false;
                        }
                        if(test)ans.push_back({nums[first],nums[second],nums[third],nums[fourth]});
                    }else if(all>target){
                        fourth--;
                        third--;
                    }else if(all<target){
                        
                        continue;
                    }
                }
            }
        }
        return ans;
    }
};

        原理同上,只需要在三数之和的基础上,再加一层循环。

        三数之和等于 target- 第一个固定的数

        因此两数之和等于target- 第一个固定的数-第二个固定的数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值