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

代码随想录之双指针刷题总结

目录

1.移除元素

2.反转字符串

3.替换数字

4.翻转字符串里的单词

5.翻转链表

6.删除链表的倒数第N个节点

7.链表相交.

8.环形链表||

9.三数之和

10.四数之和


该文是对前面的算法题中用到的双指针方法的题目进行罗列归纳

1.移除元素

题目描述:

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

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

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

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

思路:

数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。

因此使用快慢指针(从下标0开始),遇到目标数字快指针动,慢指针先暂停,没遇到就一起

注意:

明确指针的起始和移动

代码:

class Solution {
public:
	int remove(vector<int>& nums, int val) {
		int slowindex = 0;
		int fastindex = 0;
		for (int i = 0; i < nums.size(); i++) {
			if (nums[i] != val) {
				nums[slowindex++] = nums[fastindex++];
			}
			else {
				fastindex++;
			}
		}
		return slowindex;
	}
};

2.反转字符串

题目描述:

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

思路:

左右指针,分别在两端,不断地交换即可

注意:

注意终止条件,也就是i<s.size()即可

代码:

class Solution {
	void reverseString(vector<char>& s) {
		int start = 0;
		int end = s.size() - 1;
		for (int i = start, j = end; i < s.size() / 2; i++, j--) {
			swap(s[i], s[j]);
		}
	}
};

3.替换数字

题目描述:

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为love。

例如,对于输入字符串 "a1b2",函数应该将其转换为 "aloveblove"。

对于输入字符串 "a5b",函数应该将其转换为 "aloveb"

输入:一个字符串 s,s 仅包含小写字母和数字字符。

输出:打印一个新的字符串,其中每个数字字符都被替换为了number

样例输入:a1b2c3

样例输出:alovebloveclove

思路:

双指针法,一个指向旧的还没扩充的末尾,一个指向新的扩充后的末尾,检测到数字进行填充即可

注意:

对数字的范围判定要写正确,不然会出错。

代码:

int main() {
	string s;
	while (cin >> s) {
		int count = 0;
		int oldindex = s.size() - 1;
		for (int i = 0; i < s.size(); i++) {
			if (s[i] >= '0' && s[i] <= '9') {
				count++;
			}
		}
		s.resize(s.size() + 3 * count);
		int newindex = s.size() - 1;
		//这里也可以写为
		//while (oldindex >= 0);
		for (int i = 0; i <=oldindex; i++) {
			if (s[oldindex] >= '0' && s[oldindex] <= '9') {
				s[newindex--] = 'e';
				s[newindex--] = 'v';
				s[newindex--] = 'o';
				s[newindex--] = 'l';
			}
			else {
				s[newindex--] = s[oldindex];
			}
			oldindex--;
		}
		cout << s << endl;
	}
}

4.翻转字符串里的单词

题目描述:

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:
输入: "the sky is blue"
输出: "blue is sky the"

示例 2:
输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:
输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个

思路:

分为两个步骤进行:

1.先对多余的空格进行去掉操作

2.将元素进行反转交换

注意:

对去空逻辑的处理要理解透彻,不要写错

去空和反转的顺序要搞清楚,反转的区间要搞清

代码:

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 removeExtra(string& s) {
		int slow = 0;
		for (int i = 0; i < s.size(); ++i) {
			//错误写法
			//if (oldindex != 0) s[oldindex++] = ' ';
			//while (s[i] != ' ') {
			//	if (s[i] != ' ' && oldindex != 0) {
			//		s[oldindex] = s[i];
			//	}
			//	oldindex++;
			//}
			if (s[i] != ' ') {
				if (slow != 0) s[slow++] = ' ';
				while (i < s.size() && s[i] != ' ') {
					s[slow++] = s[i++];
				}
			}
		}
		//移除空格之后,数组的大小记得重新恢复一下
		s.resize(slow);
	}
	string reversewords(string& s) {
		removeExtra(s);
		//根据下标翻转
		Reverse(s, 0, s.size() - 1);
		//赋值这个过程的顺序不要反了,是去除多余空格后的s
		int start = 0;
		int end = s.size();
		for (int i = 0; i <= s.size(); ++i) {
			if (s[i] == ' ' || i == end) {
				Reverse(s, start, i - 1);
				start = i + 1;
			}
		}
		return s;
	}
};

5.翻转链表

