双指针(C++)

文章目录

  • 1、移动零
  • 2、复写零
  • 3、快乐数
  • 4、盛最多水的容器
  • 5、有效三角形的个数
  • 6、和为s的两个数
  • 7、三数之和
  • 8、四数之和


需要理解的是,双指针并非只有指针,双指针的意思是两个位置。比如对于数组来说,两个下标也是双指针。当然,也可以说,双指针是一种思想。

关于详细的考量,会在题目中展现。常用指针名字:dest和src,left和right,prev和cur,slow和fast

1、移动零

链接

在这里插入图片描述

题目要求很清晰,要求0全部在最后,0前面全部是非0数,那么这时候双指针的作用区分就很明确。左指针的左边都是非0数,左指针的右边则是0。这是一个初步的整体划分,先定义 左右指针为a和b

接下来想想逐步的操作,两个下标位置,也就是两个int数,指向两个数组里的数,然后判断是否为0;一方为0,就要交换一下,让0来到a的右边,也就是让b的位置的数字是0,然后继续遍历。这样一想,随着逐渐的往后走,b指针的右边就是待处理的部分,而ab之间是0,a左边则是非0数;这样3个区间就划分好了。

a指针指向非0数,左边都是非0数,所以a指针指向最后一个非0数,b指针左边都是0,右边是待处理的,所以b指针指向最后一个0;如果走完整个空间,就没有待处理的部分了。

数组最初的位置可以不是0,我们可以让a指针初始化为-1。因为最一开始我们并不知道是否有非0数,所以不如让a为-1,b位置是0,b开始判断是否为0;如果num[b]是0,那么b就++,a不动,这样ab之间就有0了;如果是非0,那就得交换一下,但是需要a指针往前走一步再交换,不能这样访问num[-1],而且如果一直是非0数,a++后再去交换,就相当于是原地交换数值,不发生变化。

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

2、复写零

链接

在这里插入图片描述

这道题一个简单的思路是再开一个数组,一个指针指向原数组,另一个指向新数组,原数组只要出现0,新数组内就写两个0,不是就写一个,直到新数组全部填完。假设是数组[1, 0, 2, 3],a指针指向原数组,b指针指向新开的初始化好的数组。a为1,b就填1;a走到0,b填两个0,b来到第二个0,a来到2;a为2,b填2。

两个数组的情况转成一个的。需要注意的是,0后的数字不能直接覆盖掉,而只是写两遍0,然后接着下一个数字。所以我们得保证其它数字不会被覆盖掉。像这样不能覆盖的问题,有一个常用想法就是从后向前走。为了不被覆盖,先把要复写的数确定下来,这样b指针指向最后一个位置,a指针指向前面某个位置,无论怎样,a都每次往前走一步,而如果有0,b就走两步,否则走一步。

在找最后一个要复写的数的过程中,a指针指向0位置,而b指针指向-1。我们定义b指针是指向结果的,也就是原数组的某个数在结果中应当待的位置,a为非0,b就一步;a为0,b就走两步;当b走到末尾时,也就是结果数组中最后一个数的位置,而此时a应当指向填写这个位置的数。综合考虑,b在-1位置是合适的。可以代入例子来观察。在每一次b指针走动后都去判断是否到了最后一个位置,到了就退出循环,没到就让a++。

但是按照这个思路,还是有问题,这个问题就越界问题。b指针不会乖乖地到数组末尾停止,而是可能会越界。如果在后面,a位置的数都是非0数,那么b不会越界,因为它会乖乖地走到末尾,然后一判断,等于n - 1,那就退出;一旦越界,就说明a指向了一个0,b在n - 2的位置,然后走两步来到了n,所以b只会越界到n位置,因为如果在n - 1时就会退出,也不会走到n + 1位置。面对这个情况,就和归并排序处理边界情况一样,简单操作一下即可:将数组最后一个位置置为0,然后a往前走一步,b往前走两步,回到正确的位置,并继续往前判断并填写。

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

