【算法刷题-双指针篇】

目录

  • 1.leetcode-27. 移除元素
  • 2.leetcode-344. 反转字符串
  • 3.leetcode-剑指 Offer 05. 替换空格
  • 4.leetcode-206. 反转链表
  • 5.leetcode-19. 删除链表的倒数第 N 个结点
  • 6.leetcode-面试题 02.07. 链表相交
  • 7.leetcode-142. 环形链表 II
  • 8.leetcode-15. 三数之和
  • 9.leetcode-18. 四数之和
  • 10.leetcode-151. 反转字符串中的单词

1.leetcode-27. 移除元素

(1)题目描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

在这里插入图片描述
(2)方法及思路

1.如果右指针指向的元素不等于 val,它一定是输出数组的一个元素,我们就将右指针指向的元素复制到左指针位置,然后将左右指针同时右移;

2.如果右指针指向的元素等于 val,它不能在输出数组里,此时左指针不动,右指针右移一位。

(3)代码实现

class Solution {
public:int removeElement(vector<int>& nums, int val) {int n=nums.size();int left=0,right=0;for(int right=0;right<n;++right){if(nums[right]!=val){nums[left]=nums[right];left++;}}return left;}
};

(4)代码优化
如果左指针 left 指向的元素等于 val,此时将右指针 right 指向的元素复制到左指针 left 的位置,然后右指针 right 左移一位。如果赋值过来的元素恰好也等于 val,可以继续把右指针 right 指向的元素的值赋值过来(左指针 left指向的等于 val的元素的位置继续被覆盖),直到左指针指向的元素的值不等于 val 为止。

当左指针 left 和右指针 right 重合的时候,左右指针遍历完数组中所有的元素。

class Solution {
public:int removeElement(vector<int>& nums, int val) {int left = 0, right = nums.size();while (left < right) {if (nums[left] == val) {nums[left] = nums[right - 1];right--;} else {left++;}}return left;}
};

(特别注意这里的right是n不是n-1,不然会跳出循环条件)

2.leetcode-344. 反转字符串

(1)题目描述
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
在这里插入图片描述
(2)思路及方法

1.将 left 指向字符数组首元素,right 指向字符数组尾元素。
2.当 left < right:
交换 s[left] 和 s[right];
left 指针右移一位,即 left = left + 1;
right 指针左移一位,即 right = right - 1。
3.当 left >= right,反转结束,返回字符数组即可。

(3)代码实现

class Solution {
public:void reverseString(vector<char>& s) {int end=s.size()-1;int prev=0;while(prev<end){swap(s[prev],s[end]);prev++;end--;}}
};

3.leetcode-剑指 Offer 05. 替换空格

(1)题目描述
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

(2)方法及思路

首先扩充数组到每个空格替换成"%20"之后的大小。

然后从后向前替换空格,也就是双指针法,过程如下:

i指向新长度的末尾,j指向旧长度的末尾。

(3)代码实现

class Solution {
public:string replaceSpace(string s) {int count = 0; // 统计空格的个数int sOldSize = s.size();for (int i = 0; i < s.size(); i++) {if (s[i] == ' ') {count++;}}// 扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小s.resize(s.size() + count * 2);int sNewSize = s.size();// 从后先前将空格替换为"%20"for (int i = sNewSize - 1, j = sOldSize - 1; j < i; i--, j--) {if (s[j] != ' ') {s[i] = s[j];} else {s[i] = '0';s[i - 1] = '2';s[i - 2] = '%';i -= 2;}}return s;}
};

4.leetcode-206. 反转链表

(1)题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
在这里插入图片描述
(2)方法及思路

假设链表为 1→2→3→∅,我们想要把它改成 ∅←1←2←3。
在遍历链表时,将当前节点的 next指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

(3)代码实现

class Solution {
public:ListNode* reverseList(ListNode* head) {struct ListNode* cur=head;struct ListNode* newhead=NULL;while(cur){struct ListNode* next=cur->next;cur->next=newhead;newhead=cur;cur=next;}return newhead;}
};

(4)递归

class Solution {
public:ListNode* reverseList(ListNode* head) {if (!head || !head->next) {return head;}ListNode* newHead = reverseList(head->next);head->next->next = head;head->next = nullptr;return newHead;}
};

5.leetcode-19. 删除链表的倒数第 N 个结点

(1)题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
在这里插入图片描述
(2)方法及思路
由于我们需要找到倒数第 n 个节点,因此我们可以使用两个指针 first和 second 同时对链表进行遍历,并且 first 比 second 超前 n 个节点。当 first 遍历到链表的末尾时,second 就恰好处于倒数第 n 个节点。

(3)代码实现

class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* newnode=new ListNode(0,head);ListNode* cur=head;while(n>0){cur=cur->next;n--;}ListNode* prev=newnode;while(cur){prev=prev->next;cur=cur->next;}prev->next=prev->next->next;return newnode->next;}
};

