【算法】双指针

目录

1、移动零

1.1 题目解析

1.2 算法原理讲解

1.3 代码实现

2、复写零 

2.1 解法一

2.2 解法二

3、快乐数

3.1 题目解析

3.2 算法原理讲解

3.3 代码实现

4、盛最多水的容器

​编辑 4.1 算法原理讲解

4.2 代码实现

5、有效三角形个数

5.1 代码原理讲解

5.2 代码实现

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

7、三数之和

8、四数之和


1、移动零

1.1 题目解析

可以发现,这道题的本质就是通过某一个标准,将数组划分成不同区间(数组划分、数组分块),此时可以用到双指针算法

1.2 算法原理讲解

1.3 代码实现

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

2、复写零 

2.1 解法一

利用顺序表,可以定义一个指针cur遍历数组的前n-1个元素,当cur指向的元素是0时,将cur后面所有元素都向后移动一位,在cur+1的位置赋值为0,当cur指向的元素是非0时,cur++

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        for(int cur = 0;cur<arr.size()-1;cur++)
        {
            if(arr[cur]==0)
            {
                int dest = arr.size()-2;
                while(dest>=cur)
                {
                    arr[dest+1]=arr[dest];
                    dest--;
                }
                cur++;
            }
        }
    }
};

2.2 解法二

操作数组中元素的题,一般也会用到双指针

若可以异地操作

可以发现,此时最后一个复制的元素是4

此时当cur指向的值是非0时,dest赋值为cur,并向前移动一位,当cur指向的值是0时,dest将前面两个位置都赋值为0

但问题是,如何找到cur开始的位置呢?

总结:1. 先找到最后一个需要复写的数(双指针算法)

                先判断cur的值,决定dest一次走几步(注意起始时cur=0,dest=-1)

                dest走完后,判断一下是否结束,若未结束,cur++

          2. 从后向前完成复写操作(双指针法)

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur = 0,dest = -1,n = arr.size();
        for(cur = 0;cur<=n;cur++)//找到最后一个需要复写的数
        {
            if(arr[cur])
            {
                dest++;
            }
            else
            {
                dest+=2;
            }
            if(dest>=n-1) break;
        }
        if(dest==n)//若最后一个需要复写的数是0
        {
            arr[n-1] = 0;
            dest-=2;
            cur--;
        }
        while(cur>=0)//从后向前复写数组
        {
            if(arr[cur])
            {
                arr[dest--] = arr[cur--];
            }
            else
            {
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    }
};

3、快乐数

3.1 题目解析

3.2 算法原理讲解

当是快乐数变成1后,再一直平方,任然会一直是1,所以可以将第一种情况也抽象成有环,这样子就可以定义快慢指针了(与链表中的快慢指针类似),因为两种情况都有环,所以快慢指针一定会相遇,只需要判断相遇时是否是1即可。

注意:这里的指针是用数来当指针

3.3 代码实现

class Solution {
public:
    int Square(int n)
    {
        int sum = 0;
        while(n)
        {
            int x = n%10;
            sum+=(x*x);
            n/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow = n,fast = n;
        do
        {
            slow = Square(slow);
            fast = Square(fast);
            fast = Square(fast);
        }while(slow!=fast);
        return slow==1;
    }
};

4、盛最多水的容器

 4.1 算法原理讲解

这道题是可以暴力枚举的,两层循环,但是这样子会超时

正确的做法是定义一个指针在最左端,一个指针在最右端,计算出当前的体积,然后让高度小的那个往里面走,再次计算体积,与先前的体积取大的,直到两指针相遇。

4.2 代码实现

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i = 0,j = height.size()-1,V = 0;
        while(i<j)
        {
            V = height[i]<height[j]?max(V,(j-i)*height[i++]):max(V,(j-i)*height[j--]);
        }
        return V;
    }
};

5、有效三角形个数

由题意可知,这道题是允许出现重复的

当我们判断一个数组中的3项能否组成一个三角形时,若这个数组无序,则需要判断3次,但若这个数组有序,则只需要判断较小的2项之和是否大于第3项即可,并且这样做的时间复杂度也会更低

以暴力枚举为例,若判断3次,则3*N^3,排序+判断一次,则N^3+N*logN

5.1 代码原理讲解

数组有序首先想二分或双指针

这样使用双指针算法能让时间复杂度从暴力枚举减小一个量级

5.2 代码实现

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int n = nums.size(),ans = 0;
        for(int c = nums.size()-1;c>=2;c--)
        {
            int a = 0,b = c-1;
            while(a < b)
            {
                if(nums[a]+nums[b]>nums[c])
                {
                    ans += b - a;
                    b--;
                }
                else
                {
                    a++;
                }
            }
        }
        return ans;
    }
};

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

此题暴力枚举也是会超时的,可以将数组排成有序,然后利用第5题中的方法解决

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        sort(price.begin(),price.end());
        int left = 0,right = price.size() - 1;
        while(left < right)
        {
            if(price[left] + price[right] > target)
                right--;
            else if(price[left] + price[right] < target)
                left++;
            else 
                break;
        }
        vector<int> v{price[left],price[right]};
        return v;
    }
};

7、三数之和

这道题是不能出现重复的,所以要注意去重

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> vv;
        if(nums.size() < 3) return vv;
        sort(nums.begin(),nums.end());
        for(int i = nums.size() - 1;i>=2;)
        {
            int left = 0,right = i - 1;
            while(left < right)
            {
                if(nums[left] + nums[right] + nums[i] > 0)
                    right--;
                else if(nums[left] + nums[right] + nums[i] < 0)
                    left++;
                else
                {
                    vv.push_back({nums[left],nums[right],nums[i]});// 匿名对象
                    // 更新left和right,要去重
                    int flag1 = nums[left++],flag2 = nums[right--];
                    while(left < right && nums[left] == flag1) left++;
                    while(left < right && nums[right] == flag2) right--;
                }
            }
            // 更新i,要去重
            int flag = nums[i--];
            while(i >= 2 && nums[i] == flag) i--;
        }
        return vv;
    }
};

8、四数之和

这道题是不能出现重复的,所以要注意去重

注意,这道题的数据范围较大,四个数相加会发生越界

此时超过了int的范围,前两个相加是没问题的,当第3个加上去的时候就出错了,此时可以让他们类型转换成long long,这样会发生整型提升

只要不是转换最后面的那个就可以,因为 + 是从左向右操作的

class Solution {
public:
    typedef long long ll;
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> vv;
        if(nums.size() < 3) return vv;
        sort(nums.begin(),nums.end());
        for(int i = nums.size() - 1;i>=3;)
        {
            for(int j = i - 1;j>=2;)
            {
                int left = 0,right = j - 1;
                while(left < right)
                {
                    if((ll)nums[left] + (ll)nums[right] + nums[j] + nums[i] > target)
                        right--;
                    else if((ll)nums[left] + (ll)nums[right] + nums[j] + nums[i] < target)
                        left++;
                    else
                    {
                        vv.push_back({nums[left],nums[right],nums[j],nums[i]});
                        int flag1 = nums[left++] , flag2 = nums[right--];
                        while(left < right && nums[left] == flag1) left++;
                        while(left < right && nums[right] == flag2) right--;
                    }
                }
                int flag = nums[j];
                while(j >= 2 && nums[j] == flag) j--;
            }
            int flag = nums[i];
            while(i >= 3 && nums[i] == flag) i--;
        }
        return vv;
    }
};

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值