3、快乐数

链接

在这里插入图片描述

这道题是怎么和双指针有关的?

先看一下题。快乐数没有公式,所以我们只能从代码上找思路。一个暴力解法就是直接算,算到1为止。但如果它不是快乐数,要在什么时候停止计算?这就是一个问题,我们要找的就是一个暂停条件。

如果不是快乐数,就会一直循环。对此能想到的停止办法就是参考环形队列,环形链表:定义快慢指针,然后快指针每一步都比慢指针走得多,让快追慢,追上并且在整个过程也没有出现1,就说明这不是快乐数。为什么?fast是如何追上slow的?fast能追上slow说明,fast又走到了开头位置,也就是slow开始的位置,然后追上slow。也就是说,fast走完了一整个循环过程,而这之中都没有出现1,那肯定就不是快乐数。

这里的快慢指针就代表计算出来的数,比如slow计算一次而fast计算两次。由于停止的条件是slow等于fast,所以为了能够进入,fast和slow再初始化时就不能相等,可以让fast先计算一次,反正最后都是会追上slow。

class Solution {
public:int bitSum(int n) {int sum = 0, ret = 0;while (n) {ret = n % 10;sum += ret * ret;n /= 10;}return sum;}bool isHappy(int n) {if(n == 1) return true;int s = n, f = bitSum(n);while(s != f){s = bitSum(s);if(s == 1) return true;f = bitSum(bitSum(f));if(f == 1) return true;}return false;}
};

也可以不在while里写判断,出来判断return s == 1或者判断f。

4、盛最多水的容器

链接

在这里插入图片描述

这道题是一道练习,只放代码,不写思路。

class Solution {
public:int maxArea(vector<int>& height) {int n = height.size();int l = 0, r = n - 1;int res = 0;while(left < right){res = max(res, (r - l) * min(height[l], height[r]));if(height[l] > height[r]) --r;else ++l;}return res;}
};

5、有效三角形的个数

链接

在这里插入图片描述

由于三角形三条边需要满足a + b > c,所以O(N)的时间复杂度难以实现,即使固定住一个数,另外两个数也得挨个查看是否满足条件,不过这之中可以减少一些重复的遍历。

首先为了方便后序的遍历,先从小到大排序一下;然后从后往前走,固定最大的那个数,设定k为n - 1,剩下两个数就从前面这几个数中挑,所以k必然要至少为2,留出来两个数,设定前面两个数为i和j,如果nums[i] + nums[j] > nums[k],那就满足条件,当然也可以改成减法判断,这样就避免数值过大。

k固定后,i和j在运动。为了更有效地判断所有可能,让i = 0,j = k - 1,所以此时就能看出来,j肯定是k前面的最大数字,接着判断,如果不满足条件,j不需要更改,因为已经最大了,那么就增加i,直到满足条件就停下。此时nums[i] nums[j] nums[k]就是能组合成三角形的三条边,并且i继续增加也肯定满足条件,所以此时就可以直接加上 j - i,这就是从在j和k固定的情况下,符合条件的所有三元组个数。

这次结束后,j就往前走一步,继续按照上面的步骤去判断。k当然还是固定的,j变成第二个大的数,那么i?i其实也不用改变位置,因为之前更大的j和i前面的数字相加都不满足条件,那么现在更小的j也更不可能满足,所以只移动j就行了,然后从i现在的位置开始继续往后挨个判断。

这样的做法也是嵌套循环,只是减少了一些循环次数。对于k固定后,i和j的变化,以及三角形判断,还有更快的办法,这需要用到一些别的公式,可自行看力扣解析。

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

6、和为s的两个数

链接

在这里插入图片描述
在这里插入图片描述

这道题可以用双指针来完成,但很多人想到的是哈希吧?这道题只写双指针的思路,哈希方法只写代码。

双指针的思路脱胎于暴力枚举。枚举就是算所有的数字,看看有没有符合要求的,而双指针就是有所选择地计算。

