快慢指针(双指针)
1、环形链表
题目地址:https://leetcode-cn.com/problems/linked-list-cycle/
分析:题中如果存在环的话,我们可以两个指针,快指针(fast)每次移动两步,慢指针每次移动一步,那么在存在环的情况下他们总会相遇,如果不存在环的情况我们只需要每次判断fast.next和fast.next.next是否存在即可,不存在则返回false.
code:
var hasCycle = function(head) {
if(!head) return false // 头结点为空
let fast = head,slow = head
while(fast.next&&fast.next.next){// 若为null说明不存在环
fast = fast.next.next
slow = slow.next
if(fast===slow){ // 快、慢指针相遇
return true
}
}
return false
};
时间复杂度O(n),空间复杂度O(1)
方法二:标记法(这里不多作分析)
var hasCycle = function(head) {
while(head) {
if(head.flag) return true
head.flag = true
head = head.next
}
return false
};
时间复杂度O(n),空间复杂度O(n)
2、寻找链表的入环节点
LeetCode142.环形链表II
题目地址:https://leetcode-cn.com/problems/linked-list-cycle-ii/
分析:这道题与找环相似,但这题需要找入口,解决思路是处理方法和题一一样,但在快指针与慢指针相遇后,让慢指针回到原点,然后快、慢指针每次均移动一步,在他们相遇之时即是环的入口。
为什么会这样呢?
code:
var detectCycle = function(head) {
if(!head) return null
let fast = head, slow = head
while(fast.next&&fast.next.next){
fast = fast.next.next
slow = slow.next
if(fast===slow){
slow = head
while(fast!==slow){
fast = fast.next
slow = slow.next
}
return slow
}
}
return null
};
3、寻找重复数
Leetcode287.寻找重复数
分析:这道题看似和快慢指针没关系,但是我们可以利用寻找链表的入环节点的思想, 把数组当成对链表的一种描述, 数组里的每一个元素的值表示链表的下一个节点的索引
如示例1中的[1, 3, 4, 2, 2]
把数组索引为0的元素当成链表的头节点
索引为0的元素的值为1, 表示头节点的下一个节点的索引为1, 即数组中的3
再下一个节点的索引为3, 即为第一个2
再下一个节点的索引为2, 即为4
再下一个节点的索引为4, 即为第二个2
再下一个节点的索引为2, 即为4
此时形成了一个环
而形成环的原因是下一节点的索引一致, 即为重复的数字
code:
var findDuplicate = function(nums) {
let slowPointer = 0
let fastPointer = 0
// 应为这里一定会有重复的数,即一定存在“环”,不需做越界判断
while (true) {
slowPointer = nums[slowPointer]
fastPointer = nums[nums[fastPointer]]
if (slowPointer == fastPointer) {
let _slowPointer = 0
while (nums[_slowPointer] !== nums[slowPointer]) {
slowPointer = nums[slowPointer]
_slowPointer = nums[_slowPointer]
}
return nums[_slowPointer]
}
}
};
4、删除链表倒数第k个节点
分析:
方便处理头节点, 我们创建dummy虚拟头节点
让快指针和慢指针最开始都指向dummy节点
让快指针向前移动N + 1个节点, 慢指针保持原地不动
然后两个指针以同样的速度直至快指针移动至null
此时慢指针移动到的位置即为被删除的指针前面的一个指针
code:
var removeNthFromEnd = function(head, n) {
const dummy = new ListNode(null)
dummy.next = head
let slowPointer = dummy
let fastPointer = dummy
while (n-- > -1) {
fastPointer = fastPointer.next
}
while (fastPointer !== null) {
slowPointer = slowPointer.next
fastPointer = fastPointer.next
}
slowPointer.next = slowPointer.next.next
return slowPointer === dummy ? slowPointer.next : head
};
5、链表的中间节点
code:
var middleNode = function(head) {
let fastPoint = head
let slowPoint = head
while(fastPoint&&fastPoint.next){
fastPoint = fastPoint.next.next
slowPoint = slowPoint.next
}
return slowPoint
};
6、回文链表
慢指针依次访问下一个节点, 并翻转链表
快指针依次访问下下一个节点, 直到快指针没有下一个节点(奇数链表)或者快指针指向null(偶数链表), 此时已完成了前半截链表的翻转
依次比较前半截链表和后半截链表节点的值
code:
var isPalindrome = function(head) {
if(head==null||head.next==null){
return true
}else{
let slowPoint = head
let fastPoint = head
let _head = null
let temp = null
// 找到中间节点, 并翻转前半截链表,
// 让_head指向翻转后的前半截链表的头部,
// 让slow指向后半截链表的头节点
while(fastPoint && fastPoint.next){
_head = slowPoint
slowPoint = slowPoint.next
fastPoint = fastPoint.next.next
_head.next = temp
temp = _head
}
// 奇数链表跳过最中间的节点
if (fastPoint) {
slowPoint = slowPoint.next
}
while(_head){
if(_head.val!==slowPoint.val){
return false
}
_head = _head.next
slowPoint = slowPoint.next
}
return true
}
};