优选算法专题1:双指针

双指针

目录

双指针

试题1:移动零

算法原理

代码编写

试题2:复写零

算法原理

代码编写

试题3:快乐数

算法原理

编写代码

试题4:盛最多水的容器

算法原理

编写代码

试题5:有效三角形的个数

算法原理

代码编写

试题6:和为s的两个数

算法原理

代码编写

试题7:三数之和

算法原理

代码编写

试题8:四数之和

算法原理

代码编写


试题1:移动零

算法原理

数组划分(数组分块)

提供一个数组,给定一个规则,在这个规则下将数组划分为若干个区间

使用双指针算法(注:这里的指针是数组的下标)

两个指针的作用

**cur(current):**从左往右扫描数组,遍历数组

**dest(destination):**已处理的区间内,非零元素的最后一个位置

三个区间

0,dest:非0

dest + 1,cur - 1:0

cur,n - 1:待处理

cur从前往后遍历的过程中

遇到0元素:cur++

遇到非0元素:dest++,swap(dest,cur),cur++

**补充:**双指针算法(数据划分)是快排里面最核心的一步

代码编写

  • 个人版本
cpp 复制代码
class Solution {
public:
    void moveZeroes(vector<int>& nums) 
    {
        int dest = -1;
        int cur = 0;
        while(cur != nums.size())
        {
            if(nums[cur] == 0)
            {
                cur++;
            }
            else
            {
                dest++;
                swap(nums[cur],nums[dest]);
                cur++;
            }
        }
    }
};
  • 标准版本
cpp 复制代码
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:复写零

算法原理

双指针算法

根据异地操作,优化成双指针下的就地操作

异地操作

就地操作

Step1:先找到最后一个复写的数

使用双指针算法,先判断cur位置的值,决定dest向后移动一步或者两步

再判断dest是否已经走到结束为止,最后cur++,直到cur遍历整个数组

Step1.5:处理边界情况

将n-1下标的元素改为0,cur -= 1,dest -= 2

Step2:从后向前完成复写操作

总结:画图模拟过程,多次尝试,将大题化为多个小题

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    void duplicateZeros(vector<int>& arr) 
    {
        int cur = 0;
        int dest = -1;
        while(cur < arr.size())
        {
            if(arr[cur] == 0)
            {
                dest += 2;
            }
            else
            {
                dest++;
            }
            if(dest >= arr.size() - 1)
            {
                break;
            }
            cur++;
        }
        if(dest == arr.size())
        {
            dest -= 2;
            cur--;
            arr[arr.size() - 1] = 0;
        }
        while(cur >= 0)
        {
            if(arr[cur] == 0)
            {
                arr[dest] = 0;
                arr[dest - 1] = 0;
                dest -= 2;
            }
            else
            {
                arr[dest] = arr[cur];
                dest--;
            }
            cur--;
        }
    } 
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    void duplicateZeros(vector<int>& arr) 
    {
        //#1:先找到最后一个数
        int cur = 0,dest = -1,n = arr.size();
        while(cur < n)
        {
            if(arr[cur])
            {
                dest++;
            }
            else
            {
                dest += 2;
            }
            if(dest >= n - 1)
            {
                break;
            }
            cur++;
        }
        //#2:处理边界情况
        if(dest == n)
        {
            arr[n - 1] = 0;
            cur--;
            dest -= 2;
        } 
        //#3:从后向前完成复写操作
        while(cur >= 0)
        {
            if(arr[cur])
            {
                arr[dest--] = arr[cur--];
            }
            else
            {
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    } 
};

试题3:快乐数

算法原理

快慢双指针

定义快慢指针

慢指针每次向后移动一步

快指针每次向后移动两步

判断相遇时候的值即可

**补充:**鸽巢原理(抽屉原理)

n个巢穴,n+1个鸽子:至少有一个巢穴里的鸽子数大于1

2.1 * 10^9 -> 1,810(巢穴)

9999999999 -> 9^2 * 10 = 810

随机数x经过811次操作,必定会有一个值落在1,810,所以成环是必定的

编写代码

  • 个人版本
cpp 复制代码
class Solution {
public:

    int happydata(int n)
    {
        int sum = 0;
        while(n)
        {
            int x = n % 10;
            sum += x * x;
            n = n / 10;
        }
        return sum;
    }

    bool isHappy(int n) 
    {
        int slow = n;
        int fast = n;
        do
        {
            slow = happydata(slow);
            fast = happydata(happydata(fast));
        }while(slow != fast);

        if(slow == 1)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    int bitSum(int n)
    {
        int sum = 0;
        while(n)
        {
            int t = n % 10;
            sum += t * t;
            n /= 10;
        }
        return sum;
    }

    bool isHappy(int n)
    {
        int slow = n,fast = bitSum(n);
        while(slow != fast)
        {
            slow = bitSum(slow);
            fast = bitSum(bitSum(fast));
        }
        return slow == 1;
    }
};

试题4:盛最多水的容器

算法原理

解法一:暴力枚举

两层for循环(超时)

解法二:双指针法

编写代码

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int maxArea(vector<int>& height) 
    {
        int maxv = 0;
        int left = 0;
        int right = height.size() - 1;
        while(left < right)
        {
            int v;
            if(height[left] < height[right])
            {
                v = (right - left) * height[left];
                if(v > maxv)
                {
                    maxv = v;
                }
                left++;
            }
            else
            {
                v = (right - left) * height[right];
                if(v > maxv)
                {
                    maxv = v;
                }
                right--;
            }
        }
        return maxv;
    }
};
  • 标准版本
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(ret,v);
            if(height[left] < height[right])
            {
                left++;
            }
            else
            {
                right--;
            }
       }
       return ret;
    }
};