数组本身是升序的。定义ab两个位置,分别指向最小数和最大数,如果将ab相加后依然小于目标值,那么最大值不需要变,因为它已经最大了,所以只需要改变让a++,a指向的值变大,再次判断;如果大于目标值,那就让b–。如果等于,就找到了,直接返回。这里仍然使用减法来判断。

class Solution {
public:vector<int> FindNumbersWithSum(vector<int> array, int sum) {if(array.empty()) return {};int l = 0, r = array.size() - 1;int res = 0;while(l < r){res = sum - array[l];if(res < array[r]) --r;else if(res > array[r]) ++l;else return {array[l], array[r]};}return {};}
};
class Solution {
public:vector<int> FindNumbersWithSum(vector<int> array, int sum) {if(array.empty()) return {};unordered_map<int, int> um;int n = array.size(), ret = 0;for(int i = 0; i < n; i++){ret = sum - array[i];if(um.find(ret) == um.end()){um[array[i]] = i;}else return {ret, array[i]};}return {};}
};

无论从时间还是空间上来看,双指针都明显优于哈希表。

7、三数之和

链接

在这里插入图片描述

两数之和为起点。

仔细看一下题,三个数不能相等,并且有相同的三元组只能有一个,比如-1 0 1和1 0 -1,只有有一个作为结果,另一个抛弃。

可以看出来,我们需要去重,在操作之前先对数组排序。用set去重是可行的,但这里不用多用别的容器,仅在这个数组上边判断边去重。

这道题与 和为s的两个数思路很像,我们先固定一个数,在剩下的区间内找符合条件的两个数即可,并且要找到所有的符合条件的组合;三个数都需要去重。不过如果我们固定的数是一个正数,那么它肯定不可能有对应的组合,因为后面的数都是正数,不可能相加为0。

另外一个重要的问题是越界,在代码中体现:

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int n = nums.size();int i = 0, j = 0, k = 0;int sum = 0;sort(nums.begin(), nums.end());vector<vector<int>> res;while(i < n){if(nums[i] > 0) break;//小优化j = i + 1, k = n - 1;while(j < k){sum = nums[i] + nums[j] + nums[k];if(sum > 0) --k;//大于说明数值过大了, 就让最大的数k减小else if(sum < 0) ++j;//小于说明数值过小了, 就让较小的j增加, 因为i固定else{res.push_back({nums[i], nums[j++], nums[k--]});//去重 while(j < k && nums[k] == nums[k + 1]) --k;while(j < k && nums[j] == nums[j - 1]) ++j;}}//i去重, 如果是for循环就把i++给去掉, 要不会越界++i;while(i < n && nums[i] == nums[i - 1]) i++;//i < n, 防止后面都是0, i直接出去了}return res;}
};

去重操作可以根据自己的思路去改变写代码的位置。

8、四数之和

链接

在这里插入图片描述
经历了和为s的两个数以及三数之和,四数之和也有所思路。三数之和以及四数之和都有一个基于暴力而升级的解法,就是排序 + set去重 + 暴力枚举。

四数之和也是利用双指针,或者说多指针。先固定一个数a,然后再固定一个数b,这样就剩下两个数了,然后运用找两数的方法去做。

当然,这个方法也有点暴力。它要处理的问题同样是去重,越界。

