LeetCode24、两两交换链表中的节点

80 阅读2分钟

LeetCode 系列记录我学习算法的过程。

持续创作,加速成长!这是我参与「掘金日新计划 6 月更文挑战」的第 23 天,点击查看活动详情

题目

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例:

image.png

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

提示

  • 链表中节点的数目在范围 [0, 100] 内
  • 0 <= Node.val <= 100

思路

这题又是每两项进行重复的操作,那便可以用递归的方法来实现,首先是找到递归结束的条件

即当结点只剩一个,或没有结点时,递归结束,返回自身

定义变量 res 获取当前结点的下一结点

然后当前结点的下一项由递归调用自身函数传入 res.next 获取

再将处理好的当前结点作为 res 的下一结点

最后将 res 返回即可

代码实现

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    // 如果只剩一个结点或没有结点,返回自身
    if (!head || !head.next) return head
    // 变量res获取下一结点
    let res = head.next
    // 当前结点的下一结点由递归调用自身函数获取
    head.next = swapPairs(res.next)
    // res 下一结点为当前结点
    res.next = head
    // 返回 res
    return res
};

image.png

优化

既然可以用递归,那同样也可以使用循环来处理

  • 如果链表只有一项或一项都没有,直接返回自身
  • 定义一个虚拟头结点,让其下一项指向 head
  • 定义 prev 节点指向虚拟头结点,用来链接每次交互后的链表
  • 循环,先对当前项及下一项进行交换
  • 然后通过 prev 将交换后的链表与前面处理好的链表连接起来
  • 移动 headprev,以便进行下一次交换
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    if (!head || !head.next) return head
    
    // 虚拟头节点,初始的时候让虚拟头节点下一项指向head
    const res = new ListNode(0, head) 
    // prev指针
    let prev = res 

    while (head && head.next) {
        const next = head.next // 保存head的下一项

        // 两两交换
        head.next = next.next 
        next.next = head 

        // 将交换后的链表与前面的链表链接起来
        prev.next = next 

        // 移动 prev 及 head 以便进行下一次交换
        prev = head
        head = head.next
    }

    return res.next
};