题目描述:

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

思路:

定义两个指针pre和cur,分别代表NULL,以及头节点,然后不断差序更新

注意:

记得提前保存好cur的下一个节点,因为cur指向改变了

代码:

class Solution {
public:
	ListNode* reverseList(ListNode* head) {
		ListNode* cur = head;
		ListNode* pre = NULL;
		while (cur) {
			//此时记得要保存一下cur的下一个节点,因为接下来要改变指向
			ListNode* temp = cur->next;
			cur->next = pre;
			pre = cur;
			cur = temp;
		}
		return head;
}
};

6.删除链表的倒数第N个节点

题目描述:

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

19.删除链表的倒数第N个节点

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1 输出:[]

示例 3:

输入:head = [1,2], n = 1 输出:[1]

思路:

定义fast指针和slow指针,初始值为虚拟头结点

fast指针先移动n步

快慢指针一起移动,直到慢指针移动到要删除的节点的前一个节点(这里是fast->NULL的时候)

注意:

注意内存的释放以及节点的处理

代码:

class Solution {
public:
	ListNode* deleteList(ListNode* head, int n) {
		ListNode* dummyhead = new ListNode(0);
		dummyhead->next = head;
		ListNode* slowindex = dummyhead;
		ListNode* fastindex = dummyhead;
		while (n--) {
			fastindex = fastindex->next;
		}
		while (fastindex->next != NULL) {
			fastindex = fastindex->next;
			slowindex = slowindex->next;
		}
		ListNode* temp = slowindex->next;
		slowindex->next = slowindex->next->next;
		delete temp;
		temp = nullptr;
		return dummyhead->next;
	}
};

7.链表相交.

题目描述:

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

示例 2:

示例 3:

思路:

求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置

然后curA和curB同时移动,直到curA=curB,或者未找到结果为NULL

注意:

比较的时候节点记得重新设置一下,因为指向改变了

代码:

class Solution {
public:
	ListNode* listXiangjiao(ListNode* headA, ListNode* headB) {
		int countA = 0;
		int countB = 0;
		ListNode* curA = headA;
		ListNode* curB = headB;
		while (curA != NULL) {
			countA++;
			curA = curA->next;
		}
		while (curB != NULL) {
			countB++;
			curB = curB->next;
		}
		if (countB - countA > 0) {
			swap(countA, countB);
			swap(headA, headB);
		}
		int cha = 0;
		cha = countA - countB;
		//确实需要重新设置
		ListNode* newcurA = headA;
		ListNode* newcurB = headB;
		while (cha--) {
			newcurA = newcurA->next;
		}
		while (newcurA != NULL && newcurB != NULL) {
			if (newcurA == newcurB) {
				return newcurA;
			}
			//不要忘记更新哦,不断地更新寻找
			newcurA = newcurA->next;
			newcurB = newcurB->next;
		}
		return NULL;
	}
};

8.环形链表||

题目描述:

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

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

循环链表

思路:

首先需要判断环形链表,也就是定义快慢指针,快二慢一,然后若相等就有环

若有环,那么快慢指针相交的地方,以及head节点同时移动,相等的地方就是环的开始位置

注意:

注意while循环条件的限定,理解fast指针的含义

代码:

class Solution {
	ListNode* huanxing(ListNode* head) {
		ListNode* dummyhead = new ListNode(0);
		dummyhead->next = head;
		ListNode* slowindex = dummyhead->next;
		ListNode* fastindex = dummyhead->next;
		//while(1)循环是错误的,下面是正确的写法
		while (fastindex!=NULL&&fastindex->next!=NULL) {
			slowindex = slowindex->next;
			fastindex = fastindex->next->next;
			if (fastindex == slowindex) {
				ListNode* beginindex = head;
				while (beginindex != slowindex) {
					beginindex = beginindex->next;
					slowindex = slowindex->next;
				}
				return beginindex;
			}
		}
		return NULL;
	}
};

9.三数之和

题目描述:

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

思路:

首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

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

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

注意:

对三个元素的去重与剪枝处理要很明确,尤其是对nums的处理

在对左右指针处理的时候,最后一定要记得更新!!!!!!!

代码:

class Solution {
public:
	vector<vector<int>> threeNum(vector<int>nums) {
		vector<vector<int>> result;
		sort(nums.begin(), nums.end());
		for (int i = 0; i < nums.size(); i++) {
			if (nums[i] > 0) break;
			if (i > 0 && nums[i] == nums[i - 1]) continue;
			int leftindex = i + 1;
			int rightindex = nums.size() - 1;
			while (leftindex < rightindex) {
				if (nums[i] + nums[leftindex] + nums[rightindex] < 0) leftindex++;
				else if (nums[i] + nums[leftindex] + nums[rightindex] > 0) rightindex--;
				else {
					result.push_back(vector<int>{nums[i], nums[leftindex], nums[rightindex]});
					while (leftindex < rightindex && nums[leftindex] == nums[leftindex + 1]) leftindex++;
					while (leftindex < rightindex && nums[rightindex] == nums[rightindex - 1]) rightindex--;

					//千万记得更新
					leftindex++;
					rightindex--;
				}
			}
		}
		return result;
	}
};

10.四数之和

题目描述:

题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]

思路:

和三数之和类似,只不过多了一个nums[j]元素,而且要进行两次剪枝处理

注意:

left一定是仅仅跟在第二个数组元素后面的,这个不要搞错,同时剪枝处理不能少,要深刻理解

代码:

class Solution {
public:
	vector<vector<int>> fourSum(vector<int> nums, int target) {
		vector<vector<int>> result;
		sort(nums.begin(), nums.end());
		for (int i = 0; i < nums.size(); i++) {
			if (nums[i] > 0 && nums[i] >= target) break;
			if (i > 0 && nums[i] == nums[i - 1]) continue;
			for (int j = i + 1; j < nums.size(); j++) {
				if (nums[i] + nums[j] > 0 && nums[i] + nums[j] >= target) break;
				if (j > i+1 && nums[j] == nums[j - 1]) continue;
				int leftindex = j + 1;
				int rightindex = nums.size() - 1;
				while (leftindex < rightindex) {
					//此处应该加一个long,不然会溢出(整型溢出)
					if ((long)nums[i] + nums[j] + nums[leftindex] + nums[rightindex] < target) leftindex++;
					else if (nums[i] + nums[j] + nums[leftindex] + nums[rightindex] > target) rightindex--;
					else {
						result.push_back(vector<int>{nums[i], nums[j], nums[leftindex], nums[rightindex]});
						while (leftindex < rightindex && nums[leftindex] == nums[leftindex + 1]) leftindex++;
						while (leftindex < rightindex && nums[rightindex] == nums[rightindex - 1]) rightindex--;
						leftindex++;
						rightindex--;
					}
				}
			}
		}
		return result;
	}
};


http://www.kler.cn/a/386233.html

相关文章:

  • LeetCode105.从前序与中序遍历构造二叉树
  • 浅谈“通感一体”
  • 人力资源招聘系统的革新之路:从传统到智能的转变
  • SQL 注入详解:原理、危害与防范措施
  • Vue3.js - 一文看懂Vuex
  • Rocky、Almalinux、CentOS、Ubuntu和Debian系统初始化脚本v9版
  • wordpress判断page页与非page页
  • 【图论】图的C++实现代码
  • Python小白学习教程从入门到入坑------第二十八课 文件基础操作文件读写(语法进阶)
  • 【AIGC】如何通过ChatGPT轻松制作个性化GPTs应用
  • java后台生成模拟聊天截图并返回给前端
  • MySql中索引为什么用B+树,他有什么特点?时间复杂度是多少?能存多少数据?是不是只能三层?他与B-树有什么不同?还有其它的树你是是否知道?
  • AIPPT项目(提供完整API接入支持套壳)成熟产品线上运营
  • MySQL常用订单表复杂查询15例
  • 找工作就上:万码优才!海量技术岗位等你来
  • PKG_CHECK_MODULES(FUSE,fuse)
  • 「实战应用」如何用图表控件LightningChart .NET在WPF中制作表格?(一)
  • 关于 CSS 常用布局及特点作用
  • 微信小程序-事件总线
  • BP 神经网络模型:原理、实现与应用
  • GFPS技术原理(二)-模型注册和配置
  • react中的组件传参
  • pythons工具——图像的随机增强变换(只是变换了图像,可用于分类训练数据的增强)
  • Elasticsearch 实战应用详解!
  • Vue3+exceljs+file-saver 实现将表格数据中包含图片导出Excel
  • 算法