class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {sort(nums.begin(), nums.end());vector<vector<int>> res;long long ret = 0;int n = nums.size(), sum = 0;int i = 0, j = 0, l = 0, r = 0;while(i < n){j = i + 1;while(j < n){l = j + 1, r = n - 1;ret = (long long)target - nums[j] - nums[i];while(l < r){sum = nums[l] + nums[r];if(sum > ret) --r;else if(sum < ret) ++l;else{res.push_back({nums[i], nums[j], nums[l++], nums[r--]});//去重while(l < r && nums[l] == nums[l - 1]) ++l;while(l < r && nums[r] == nums[r + 1]) --r;}}++j;while(j < n && nums[j] == nums[j - 1]) ++j;}++i;while(i < n && nums[i] == nums[i - 1]) ++i;}return res;}
}

long long是为了解决力扣例子的中的数字超大问题。

结束。

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

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

相关文章

merge and rebase

文章目录 什么是merge什么是rebasemerge和rebase的区别操作执行git merge操作git rebase操作冲突解决解决冲突的步骤 Git Merge 和 Git Rebase 都是用于集成来自不同分支的修改的 Git 命令。 什么是merge Git Merge 是将一个分支的改动合并到另一个分支的方式。当你执行一个 m…

LabVIEW机械臂控制与图像处理示教平台

LabVIEW机械臂控制与图像处理示教平台 随着工业自动化技术的快速发展&#xff0c;工业机器人在制造业中的应用越来越广泛&#xff0c;它们在提高生产效率、降低人工成本以及保证产品质量方面发挥着重要作用。然而&#xff0c;传统的工业机器人编程和操作需要专业知识&#xff…

2024-5-3学习笔记 虚拟继承原理

目录 原理 总结 前面提到过&#xff0c;解决菱形继承产生的数据二义性问题和数据冗余&#xff0c;就需要用到虚拟继承&#xff0c;关于它是如何解决的&#xff0c;我们来一起研究。 class Person { public :string _name ; // 姓名 }; class Student : virtual public Perso…

某招聘网站搜索结果接口逆向之webpack扣取

逆向网址 aHR0cHM6Ly93ZS41MWpvYi5jb20v 逆向链接 aHR0cHM6Ly93ZS41MWpvYi5jb20vcGMvc2VhcmNoP2pvYkFyZWE9MDAwMDAwJmtleXdvcmQ9cGhwJnNlYXJjaFR5cGU9MiZrZXl3b3JkVHlwZT0 逆向接口 aHR0cHM6Ly93ZS41MWpvYi5jb20vYXBpL2pvYi9zZWFyY2gtcGM 逆向过程 请求方式 POST 逆向参数 …

华为Pura70发布,供应链公司进入静默保密期

保密措施&#xff1a;与华为Pura70发布相关的供应链公司在产品发布前后处于静默保密期。这可能是由于华为对于手机供应链的一些信息处于保密状态&#xff0c;尤其是关于麒麟芯片的代工厂商等敏感信息。这种保密措施有助于保持产品的神秘感&#xff0c;调动用户的好奇心&#xf…

微信小程序+esp8266温湿度读取

本文主要使用微信小程序显示ESP8266读取的温湿度并通过微信小程序控制LED灯。小程序界面如下图所示 原理讲解 esp8266 通过mqtt发布消息,微信小程序通过mqtt 订阅消息,小程序订阅后,就可以实时收到esp8266 传输来的消息。 个人可免费注册五个微信小程序账号,在微信小程序官…

开源框架 NanUI 项目宣布将暂停开发,作者转行卖钢材

NanUI 界面组件是一个开源的 .NET 窗体应用程序界面框架&#xff0c;适用于希望使用 HTML5 / CSS3 等前端技术来构建 Windows 窗体应用程序用户界面的 .NET 开发人员。 该项目的作者林选臣日前在 GitHub 上发布了停更公告&#xff0c;称因去年被裁员失业&#xff0c;他目前已经…

Redis 实战1

SDS Redis 只会使用 C 字符串作为字面量&#xff0c; 在大多数情况下&#xff0c; Redis 使用 SDS &#xff08;Simple Dynamic String&#xff0c;简单动态字符串&#xff09;作为字符串表示。 比起 C 字符串&#xff0c; SDS 具有以下优点&#xff1a; 常数复杂度获取字符串…

R语言学习—4—数据矩阵及R表示

1、创建向量、矩阵 在R中&#xff0c;c()函数用于创建向量或组合数据对象。它在某些情况下可能会被省略&#xff0c;因为R有一些隐式的向量创建规则。例如&#xff0c;当你使用:操作符创建一个数字序列时&#xff0c;R会自动创建一个向量&#xff0c;所以你不需要显式地调用c()…

UI-Diffuser——使用生成式扩散模型的UI原型设计算法解析

概述。 移动UI是影响参与度的一个重要因素&#xff0c;例如用户对应用的熟悉程度和使用的便利性。如果你有一个类似的应用程序&#xff0c;你可能会选择一个具有现代、好看的设计的应用程序&#xff0c;而不是一个旧的设计。然而&#xff0c;要从头开始研究什么样的UI最适合应…

[leetcode]Z 字形变换

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string convert(string s, int numRows) {int n s.length(), r numRows;if (r 1 || r > n) {return s;}int t r * 2 - 2;int c (n t - 1) / t * (r - 1);vector<string> mat(r, string(c, 0)…

达梦(DM) SQL聚集函数及日期运算操作

达梦DM SQL聚集函数及日期运算操作 聚集函数MAX、MIN、SUM、AVG、COUNT使用分析函数 sum (…) over (order by…) 可以生成累计和更改累计和的值计算出现次数最多的值 日期运算加减日、月、年加减时、分、秒日期间隔之时、分、秒日期间隔之日、月、年求两个日期间的工作天数确定…

Java基础知识(三) -- 流程控制

不论哪种编程语言&#xff0c;都会提供两种基本的流程控制结构&#xff1a;分支结构和循环结构。其中分支结构用于实现根据条件来选择性地执行某段代码&#xff0c;循环结构则用于实现根据循环条件重复执行某段代码。 1. 顺序结构 任何编程语言中最常见的程序结构就是顺序结构…

单片机编程实例400例大全(100-200)

今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际产品的参考价值。 今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际…

频分复用系统设计及其MATLAB实现

引言 随着通信技术的飞速发展&#xff0c;通信系统的容量需求不断增长。频分复用&#xff08;Frequency Division Multiplexing, FDM&#xff09;作为一种重要的多路复用技术&#xff0c;被广泛应用于现代通信系统中。本文将介绍频分复用系统的设计原理&#xff0c;并展示如何…

简单工厂模式、工厂方法模式和抽象工厂模式

1、简单工厂模式 1.1 结构 简单工厂包含如下角色&#xff1a; 抽象产品 &#xff1a;定义了产品的规范&#xff0c;描述了产品的主要特性和功能。 具体产品 &#xff1a;实现或者继承抽象产品的子类 具体工厂 &#xff1a;提供了创建产品的方法&#xff0c;调用者通过该方法…

springBootAdmin监控

简介 用于对 Spring Boot 应用的管理和监控。可以用来监控服务是否健康、是否在线、以及一些jvm数据等等 Spring Boot Admin 分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client)&#xff0c;服务端和客户端之间采用 http 通讯方式实现数据交互&#xf…

一文get等保测评与密评的差异

“等保”即网络安全等级保护&#xff0c;旨在通过不同安全等级的管理和技术措施&#xff0c;确保信息系统的安全稳定运行。我国于2017年颁布的《网络安全法》中第二十一条规定“国家实行网络安全等级保护制度”。法律要求网络运营者需按照网络安全等级保护制度要求&#xff0c;…

快讯! MySQL 8.4.0 LTS 发布(MySQL 第一个长期支持版本)

MySQL 第一个长期支持版本 8.4.0 LTS 发布&#xff0c;社区版下载地址&#xff1a; https://dev.mysql.com/downloads/mysql/ 功能变更 添加或更改的功能 组复制&#xff1a;与组复制相关的两个服务器系统变量的默认值已更改&#xff1a; 系统变量的默认值为 group_replication…

Unity开发一个FPS游戏之四

在前面的系列中&#xff0c;我已介绍了如何实现一个基本的FPS游戏&#xff0c;这里将继续进行完善&#xff0c;主要是增加更换武器以及更多动作动画的功能。 之前我是采用了网上一个免费的3D模型来构建角色&#xff0c;这个模型自带了一把AR自动步枪&#xff0c;并且自带了一些…