当前位置: 首页 > news >正文

【算法】双指针

双指针问题

双指针算法的核心逻辑

双指针算法主要用于解决数组划分也叫数组分块类的问题,通过两个指针(这里的指针也可以是数组的下标),将数组进行划分成三个部分,当cur指针进行扫描完整个区间后,即只剩下两个区间,即题目的要求。

283、移动零

题解

class Solution {
public:
    void moveZeroes(vector<int>& nums) 
    {
        int dest=0;     //进行代表处理完毕的区间
        int cur=0;    //进行遍历过的区间
        while(cur<nums.size())
        {
            if(nums[cur]!=0)
            {
                int temp=nums[dest];
                nums[dest]=nums[cur];
                nums[cur]=temp;
                cur++;
                dest++;
            }
            else
            {
                cur++;
            }
        }
    }
};

1083、复写零

题解一:循环覆盖补零法

通过阅读题意

当数组中出现0的时候,需要将0进行复写,将0以后的元素进行向后移动一位,但是数组的长度是固定的,所以说最后一个元素就没了,这里我就直接进行想到了从0之后的位置进行循环向后移动进行覆盖,然后在进行复写0。在进行复写0后向后进行移动两位,防止进行全部置零处理了。

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int n = arr.size();
        for (int i = 0; i < n; ) 
        {
            if (arr[i] == 0) {
                // 将后面的元素全部右移一位
                for (int j = n - 1; j > i; j--) 
                {
                    arr[j] = arr[j - 1];
                }
                if (i + 1 < n) 
                {
                    arr[i + 1] = 0; // 复制 0
                }
                i += 2; // 跳过已处理的 0
            } 
            else 
            {
                i++;
            }
        }
    }
};

题解二:双指针算法

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

 202、快乐数

题解

快慢指针法

class Solution {
public:
    bool isHappy(int n) 
    {   
        int slow=n-1;
        int fast=n;
        int temp1=n;
        int temp2=n;
        while(fast!=slow)
        {
            int count=0;
            while(temp1)
            {
                count+=(temp1%10)*(temp1%10);
                temp1=temp1/10;
            }
            slow=count;
            temp1=count;

            int num=2;
            while(num--)
            {
                count=0;
                while(temp2)
                {
                    count+=(temp2%10)*(temp2%10);
                    temp2=temp2/10;
                }
                temp2=count;
            }
            fast=count;
        }
        if(fast==1)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

11、盛水最多的容器

题解

通过阅读题意,很容易进行想到通过暴力的方式进行解决,暴力的方式就是通过两个for循环进行依次取两条线进行进行暴力枚举,但是这种方式是超时的,不满足时间复杂度

影响水池中水的因素有两种,分别是两条线的距离两条线中较短的那一条线,这里采取的思想是在刚开始的时候直接将两条线之间的距离设成最大,在两条线之间的距离变小的过程中,只有较短的那条线的长度变长时才有可能容量变大。

class Solution {
public:
    int maxArea(vector<int>& height) 
    {
        int left=0;
        int right=height.size()-1;  //将两条线的距离设成最大
        int capacity=(height[left]<height[right]?height[left]:height[right])*(right-left);  //进行初始化容量
        int newcap=0;
        while(right>left)
        {
            //判断两条线中较短的那条线进行变长时的容量变化
            if(height[left]<height[right])
            {
                left++;
                newcap=(height[left]<height[right]?height[left]:height[right])*(right-left);
            }
            else
            {
                right--;
                newcap=(height[left]<height[right]?height[left]:height[right])*(right-left);
            }
            if(newcap>capacity)
            {
                capacity=newcap;
            }
        }
        return capacity;
    }
};

611、有效三角形个数

题解

这道题也不难想,第一时间最容易进行想到的一般都是暴力的方法,但是这道题直接进行暴力解决问题的话需要进行三个for循环进行解决问题,时间复杂度是O(n*n*n)直接就超时了。

可以通过一个for循环和双指针进行对暴力的方法进行优化,将时间复杂度进行优化到O(n*n)

这种优化都是基于单调性进行的优化两个指针一般是不同侧的。

class Solution {
public:
    class Compare
    {
    public:
        bool operator()(int a,int b)
        {
            return a<b;
        }
    };
    int triangleNumber(vector<int>& nums) 
    {
        int left=0;
        int right=0;
        int count=0;
        //进行数组排序
        sort(nums.begin(),nums.end());
        for(int i=nums.size()-1;i>1;i--)
        {
            left=0;
            right=i-1;; 
            while (left < right) 
            {
                if (nums[left] + nums[right] > nums[i]) 
                {
                    count += right - left;
                    --right;
                } 
                else 
                {
                    ++left;
                }
            }
            
        }
        return count;
    }
};

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

题解:

暴力:

通过两个for循环进行将数组中的任意两个数进行求和然后进行比对,时间复杂度O(n*n)。

双指针:

通过双指针对暴力的方式进行优化,先通过排序的方式进行将数组处理成有序,然后依然可以通过单调性进行优化。时间复杂度O(logN+O(n))

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

15、三数之和 

题解

暴力

直接无脑三个for循环然后进行遍历。

双指针

通过双指针进行优化,这道题的思路上有效三角形的个数那道题的思想非常的相似。通过for循环进行固定一个数,然后通过双指针进行进行优化。

这道题最最最恶心的一个点是需要进行去重,这里着重说一下去重的思路,需要进行内部去重和外部去重,内部去重就是for循环进行固定的数进行去重(跳过相同的数),外部去重(双指针进行去重)。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        //进行排序
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret={};
        for(int i=nums.size()-1;i>1;i--)
        {
            int left=0;
            int right=i-1;
            //进行内部去重
            if (i < nums.size()-1&& nums[i] == nums[i+1]) continue;
            while(left<right)
            {
                if(nums[left]+nums[right]+nums[i]>0)
                {
                    right--;
                }
                else if(nums[left]+nums[right]+nums[i]<0)
                {
                    left++;
                }
                else
                {
                    ret.push_back({nums[left],nums[right],nums[i]});
                    //进行外部去重
                    while(left < right &&nums[right]==nums[right-1])
                    {
                        right--;
                    }
                    while(left < right &&nums[left+1]==nums[left])
                    {
                        left++;
                    }
                    left++;
                    right--;
                }
            }
        }
        return ret;
    }
    

};

 18、四数之和

