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

算法专题:双指针

1.移动零

283. 移动零 - 力扣(LeetCode)

双指针算法

两个指针的作用:

cur:从左向右遍历数组  dest:已处理区间内非零元素的最后一个位置。

[0,dest]  非零元素  

[dest+1,cur]      0

[cur,n-1]   待处理

cur遍历过程中遇到0元素cur++,遇到非0元素交换cur和dest+1位置的值,dest++和cur++

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

2.复写零

1089. 复写零 - 力扣(LeetCode)

方法一:运用库函数

直接遍历数组,在0的位置之前插入0,最后resize为原空间大小

class Solution {
public:
    void duplicateZeros(vector<int>& arr) 
    {
        int n = arr.size();
        auto it = arr.begin();
        while(it != arr.end())
        {
            if(*it == 0)
            {
                it = arr.insert(it,0);
                it++;
            }
            it++;
        } 
        arr.resize(n);   
    }
};

方法二:双指针法

1.先找到最后一个复写的数

        先判断cur位置的值,再决定dest往后移动一不还是两步,判断dest是否已经到了结束位置,cur++。

        找到最后一个复写的树,处理一下边界情况,如果dest == n 将arr[dest - 1]的位置置为0,cur--,dest -= 2。

2.从后往前完成复写操作

void duplicateZeros(vector<int>& arr) 
    {
        int dest = -1,cur = 0;
        int n = arr.size();
        while (cur < n)
        {
            if(arr[cur] == 0)
            {
                dest += 2;
            }
            else
            {
                dest++;
            }
            if(dest >= n - 1)
                break;
            cur++;
        }

        if(dest == n)
        {
            arr[dest - 1] = 0;
            cur--;
            dest -= 2;
        }

        while(cur >= 0)
        {
            if(arr[cur])
            {
                arr[dest--] = arr[cur--];
            }
            else
            {
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    }

3.快乐数

202. 快乐数 - 力扣(LeetCode)

思路:上述情况所示,这个值最后会形成一个环,一个环中全为1,一个环中没有1。所以可以使用判断链表是否有环的解法:快慢双指针。

1.定义快慢双指针

2.慢指针每次向后移动一位,快指针每次向后移动两步

3.判断相遇时候的值就可以

class Solution {
public:

    int retn(int n)
    {
        int ret = 0;
        while(n)
        {
            int x = n % 10;
            n /= 10;

            ret += x*x;
        }
        return ret;
    }

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

4.盛最多水的容器

11. 盛最多水的容器 - 力扣(LeetCode)

方法一(未通过):暴力解法,直接遍历数组

class Solution 
{
public:
    int maxArea(vector<int>& height) 
    {
        int max = 0;
        for(int i = 0; i < height.size(); i++)
        {
            for(int j = i + 1; j < height.size();j++)
            {
                int m = min(height[i],height[j]);
                int ret = m * (j - i);
                if(max < ret)
                    max = ret;
            }
        }
        return max;
    }
};

但是数据太多会超时。

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

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.有效三角形的个数

611. 有效三角形的个数 - 力扣(LeetCode)

思路:先对数组排序(在有序中只要判断a+b>c就可以判断是否为三角形),利用单调性,用双指针算法来解决问题

  1. 先找最大的数就是排序完的最后一个数
  2. 在最大数的左区间,使用双指针算法,快速统计出符合要求的三元组的个数
  3. 当nums[left] + nums[right] > nums[c] 剩下left的右边的数与right的数相加必然大于c的数,ret直接加等right - left区间的数,right再--。
  4. 当nums[left] + nums[right] < nums[c] left与right左边的数相加肯定小于c的数,直接++left
  5. 结束条件是c的位置要大于等于2(c左边还有right和left)
class Solution {
public:
    int triangleNumber(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        int c = nums.size() - 1;
        int left = 0, right = c - 1;
        int ret = 0;
        while(c >= 2)
        {
            while(left < right)
            {
                if(nums[left] + nums[right] > nums[c])
                {
                    ret += right - left;
                    right--;
                }
                else
                {
                    left++;
                }
            }
            c--;
            right = c - 1;
            left = 0;
        }
        return ret;
    }
};

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

LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)

思路:利用单调性(题目中说了有序),使用双指针算法解决问题

定义left指向开始,和right指向结尾,判断两数相加的值与target的关系,大于就right--,小于left++

    vector<int> twoSum(vector<int>& price, int target) 
    {
        vector<int> ret;
        int left = 0, right = price.size() - 1;
        while(left < right)
        {
            if(price[left] + price[right] == target)
            {
                ret.push_back(price[left]);
                ret.push_back(price[right]);
                break;
            }
            else if(price[left] + price[right] > target)
            {
                --right;
            }
            else
            {
                ++left;
            }
        }    
        return ret;
    }

还有一种直接隐式类型转换做返回值

vector<int> twoSum(vector<int>& price, int target) 
    {       
        int left = 0, right = price.size() - 1;
        while(left < right)
        {
            int sum = price[left] + price[right];
            if(sum > target) right--;
            else if(sum < target) left++;
            else return {price[left],price[right]};
        }

        //不加这句在leetcode上编译不过,所以要返回一个值
        return {-1,-1};    
    }

7.三数之和

思路:对数组进行排序后,使用双指针,固定一个数c,然后利用上面两个商品的两数求和来求c的相反数,区别是找到一种结果后不要停(当c大于0时,因为数组有序,所以在c右边不可能找到相加为c相反数的值)。

这道题还需要去重,找到一种结果后,left和right要跳过重复元素,当使用完一次双指针后c也要跳过重复元素。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        vector<vector<int>> vv;
        for(int c = 0; c < nums.size() - 2; c++)
        {
            if(nums[c] > 0)//大于0后,后面的数相加绝对不会等于他的相反数
                break;
            int left = c + 1;
            int right = nums.size() - 1;
            while(left < right)
            {
                int x = -nums[c];
                int sum = nums[left] + nums[right];
                vector<int> v;
                if(sum < x)
                    left++;
                else if(sum > x)
                    right--;
                else
                {
                    v.push_back(nums[left]);
                    v.push_back(nums[right]);
                    v.push_back(nums[c]);

                    vv.push_back(v);
                    while(left < right && nums[left] == nums[left + 1])
                    {
                        left++;
                    }
                    while(right > left &&nums[right] == nums[right - 1])
                    {
                        right--;
                    }
                    left++;
                    right--;
                }
                while(nums[c] == nums[c + 1] && c < nums.size() - 2)
                    c++;
            }
        }
        return vv;
    }
};

