数据结构进阶篇,链表专题(二)

本文介绍了三种链表操作的解题思路:1) 旋转链表,将链表每个节点向右移动指定位置;2) 反转链表的指定区间;3) 交换链表中正数第k个节点与倒数第k个节点的值。通过示例和解题步骤,展示了如何使用一次或两次遍历来高效地完成这些操作,时间复杂度均为O(n),空间复杂度为O(1)。
摘要由CSDN通过智能技术生成

61. 旋转链表

  「题目:」

  给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

  「示例:」

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

  「解题思路:」

  记链表的长度为 n,如果 k 大于等于 n 时,只需要向右移动 k % n 次即可,因为每 n 次移动,链表会恢复原状。

f8457742f3e45f60462aded73f4c40c0.png

  由上图可知(链表题型画完图,能很清晰地梳理出节点之间的引用变换):

  • 遍历链表,获取到链表的长度,那么分割点即为 length - (k % length),这里需要将前置节点的 next 设置为 null,所以分割点的位置需要向前移动一位

  • 遍历到链表的尾节点,将其指向旧链表的头节点。

  一定要舍得使用临时变量来管理链表节点的引用。

  时间复杂度:O(n),空间复杂度:O(1)。

const rotateRight = (head, k) => {

  if (!head || !head.next) {
    return head;
  }

  let length = 0;
  let currentHead = head;
  while (currentHead) {
    length++;
    currentHead = currentHead.next;
  }

  let index = length - (k % length) - 1;

  currentHead = head;
  while (index) {
    currentHead = currentHead.next;
    index--;
  }

  let lastHead = currentHead;
  while (lastHead.next) {
    lastHead = lastHead.next;
  }

  lastHead.next = head;
  const ans = currentHead.next;
  currentHead.next = null;

  return ans;
}

92. 反转链表 II

  「题目:」

  给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表。

  「示例:」

  输入:head = [1,2,3,4,5], left = 2, right = 4, 输出:[1,4,3,2,5].

  「第一种解题思路:先将需要翻转的区间剥离成单独的链表,并且保存该链表的前驱节点和后继节点,然后对其进行翻转操作,最后将前驱节点和后继节点关联上。」

f6ffa9cf61d76121ee46185f58fa3ce4.png

  时间复杂度:O(n),空间复杂度:O(1)。

const reverseBetween = (head, m, n) => {
  const dummyHead = new ListNode(null);
  dummyHead.next = head;

  let preHead = dummyHead;

  for (let i = 0; i < m - 1; i++) {
    preHead = preHead.next;
  }

  let rightHead = preHead;
  for (let i = 0; i < n - m + 1; i++) {
    rightHead = rightHead.next;
  }

  let leftHead = preHead.next;
  let sufHead = rightHead.next;

  preHead.next = null;
  rightHead.next = null;
  
  reverseLinkedList(leftHead);

  preHead.next = rightHead;
  leftHead.next = sufHead;

  return dummyHead.next;
}

function reverseLinkedList(head) {
  let pre = null;
  let current = head;

  while (current) {
    const next = current.next;
    current.next = pre;
    pre = current;
    current = next;
  }
}

  「第二种解题思路:上述方法的缺点主要在于两次遍历链表,接下来通过在反转区间中对每一个遍历的元素依次反转即可优化。」

41b0a6b974c540fc072574fe4e8c4236.png

  「在遍历的过程,不断维护 preHead、current 和 next 这三个节点之间的指向关系。」

  时间复杂度:O(n),空间复杂度:O(1)。

const reverseBetween = (head, m, n) => {
    const dummyHead = new ListNode(null);
    dummyHead.next = head;

    let preHead = dummyHead;
    for (let i = 0; i < m - 1; i++) {
        preHead = preHead.next;
    }

    let current = preHead.next;
    let next = null;

    for (let i = 0; i < n - m; i++) {
        next = current.next;
        current.next = next.next;
        next.next = preHead.next;
        preHead.next = next;
    }

    return dummyHead.next;
}

1721. 交换链表中的节点

  「题目:」

  给你链表的头节点 head 和一个整数 k 。

  交换 链表正数第 k 个节点和倒数第 k 个节点的值后,返回链表的头节点(链表 从 1 开始索引)。

  「示例:」

  输入:head = [1,2,3,4,5], k = 2;输出:[1,4,3,2,5]。

  「第一种解题思路:首先通过一次遍历可以获取到第 k 个节点,并且计算出链表的总长度,这样就可以定位到倒数第 k 个节点,然后再通过一次遍历拿到倒数第 k 个节点,交换两者的值即可。」

  时间复杂度:O(n+k),空间复杂度:O(1)。

const swapNodes = function(head, k) {
    let length = 0;
    let x = null;
    let currentHead = head;
    while (currentHead) {
        length++;
        if (length === k) {
            x = currentHead;
        }
        currentHead = currentHead.next;
    }

    let k2 = length - k;
    let y = null;
    currentHead = head;
    while (k2--) {
        currentHead = currentHead.next;
    }
    y = currentHead;

    const xval = x.val;

    x.val = y.val;
    y.val = xval;

    return head;
};

  「第二种解法:利用双指针技巧可以将上述解法的时间复杂度再次优化,实现真正意义上的一次遍历。」

  时间复杂度:O(n),空间复杂度:O(1)。

const swapNodes = function(head, k) {
    let currentHead1 = head;
    let currentHead2 = head;

    while (--k) {
        currentHead1 = currentHead1.next;
    }

    const x = currentHead1;

    while (currentHead1.next) {
        currentHead1 = currentHead1.next;
        currentHead2 = currentHead2.next;
    }

    const y = currentHead2

    const temp = x.val;
    x.val = y.val;
    y.val = temp;

    return head;
};

写在最后

  「感谢您能耐心地读到这里,如果本文对您有帮助,欢迎点赞、分享、或者关注下方的公众号哟。」

  相关链接:

  • https://leetcode-cn.com/problems/rotate-list/

  • https://leetcode-cn.com/problems/reverse-linked-list-ii/

  • https://leetcode-cn.com/problems/swapping-nodes-in-a-linked-list/

fa70edc4cd007ac094b3f819c31eecaf.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值