题解

双指针

和上一道题思路一样,不进行解释了。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        int n = nums.size();
        if (n < 4) return res;
        
        sort(nums.begin(), nums.end());
        
        for (int i = 0; i < n-3; ++i) {
            // 跳过重复值
            if (i > 0 && nums[i] == nums[i-1]) continue;
            
            // 提前终止条件(可选优化)
            if ((long long)nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break;
            if ((long long)nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target) continue;
            
            for (int j = i+1; j < n-2; ++j) {
                // 跳过重复值
                if (j > i+1 && nums[j] == nums[j-1]) continue;
                
                int left = j+1, right = n-1;
                while (left < right) {
                    long long sum = (long long)nums[i] + nums[j] + nums[left] + nums[right];
                    
                    if (sum == target) {
                        res.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;
                        ++left;
                        --right;
                    }
                    else if (sum < target) ++left;
                    else --right;
                }
            }
        }
        return res;
    }
};

http://www.dtcms.com/a/108375.html

相关文章:

  • GIT ---- 解决【fatal: Authentication failed for】
  • 【案例89】达梦数据库优化器参数导致的SQL执行错误
  • 在Ubuntu20.04开发Dify插件教程,部署Dify插件脚手架
  • 深度学习 Deep Learning 第15章 表示学习
  • 针对 MySQL 数据库的详细说明,分类列出临时资源(临时表、游标、未提交事务、会话变量、预编译语句)的创建、清理方式及未清理后果,并以表格总结
  • [CH32] RISC-V汇编指令解释
  • linux下springboot项目守护进程编写
  • arm64平台下linux访问寄存器
  • Python----机器学习(线性回归:前向传播和损失函数)
  • 【C++基础知识】 C 预处理器中的 #line 指令详解
  • RabbitMQ应用2
  • Linux系统之SFTP-搭建SFTP服务器
  • ui-tars和omni-parser使用
  • JavaScript 模块化详解( CommonJS、AMD、CMD、ES6模块化)
  • 网络安全-等级保护(等保) 1-0 等级保护制度公安部前期发文总结
  • 蓝桥杯 web 表格数据转化(组件挂载、模板字符串)
  • 【硬件视界9】网络硬件入门:从网卡到路由器
  • C# 扩展方法
  • 跨网连接vscode
  • 银联三级等保定级报告
  • CMake学习--Window下VSCode 中 CMake C++ 代码调试操作方法
  • 闭环SOTA!北航DiffAD:基于扩散模型实现端到端自动驾驶「多任务闭环统一」
  • 面基spring如何处理循环依赖问题
  • conda 清除 tarballs 减少磁盘占用 、 conda rename 重命名环境、conda create -n qwen --clone 当前环境
  • 机器学习、深度学习和神经网络
  • vscode调试python(transformers库的llama为例)
  • C#实现HiveQL建表语句中特殊数据类型的包裹
  • 用docker部署goweb项目
  • RainbowDash 的 Robot
  • C++学习笔记(三十一)——map