8.四数之和

18. 四数之和 - 力扣(LeetCode)

和上面三数之和的思路一样,四数之和是固定两个数,再左右指针往下走,但是这个题在减的时候会报错,所以要开成long long。

class Solution 
{
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
    sort(nums.begin(),nums.end());
    vector<vector<int>> vv;
    size_t n = nums.size();
    for(size_t i = 0; i < n; )
    {
        for(size_t j = i + 1; j < n;)
        {
            size_t left = j + 1;
            size_t right = n - 1;
            long long x = (long long)target - nums[i] - nums[j];
            while(left < right)
            {
                int sum = nums[left] + nums[right];
                if(sum < x)
                {
                    left++;
                }
                else if(sum > x)
                {
                    right--;
                }
                else
                {
                    vector<int> v;
                    v.push_back(nums[i]);
                    v.push_back(nums[j]);
                    v.push_back(nums[left]);
                    v.push_back(nums[right]);

                    vv.push_back(v);

                    left++;
                    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 vv;
}
};

相关文章:

  • 470用 Rand7() 实现 Rand10()
  • [MSPM0开发]之五 MSPM0G3507 SysTick定时器的配置与使用(systick中断实现延时函数)
  • 微信小程序运行机制详解
  • 单片机Day05---动态数码管显示01234567
  • WindowsPE文件格式入门08.导出表
  • 蓝桥杯嵌入式历年省赛客观题
  • GPU虚拟化技术在深度学习集群中的应用实践
  • Spring AI 结构化输出详解
  • 【foc思考】为什么svpwm要不停变换占空比
  • Python 实现最小插件框架
  • JDK(Java Development Kit)从发布至今所有主要版本 的详细差异、新增特性及关键更新的总结,按时间顺序排列
  • 【架构师从入门到进阶】第五章:DNSCDN网关优化思路——第七节:网关-XSS攻击与预防
  • uniapp日常总结--uniapp页面跳转方式
  • 单片机Day05---静态数码管
  • Cocos Creator Shader入门实战(八):Shader实现圆形、椭圆、菱形等头像
  • IIC通信协议
  • Python快速入门指南:从零开始掌握Python编程
  • JetBrain/IDEA :Request for Open Source Development License
  • 基于springboot+vue的秦皇岛旅游景点管理系统
  • MySql 自我总结
  • 360免费网站建设/厦门人才网597人才网
  • 建筑设计公司资质/优化关键词排名
  • 唐山网站公司建站/淘宝关键词查询
  • 公司管理系统开发/seo在线优化工具 si
  • python做网站步骤/潍坊网站定制模板建站
  • 网站要怎么做吸客户引眼球/百度seo推广软件