【算法】利用双指针解决算法题(C++)

news2024/11/26 5:48:52

文章目录

  • 1. 前言
  • 2. 双指针法引入
    • 283.移动零
  • 3. 使用双指针法解决算法题
    • 1089.复写零
    • 202.快乐数
    • 11.盛最多水的容器
    • [611.有效三角 形的个数](https://leetcode.cn/problems/valid-triangle-number/description/)
    • LCR179.查找总价格为目标值的两个商品
    • 15.三数之和

1. 前言

  1. 双指针并非真正意义上的指针,实际上一般使用下标表示/代替。
  2. 通常用于处理数组或链表等数据结构。
  3. 主要思想是使用两个指针在数组或链表中进行迭代、比较或操作

2. 双指针法引入

我们直接通过下面一道例题进行双指针方法的引入

283.移动零

在这里插入图片描述

思路

  1. 如图所示,我们定义双指针,每次移动指针,数组都满足:
    • cur左侧元素为处理过的元素,右侧为未处理的元素
    • 处理后的元素分为非零元素和零
    • dest 左侧为非零元素,dest与cur之间为零
  2. 细节注意:
    • 由于cur从0开始遍历数组,将dest从-1位置开始
    • 如果cur当前元素不为0,则于dest位置元素交换(先进行++dest)

代码

void moveZeroes(vector<int>& nums) {
    // cur: 从左到右遍历数组,dest: 非零元素的最后一个位置
    // [0, dest] 非零 [dest+1, cur-1] 零 [cur+1, n-1] 未处理
    int cur = 0, dest = -1;
    for(cur = 0; cur < nums.size(); ++cur)
    {
        if(nums[cur] != 0)
            std::swap(nums[++dest], nums[cur]);
    }
}

3. 使用双指针法解决算法题

1089.复写零

在这里插入图片描述

思路

  1. 题目要求复写数组中的0,且数组长度不变(且应就地修改数组)
  2. 此时我们思考:
    • 当从前向后复写的时候,如1023 -> 1002,此时就会发生问题,当我们将0复写到2的位置后,2已经被覆盖,后续找不到该元素了
    • 此时我们可以尝试进行从后向前复写
  3. 当决定从后向前复写的操作,此时思路步骤如下图所示:
    在这里插入图片描述

代码

void duplicateZeros(vector<int>& arr) {
    int cur = 0, dest = -1;
    int n = arr.size();
    // 先找到最后一个复写的数
    while(dest < n - 1)
    {
        // num[cur] 不为0,cur,dest后移一步,为零cur后移一步dest后移两步
        if(arr[cur] != 0)  dest++;
        else    dest += 2;

        if(dest >= n-1) break;
        cur++;
    }

    // 处理边界情况 如[1, 0, 2, 3, 0, 4]
    if(dest == n)
    {
        arr[n-1] = 0;
        dest-=2, cur--;
    }

    // 从后向前复写
    while(cur >= 0)
    {
        if(arr[cur] != 0)
        {
            arr[dest--] = arr[cur];
        }
        else
        {   // nums[cur] 为零,复写两次
            arr[dest--] = 0;
            arr[dest--] = 0;
        }
        cur--;
    }
}

202.快乐数

在这里插入图片描述

思路

在这里插入图片描述

  1. 我们首先通过上面的方式有了判断快乐数的方法,即一直算平方和看最后成环是否有1
  2. 对于此类成环问题,如判断链表是否有环,我们采用快慢指针来解决:
    • 如果快慢指针相遇,如果相遇位置不等于1,则不是快乐数
  3. 由此我们可以编写代码↓

代码

  • 将求 每位平方和 的操作写位函数
  • 快慢指针的移动,即求每位平方和的次数:
    • 慢指针每次求一次,快指针每次求两次。
    • 当两指针相遇,通过判断某个指针是否为1返回最终结果。
// 求每位平方和
int bitSquare(int n)
{
    int sum = 0; 
    while(n > 0)
    {
        int bit = n % 10;
        sum += bit * bit;
        n /= 10;
    }
    return sum;
}

bool isHappy(int n) {
    // 由已知得,数字一定成环,当slow与fast相遇
    // 如果相遇位置值为1,则是快乐数
    int slow = n, fast = bitSquare(n);;
    while(slow != fast)
    {
        // 慢指针每次一步,快指针每次两步
        slow = bitSquare(slow);
        fast = bitSquare(bitSquare(fast));
    }

    return fast == 1;
}

11.盛最多水的容器

在这里插入图片描述

思路

  • 解法一:暴力解法:

    在这里插入图片描述

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

    在这里插入图片描述

  • 有了双指针法的前提算法思想,我们可以总结出步骤:

    • 每次统计以left,right为边界的容量,并移动值更小的指针,重复步骤直到指针相遇

代码

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]){
            right--;
        }
        else{
            left++;
        }
    }
    
    return ret;
}

