概述
文章目录
- 链表中快慢指针
- 1 链表简介
- 2 快慢指针的妙用
- 2.1 找中间值
- 2.2 判断链表中的环
- 2.3 删除倒数第n个节点
- 双指针
- 一、快慢指针的常见算法
- 已知链表中含有环,返回环的起始位置
- 寻找链表的中点
- 寻找链表的倒数第 k 个元素
- 二、左右指针的常用算法
- 1、二分查找
- 2、两数之和
- 3、反转数组
- 4、滑动窗口算法
- 链接
链表中快慢指针
1 链表简介
2 快慢指针的妙用
- 快慢指针就是定义两根指针,
- 移动的速度一快一慢,以此来制造出想要的差值
- 这个差值可让我们找到链表上相应节点
2.1 找中间值
- 一般思路:先遍历一次链表,记录住一共有多少个节点,
- 然后,再次遍历找寻中点。
- 利用快慢指针
- 最开始slow与fast指针都指向链表第一个节点,
- 然后slow每次移动一个指针,fast每次移动两个
2.2 判断链表中的环
- 圆环跑道中,两个人有速度差,迟早两人会相遇,只要相遇那么就说明有环。
- 环上加了额外的两个节点,我们可以想象一下,只要两个指针跑进了环里,那么因为存在速度差,他们之间的距离总会由远及近,然后相遇,在远离。像极了我们人世间某些人在你生命中匆匆而过的感觉。
2.3 删除倒数第n个节点
- 先找出待删除元素前一个元素,也就是第n-1个节点。
- 转化为找链表上的某个节点的问题了,这是快慢指针最擅长的场景。
- 如何找第(n-1)个元素?
- 一开始就让fast指针比slow指针快n+1个元素,接下来,两个指针都是一步一步来往下走。
- 当fast走完时,slow指针就刚刚好停留在第(n-1)个元素上。
- n=2时的情形(dummyHead是手动加上去的虚拟头节点):
双指针
- 双指针技巧分两类,
- 快慢指针,
- 左右指针。
- 前者解决主要解决链表中的问题,
- 如的判定链表中是否包含环;
- 后者主要解决数组(或者字符串)中的问题,
- 比如二分查找。
一、快慢指针的常见算法
已知链表中含有环,返回环的起始位置
ListNode detectCycle(ListNode head) {
ListNode fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
break;
}
slow = head;
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
- 第一次相遇时,slow 走k 步,那么快指针走2k
- fast比 slow 多走k 步
- k就是环的长度
- 因为他们速度差只是1啊!!!
- 设相遇点距环的起点的距离为m,
- 环的起点距头结点 head 的距离为 k - m,
- 也就是说如果从 head 前进 k - m 步就能到达环起点。
- 如果从相遇点继续前进 k - m ,也恰好到达环起点。
- 只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。
寻找链表的中点
ListNode slow, fast;
slow = fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
// slow 就在中间位置
return slow;
- 长度是奇数时,slow 恰巧停在中点位置;
- 偶数,slow 最终的位置是中间偏右:
- 寻找链表中点的一个重要作用是对链表进行归并排序。
回想数组的归并排序:求中点索引递归地把数组二分,最后合并两个有序数组。对于链表,合并两个有序链表是很简单的,难点就在于二分。
但是现在你学会了找到链表的中点,就能实现链表的二分了。关于归并排序的具体内容本文就不具体展开了。
寻找链表的倒数第 k 个元素
- 让快指针先走 k 步,然后快慢指针开始同速前进。
- 这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(为了简化,假设 k 不会超过链表长度):
ListNode slow, fast;
slow = fast = head;
while (k-- > 0)
fast = fast.next;
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;
二、左右指针的常用算法
- 左右指针在数组中实际是指两个索引值,
- 一般初始化left = 0, right = nums.length - 1 。
1、二分查找
前文 二分查找算法详解 有详细讲解,这里只写最简单的二分算法,旨在突出它的双指针特性:
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
}
2、两数之和
直接看一道 LeetCode 题目吧:
只要数组有序,就应该想到双指针技巧。这道题的解法有点类似二分查找,通过调节 left 和 right 可以调整 sum 的大小:
3、反转数组
void reverse(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
// swap(nums[left], nums[right])
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
right–;
}
}
4、滑动窗口算法
这也许是双指针技巧的最高境界了,如果掌握了此算法,可以解决一大类子字符串匹配的问题,不过「滑动窗口」算法比上述的这些算法稍微复杂些。
幸运的是,这类算法是有框架模板的,下篇文章就准备讲解「滑动窗口」算法模板,帮大家秒杀几道 LeetCode 子串匹配的问题。
链接
- https://www.cnblogs.com/kyoner/p/11087755.html
最后
以上就是美好缘分为你收集整理的快慢指针+双指针链表中快慢指针双指针的全部内容,希望文章能够帮你解决快慢指针+双指针链表中快慢指针双指针所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复