LeetCode中快、慢指针问题(javascript)

快慢指针(双指针)

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
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值