611.有效三角 形的个数

在这里插入图片描述
思路

  • 解法一:暴力枚举
    • 用三个for循环计算更新结果,时间开销太大!
for(int i = 0; i < n-1; ++i)
	for(int j = i + 1; j < n - 1; ++j)
		for(int k = j + 1; k < n - 1; ++k)
			check(i, j, k); // 省略具体步骤
  • 解法二:根据单调性用双指针法

    1. 我们知道三条边构成三角形的条件是 任意两条边之和>第三条边
    • 而对于a <= b <= c 的三条边来说,由于a+c > b, b + c > a恒成立,我们只需判断a + b > c 是否成立即可 (注意下图思路中,我们用下标表示其在数组中的值)

    在这里插入图片描述
    2. 则思路如下:

    • 排序数组
    • 通过外层for循环固定最大边,内层while循环双指针找满足条件的边

代码

// 给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数
int triangleNumber(vector<int>& nums) {
    std::sort(nums.begin(), nums.end()); // 先排序数组
    
    int n = nums.size(), count = 0;
    
    // 外层for循环 控制最大数
    for(int i = n - 1; i >= 1; --i)
    {
        int left = 0, right = i - 1;
        
        while(left < right)
        { 
            if(nums[left] + nums[right] <= nums[i])   left++;
            else { // 如果 left + right > 最大数,则区间内的所有left与right组合均满足三角形
                count += right - left;
                right--;
            }
        }
    }
    
    return count;
}

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

在这里插入图片描述

思路

  • 解法一:暴力枚举
    • 通过两层for循环,判断是否满足条件
    • 时间开销大,O(n^2)
for(int i = 0; i < n; ++i)
	for(int j = i + 1; j < n - 1; ++j)
		check(nums[i] + nums[j] == target);
  • 解法二:利用单调性使用双指针法
    1. 题目要求找到数组中任意两个和为target的数: 在这里插入图片描述

代码

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

15.三数之和

在这里插入图片描述

思路

  1. 题目要求找到数组中三个不同的位置,满足nums[i] + nums[j] + nums[k] == 0,并找到所有满足条件的三元组:
  • 解法一:排序+暴力枚举+使用set去重

  • 解法二:排序+双指针法
    在这里插入图片描述

代码

vector<vector<int>> threeSum(vector<int>& nums) {
    // 排序数组 便于后面去重等
    std::sort(nums.begin(), nums.end());
    
    int n = nums.size();
    vector<vector<int>> ret;

    // for循环固定第一个数,则固定的数后的序列进行“找和为target的两个数”
    // 如果 i = -5, 则target = 5
    for(int i = 0; i < n - 2; ++i)
    {
        // 如果固定的数重复,跳过重复的数
        if(i > 0 && nums[i] == nums[i-1])    continue;

        int left = i + 1, right = n - 1;
        while(left < right)
        {
            int sum = nums[i] + nums[left] + nums[right];
            if(sum < 0)
            {
                left++;
            }   
            else if(sum > 0)
            {
                right--;
            }
            else // 找到满足的数,插入到ret中,并更新left和right
            {
                ret.push_back({nums[i], nums[left], nums[right]});
                
                // 判断left和right的下一位是否重复,如果重复则跳过重复的数
                while(left < right && nums[left] == nums[left+1]) {
                    left++;
                }
                while(left < right && nums[right] == nums[right-1]) {
                    right--;
                }

                left++, right--;
            }
        }
    }
    return ret;
}