试题5:有效三角形的个数

算法原理

解法一:暴力枚举

时间复杂度:O(N^3)

直接枚举:3 * N^3

排序后枚举:N * logN + N^3

cpp 复制代码
for(i = 0;i < n;i++)
{
    for(j = i + 1;j < n;j++)
    {
        for(k = j + 1;k < n;k++)
        {
            check(i,j,k);
        }
    }
}

解法二:利用单调性,使用双指针算法

先固定最大的数O(N)

在最大的数的左区间内,使用双指针算法O(N)

快速统计出符合要求的三元组的个数

时间复杂度:O(N^2)

**补充:**给定三个数,判断是否能够构成三角形

已知三个数的大小顺序,判断较小的两个数之和大于第三个数,就能构成三角形

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    int triangleNumber(vector<int>& nums) 
    {
        int count = 0;
        sort(nums.begin(),nums.end());
        for(int i = nums.size() - 1;i >= 2;i--)
        {
            int left = 0;
            int right = i - 1;
            int c = nums[i];
            while(left < right)
            {
                if(nums[left] + nums[right] > c)
                {
                    count += right - left;
                    right--;
                }
                else
                {
                    left++;
                }
            }
        }
        return count;
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    int triangleNumber(vector<int>& nums) 
    {
        //#1:优化
        sort(nums.begin(),nums.end());

        //#2:利用双指针解决问题
        int ret = 0;
        int n = nums.size();
        for(int i = n - 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;
    }
};

试题6:和为s的两个数

算法原理

解法一:暴力枚举

cpp 复制代码
for(i = 0;i < n;i++)
{
    for(j = i + i;j < n;j++)
    {
        check(num[i] + nums[j] == t);
    }
}

解法二:利用单调性,使用双指针算法解决问题

代码编写

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

试题7:三数之和

算法原理

解法一:暴力枚举

排序+枚举+利用unordered_set去重

时间复杂度:O(N^3)

解法二:排序+双指针(有序数组)

时间复杂度:O(N^2)

排序,固定一个数为a(a <= 0)

在该数后面的区间内,利用双指针算法

快速找到两个数的和为-a即可

处理细节问题:

去重

找到一种结果之后,left和right指针需要跳过重复元素

当使用完一次双指针算法之后,i也需要跳过重复元素

避免越界

不漏

找到一种结果之后,不要停,缩小区间,继续寻找

代码编写

  • 个人版本
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();i++)
       {
            if(nums[i] > 0)
            {
                break;
            }
            if(i > 0 && nums[i] == nums[i - 1])
            {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while(left < right)
            {
                if(nums[left] + nums[right] < -nums[i])
                {
                    left++;
                }
                else if(nums[left] + nums[right] > -nums[i])
                {
                    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--;
                    }
                    left++;
                    right--;
                }
            }
       }
       return ret;
    }
};

标准版本

cpp 复制代码
class Solution 
{
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        vector<vector<int>> ret;

        //#1:排序
        sort(nums.begin(),nums.end());