6.leetcode-面试题 02.07. 链表相交

(1)题目描述

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
在这里插入图片描述
(2)方法及思路
1.每步操作需要同时更新指针 pA和 pB。

2.如果指针 pA不为空,则将指针 pA移到下一个节点;如果指针 pB 不为空,则将指针 pB 移到下一个节点。

3.如果指针 pA 为空,则将指针 pA\textit{pA}pA 移到链表 headB的头节点;如果指针 pB为空,则将指针 pB 移到链表 headA的头节点。

4.当指针 pA 和 pB 指向同一个节点或者都为空时,返回它们指向的节点或者 null。

(3)代码实现

class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if (headA == nullptr || headB == nullptr) {return nullptr;}ListNode *pA = headA, *pB = headB;while (pA != pB) {pA = pA == nullptr ? headB : pA->next;pB = pB == nullptr ? headA : pB->next;}return pA;}
};

7.leetcode-142. 环形链表 II

(1)题目描述

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

在这里插入图片描述
(2)方法及思路
我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。

如下图所示,设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n圈,因此它走过的总距离为 a+n(b+c)+b=a+(n+1)b+nc。

(3)代码实现

class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode *slow = head, *fast = head;while (fast != nullptr) {slow = slow->next;if (fast->next == nullptr) {return nullptr;}fast = fast->next->next;if (fast == slow) {ListNode *ptr = head;while (ptr != slow) {ptr = ptr->next;slow = slow->next;}return ptr;}}return nullptr;}
};

8.leetcode-15. 三数之和

(1)题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。
在这里插入图片描述
(2)方法及思路

在这里插入图片描述
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

(3)代码实现

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int n = nums.size();sort(nums.begin(), nums.end());vector<vector<int>> ans;// 枚举 afor (int first = 0; first < n; ++first) {// 需要和上一次枚举的数不相同if (first > 0 && nums[first] == nums[first - 1]) {continue;}// c 对应的指针初始指向数组的最右端int third = n - 1;int target = -nums[first];// 枚举 bfor (int second = first + 1; second < n; ++second) {// 需要和上一次枚举的数不相同if (second > first + 1 && nums[second] == nums[second - 1]) {continue;}// 需要保证 b 的指针在 c 的指针的左侧while (second < third && nums[second] + nums[third] > target) {--third;}// 如果指针重合,随着 b 后续的增加// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环if (second == third) {break;}if (nums[second] + nums[third] == target) {ans.push_back({nums[first], nums[second], nums[third]});}}}return ans;}
};

(4)代码变形

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int n = nums.size();sort(nums.begin(), nums.end());vector<vector<int>> ans;for (int first = 0; first < n - 2; ++first) {if (first > 0 && nums[first - 1] == nums[first]) {continue;}int second = first + 1;int third = n - 1;while (second < third) {int sum = nums[first] + nums[second] + nums[third];if (sum == 0) {ans.push_back({nums[first], nums[second], nums[third]});while (second < third && nums[second] == nums[second + 1]) {second++;}while (second < third && nums[third] == nums[third - 1]) {third--;}second++;third--;} else if (sum < 0) {second++;} else {third--;}}}return ans;}
};

9.leetcode-18. 四数之和

(1)题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
在这里插入图片描述
(2)方法及思路

四数之和,和15.三数之和 (opens new window)是一个思路,都是使用双指针法, 基本解法就是在15.三数之和 (opens new window)的基础上再套一层for循环。
但是有一些细节需要注意,例如: 不要判断nums[k] > target 就返回了,三数之和 可以通过 nums[i] > 0 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1],target是-10,不能因为-4 > -10而跳过。但是我们依旧可以去做剪枝,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)就可以了。