思路

代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1332526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AcWing 889. 满足条件的01序列(卡特兰数应用)

满足条件的01序列 假设长度为n个序列要求满足题意1的前缀0的个数不能超过1的个数 将问题抽象为从(0, 0)到(n, n) 向上走一个代表这一步对应序列中的值是1&#xff0c;向右走代表序列中的值是0 要想满足1的前缀0的数量大于1的数量就需要满足所有路过的途径在y x这个函数个下面…

java进阶学习笔记

学习java深度学习&#xff0c;提升编程思维&#xff0c;适合掌握基础知识的工作者学习 1.反射和代理1.1 概念介绍1.2应用场景1.3 反射-reflect1.3.1 获得类-Class1.3.2 获得类的字段-Field1.3.3 动态访问和修改对象实例的字段1.3.4 获得类方法-Method1.3.5 调用方法.invoke1.3.…

SpringBoot多模块项目下的包和组件扫描

问题阐述&#xff1a;为了简化项目代码&#xff0c;我们通常会使用多模块化代码进行开发&#xff0c;但是会出现如下问题&#xff1a;写代码时能够正常扫描或注入其他模块的Service&#xff0c;但是启动类一启动就报错Consider defining a bean of type com.xiaoqian.common.se…

【MATLAB第85期】基于MATLAB的2023年智能进化算法/元启发式算法合集(持续更新)

【MATLAB第85期】基于MATLAB的2023年智能进化算法/元启发式算法合集&#xff08;持续更新&#xff09; 1.海象进化算法&#xff08;Walrus Optimization Algorithm&#xff09; 作者&#xff1a;Pavel Trojovsk and Mohammad Dehghani 2.暴龙优化算法&#xff08;Tyrannosa…

java设计模式学习之【责任链模式】

文章目录 引言责任链模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用日志示例代码地址 引言 在现实生活中&#xff0c;常常会遇到这样的场景&#xff1a;一个请求或命令需要经过多个层级的处理。例如&#xff0c;一个行政审批流程可能需要通过多个部门的审…

H5 cryptojs加密算法

1. cryptojs是什么&#xff1f; 有时候项目涉及到的敏感数据比较多&#xff0c;为了信息安全&#xff0c;我们常常需要对一些数据进行接口加密处理&#xff0c;如编码、将明文转化为暗文、加密比对、AES BASE64 算法加密等。 接下来我们就分别说一下 CryptoJS 常用的一些方法。…

爬虫是什么?起什么作用?

【爬虫】 如果把互联网比作一张大的蜘蛛网&#xff0c;数据便是放于蜘蛛网的各个节点&#xff0c;而爬虫就是一只小蜘蛛&#xff0c;沿着网络抓取自己得猎物&#xff08;数据&#xff09;。这种解释可能更容易理解&#xff0c;官网的&#xff0c;就是下面这个。 爬虫是一种自动…

jvm对象探究

hostpot虚拟机对象探究 jvm虚拟机创建对象的流程 ava虚拟机&#xff08;JVM&#xff09;创建对象的过程包括以下步骤&#xff1a; 类加载&#xff1a; 首先&#xff0c;JVM会检查对象的类是否已经被加载。如果该类还没有被加载&#xff0c;JVM会通过类加载器加载该类的字节码…

TCP服务器的演变过程:多进程实现一对多的TCP服务器

使用多进程实现一对多的TCP服务器 一、前言二、新增使用的fork()函数三、实现步骤四、完整代码五、TCP客户端5.1、自己实现一个TCP客户端5.2、Windows下可以使用NetAssist的网络助手工具 小结 一、前言 手把手教你从0开始编写TCP服务器程序&#xff0c;体验开局一块砖&#xf…