        //#2:利用双指针解决问题
        int n = nums.size();
        for(int i = 0;i < n;)//固定数a
        {
            if(nums[i] > 0)//小优化
            {
                break;
            }
            int left = i + 1;
            int right = n - 1;
            int target = -nums[i];
            while(left < right)
            {
                int sum = nums[left] + nums[right];
                if(sum > target)
                {
                    right--;
                }
                else if(sum < target)
                {
                    left++;
                }
                else
                {
                    ret.push_back({nums[i],nums[left],nums[right]});
                    left++;
                    right--;
                    //去重操作 left 和 right
                    while(left < right && nums[left] == nums[left - 1])
                    {
                        left++;
                    }
                    while(left < right && nums[right] == nums[right + 1])
                    {
                        right--;
                    }
                }
            }
            //去重操作 i
            i++;
            while(i < n && nums[i] == nums[i - 1])
            {
                i++;
            }
        }
        return ret;
    }
};

试题8:四数之和

算法原理

解法一:暴力枚举

排序 + 暴力枚举 + 利用set去重

解法二:双指针法

排序 + 双指针

依次固定一个数a

在a后面的区间,利用三数之和找到三个数

使这三个数的和等于target - a即可

依次固定一个数b

在b后面的区间,利用双指针找到两个数

使这两个数的和等于target - a - b即可

处理细节问题:去重、不漏

时间复杂度:O(N^3)

代码编写

  • 个人版本
cpp 复制代码
class Solution 
{
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        vector<vector<int>> ret;
        sort(nums.begin(),nums.end());
        int n = nums.size();
        for(int a = 0;a < n;)
        {
            for(int b = a + 1;b < n;)
            {
                int left = b + 1;
                int right = n - 1;
                long long new_target = (long long)target - nums[a] - nums[b];
                while(left < right)
                {
                    long long sum = (long long)nums[left] + nums[right];
                    if(sum < new_target)
                    {
                        left++;
                    }
                    else if(sum > new_target)
                    {
                        right--;
                    }
                    else
                    {
                        ret.push_back({nums[a],nums[b],nums[left],nums[right]});
                        left++;
                        right--;
                        while(left < right && nums[left] == nums[left - 1])
                        {
                            left++;
                        }
                        while(left < right && nums[right] == nums[right + 1])
                        {
                            right--;
                        }
                    }
                }
                b++;
                while(b < n && nums[b] == nums[b - 1])
                {
                    b++;
                }
            }
            a++;
            while(a < n && nums[a] == nums[a - 1])
            {
                a++;
            }
        }
        return ret;
    }
};
  • 标准版本
cpp 复制代码
class Solution 
{
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        vector<vector<int>> ret;

        //#1:排序
        sort(nums.begin(),nums.end());

        //#2:利用双指针解决问题
        int n = nums.size();
        for(int i = 0;i < n;)//固定数a
        {
            //利用三数之和
            for(int j = i + 1;j < n;)//固定数b
            {
                //双指针解法
                int left  = j + 1;
                int right = n - 1;
                long long aim = (long long)target - nums[i] - nums[j];
                while(left < right)
                {
                    int sum = nums[left] + nums[right];
                    if(sum < aim)
                    {
                        left++;
                    }
                    else if(sum > aim)
                    {
                        right--;
                    }
                    else
                    {
                        ret.push_back({nums[i],nums[j],nums[left++],nums[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;
    }
};
相关推荐
ziguo11225 小时前
Windows API 文件操作超级指南——从入门到内核
c++·windows·visualstudio
撩得Android一次心动5 小时前
Linux编程笔记4【个人用】
linux·笔记·学习
KaMeidebaby6 小时前
卡梅德生物技术快报|bli亲和力检测gst:告别批量跑胶:BLI实时酶切监测技术加速GST融合蛋白下游流程优化
前端·网络·数据库·人工智能·算法
alphaTao6 小时前
LeetCode 每日一题 2026/7/20-2026/7/26
算法·leetcode
zhangfeng11337 小时前
Ascendc 大语言模型框架 AscendCraft 和pypto 的区别 pypto生成算子c++源码吗
c++·人工智能·语言模型·算子开发
xqqxqxxq7 小时前
技术笔记:上下文工程(Context Engineering)心得总结(李博杰《深入理解 AI Agent》2.1,2.2观后总结)
人工智能·笔记
一只小灿灿7 小时前
C++ 修饰符全面详解
开发语言·c++
圣保罗的大教堂7 小时前
leetcode 3514. 不同 XOR 三元组的数目 II 中等
leetcode
code_pgf7 小时前
C/C++ 中 `typedef` 关键字详解
c语言·c++
会周易的程序员8 小时前
js-shm: 高性能 Node.js 共享内存模块
开发语言·javascript·c++·node.js·共享内存·shm