(3)代码实现

class Solution
{
public:vector<vector<int>> fourSum(vector<int>& num, int target){vector<vector<int>>  newnum;sort(num.begin(), num.end());int n = num.size();for (int first = 0; first < n; ++first){if (first > 0 && num[first] == num[first - 1])continue;for (int second = first + 1; second < n; ++second){if (second > first + 1 && num[second] == num[second - 1])continue;int third = second + 1;int forth = n - 1;long long target_c = (long long)target - num[first] - num[second];while (third < forth){if (target_c == num[third] + num[forth]) {newnum.push_back({ num[first], num[second], num[third], num[forth] });do {third++;} while (third < n && num[third] == num[third - 1]);}else if (target_c > num[third] + num[forth]){third++;}elseforth--;}}}return newnum;}
};

10.leetcode-151. 反转字符串中的单词

(1)题目描述

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
在这里插入图片描述
(2)方法及思路

逻辑很简单,从前向后遍历,遇到空格了就erase。

如果不仔细琢磨一下erase的时间复杂度,还以为以上的代码是O(n)的时间复杂度呢。

想一下真正的时间复杂度是多少,一个erase本来就是O(n)的操作。

erase操作上面还套了一个for循环,那么以上代码移除冗余空格的代码时间复杂度为O(n^2)。

那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。

(3)代码实现

class Solution {
public:void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []for (int i = start, j = end; i < j; i++, j--) {swap(s[i], s[j]);}}void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.htmlfor (int i = 0; i < s.size(); ++i) { //if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。s[slow++] = s[i++];}}}s.resize(slow); //slow的大小即为去除多余空格后的大小。}string reverseWords(string s) {removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。reverse(s, 0, s.size() - 1);int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。for (int i = 0; i <= s.size(); ++i) {if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。start = i + 1; //更新下一个单词的开始下标start}}return s;}
};

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

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

相关文章

python unitest自动化框架

以下举一个最简单的unitest实例&#xff0c;包含备注&#xff0c;自己拉取代码运行一次就知道原理了 import unittest import osclass TestSample(unittest.TestCase):classmethoddef setUpClass(cls) -> None:print(整个测试类只执行一次)def setUp(self) -> None:prin…

【python零基础入门学习】python基础篇之判断与for循环(二)

本站以分享各种运维经验和运维所需要的技能为主 《python》&#xff1a;python零基础入门学习 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8》暂未更新 《docker学习》暂未更新 《ceph学习》ceph日常问题解…

flutter plugins插件【二】【FlutterAssetsGenerator】

2、FlutterAssetsGenerator 介绍地址&#xff1a;https://juejin.cn/post/6898542896274735117 配置assets目录 ​ 插件会从pubspec.yaml文件下读取assets目录&#xff0c;因此要使用本插件&#xff0c;你需要在pubspec.yaml下配置资源目录 flutter:# The following line ens…

Navicat连接数据库报2003错误解决办法

是防火墙还没有开启 查看防火墙管理的端口 设置3306防火墙开启&#xff0c;重载防火墙 连接成功

2024年java面试--多线程(2)

系列文章目录 2024年java面试&#xff08;一&#xff09;–spring篇2024年java面试&#xff08;二&#xff09;–spring篇2024年java面试&#xff08;三&#xff09;–spring篇2024年java面试&#xff08;四&#xff09;–spring篇2024年java面试–集合篇2024年java面试–redi…

CSS中如何实现弹性盒子布局(Flexbox)的换行和排序功能?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 换行&#xff08;Flexbox Wrapping&#xff09;⭐ 示例&#xff1a;实现换行⭐ 排序&#xff08;Flexbox Ordering&#xff09;⭐ 示例&#xff1a;实现排序⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得…

【uniapp 配置启动页面隐私弹窗】

为什么需要配置 原因 根据工业和信息化部关于开展APP侵害用户权益专项整治要求&#xff0c;App提交到应用市场必须满足以下条件&#xff1a; 1.应用启动运行时需弹出隐私政策协议&#xff0c;说明应用采集用户数据 2.应用不能强制要求用户授予权限&#xff0c;即不能“不给权…