【C++】unordered_set/unordered_multiset/unordered_map/unordered_multimap

我们下面来学习C的另外两个容器&#xff1a;unordered_set和unordered_map 目录 一、unordered系列关联式容器 二、unordered_map 2.1 unordered_map的介绍 2.2 unordered_map的接口说明 2.2.1 unordered_map的构造 2.2.2 unordered_map的容量 2.2.3 unordered_map的迭…

室内设计品牌网站搭建的作用是什么

随着人们生活质量日益提升&#xff0c;对其自身的居住环境也有了较高要求&#xff0c;每个人审美不一样&#xff0c;无论自己居住的房屋还是公司办公/商场等场景都需要设计不同的内容&#xff0c;还有各种设施的摆放及类别等都有讲究&#xff0c;尤其对公司及商场等环境&#x…

动态规划算法练习题

45. 跳跃游戏 II 中等 2K 相关企业 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j &…

EasyExcel 导出文件的格式化

阿里开源的这个库&#xff0c;让 Excel 导出不再复杂&#xff08;既要能写&#xff0c;还要写的好看&#xff09; 之前聊了 EasyExcel 的内容导出&#xff0c;本文主要说一下导出文件的格式化&#xff0c;格式化包括工作表/单元格样式和内容格式化。毕竟&#xff0c;有时候还是…

【leetcode21】合并两个有序链表Java代码讲解

12.22 21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2…

Appium Server 启动失败常见原因及解决办法

Error: listen EADDRINUSE: address already in use 0.0.0.0:4723 如下图&#xff1a; 错误原因&#xff1a;Appium 默认的4723端口被占用 解决办法&#xff1a; 出现该提示&#xff0c;有可能是 Appium Server 已启动&#xff0c;关闭已经启动的 Appium Server 即可。472…

音视频类App广告变现如何破局,最大化广告变现收益,让应用增收?

音视频App已然成为了我们日常获取、发布和交换信息的重要方式&#xff0c;在音视频行业不断的拓展中&#xff0c;用户的渗透率提升。 据数据显示&#xff0c;我国网络视听用户的规模已达9亿人次&#xff0c;网民使用率也突破了90%。庞大的市场规模和用户需求吸引了大批开发者和…

SRE - 监控建设

监控⽂档 随着信息技术的迅速发展及其在商业和工业环境中的广泛应用,系统的可靠性成为了组织的生存之本。Site Reliability Engineering(SRE)作为一种实践的结合体,广泛地用于确保和提升软件系统的可靠性。其中,它的一个重要组成部分是制定和监控服务的关键性能指标(Ser…

行为型设计模式(五):访问者模式 观察者模式

访问者模式 Visitor 1、什么是访问者模式 访问者模式允许定义一些不改变数据结构的前提下的操作。通过这种方式&#xff0c;可以在不修改元素类的情况下定义新的操作。访问者模式常用于对复杂对象结构进行操作&#xff0c;而又不希望在这些对象上破坏封装性。 2、为什么使用…

摄影企业网站搭建的作用是什么

几乎每个成年人都有一部手机&#xff0c;在互联网信息时代&#xff0c;手机的作用不言而喻&#xff0c;拍照/摄像成为了不少人经常会做的事&#xff0c;拍一张美美的照片发到社交圈赢得赞声&#xff0c;或是为以后留下回忆或发给自己在意的人&#xff0c;但这只限于生活记叙类图…

收集足够苹果的最小花园周长(LeetCode日记)

LeetCode-1954-收集足够苹果的最小花园周长 题目信息: 给你一个用无限二维网格表示的花园&#xff0c;每一个 整数坐标处都有一棵苹果树。整数坐标 ( i , j ) (i, j) (i,j) 处的苹果树有 ∣ i ∣ ∣ j ∣ |i| |j| ∣i∣∣j∣ 个苹果。 你将会买下正中心坐标是 ( 0 , 0 )…