iPhone 14 Plus与iPhone 14 Pro:你应该买哪一款

又到了iPhone季,这意味着你可能会在几种不同的机型之间左右为难,无法决定买哪一款。更令人困惑的是,苹果推出的iPhone变体——iPhone 14 Plus,只比老款iPhone 14 Pro低100美元。 有这么多选择,你可能想知道哪款iPhone最适合你。你应该买一部大屏幕的iPhone 14 Plus并节省…

C语言每日一练--------Day(11)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;找到数组中消失的数字 哈希表 &#x1f493;博主csdn个人主页&#xff…

leetcode原题: 最小值、最大数字

题目1&#xff1a;最小值 给定两个整数数组a和b&#xff0c;计算具有最小差绝对值的一对数值&#xff08;每个数组中取一个值&#xff09;&#xff0c;并返回该对数值的差 示例&#xff1a; 输入&#xff1a;{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8} 输出&#xff1a;3&…

网络编程 http 相关基础概念

文章目录 表单是什么http请求是什么http请求的结构和说明关于http方法 GET和POST区别http常见状态码http响应http 请求是无状态的含义html是什么 &#xff08;前端内容&#xff0c;了解即可&#xff09;html 常见标签 &#xff08;前端内容&#xff0c;了解即可&#xff09;关于…

项目总结知识点记录-文件上传下载(三)

&#xff08;1&#xff09;文件上传 代码&#xff1a; RequestMapping(value "doUpload", method RequestMethod.POST)public String doUpload(ModelAttribute BookHelper bookHelper, Model model, HttpSession session) throws IllegalStateException, IOExcepti…

XSS的分析

目录 1、XSS的原理 2、XSS的攻击类型 2.1 反射型XSS 2.2 存储型XSS 2.3 DOM-based 型 2.4 基于字符集的 XSS 2.5 基于 Flash 的跨站 XSS 2.6 未经验证的跳转 XSS 3、复现 3.1 反射性 3.2 DOM-based型 1、XSS的原理 XSS的原理是恶意攻击者往 Web 页面里插入恶意可执行…

Android中级——消息机制

消息机制 概念ThreadLocalMessageQueueLooperHandlerrunOnUiThread() 概念 MessageQueue&#xff1a;采用单链表的方法存储消息列表Looper&#xff1a;查询MessageQueue是否有新消息&#xff0c;有则处理&#xff0c;无则等待ThreadLocal&#xff1a;用于Handler获取当前线程的…

TypeScript配置-- 1. 新手处理TS文件红色波浪线的几种方式

Typescript 规范化了JS的项目开发&#xff0c;但是对一些项目的一些新手来说&#xff0c;确实是不怎么优好&#xff0c;譬如我&#xff1a;将我之前珍藏的封装JS代码&#xff0c;拿进了配置了tsconfig.json的vue3项目&#xff0c;在vscode下&#xff0c;出现了满屏的红色 &…

UML四大关系

文章目录 引言UML的定义和作用UML四大关系的重要性和应用场景关联关系继承关系聚合关系组合关系 UML四大关系的进一步讨论UML四大关系的实际应用软件开发中的应用其他领域的应用 总结 引言 在软件开发中&#xff0c;统一建模语言&#xff08;Unified Modeling Language&#x…

如何在Spring Boot应用中使用Nacos实现动态更新数据源

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库

通过DMA方式输出PWM模拟LED数据信号 优点&#xff1a;不消耗CPU资源 缺点&#xff1a;占用内存较大 STM32CUBEMX配置 定时器配置 定时器通道&#xff1a;TIM3 CH2 分频&#xff1a;0 重装值&#xff1a;79&#xff0c;芯片主频64Mhz&#xff0c;因此PWM输出频率&#xff1a…

(超简单)将图片转换为ASCII字符图像

将一张图片转换为ASCII字符图像 原图&#xff1a; 效果图&#xff1a; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileWriter; import java.io.IOException;public class ImageToASCII {/*** 将图片转换为A…

c#继承(new base)的使用

概述 C#中的继承是面向对象编程的重要概念之一&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;从另一个类&#xff08;称为父类或基类&#xff09;继承属性和行为。 继承的主要目的是实现代码重用和层次化的组织。子类可以继承父类的字段、属性、方法和事…