【链表】leetcode24. 两两交换链表中的节点(C/C++/Java/Js)

leetcode24. 两两交换链表中的节点

  • 1 题目
  • 2 思路
  • 3 代码
    • 3.1 C++版本(递归+迭代)
    • 3.2 C版本(递归+迭代)
    • 3.3 Java版本(递归+迭代)
    • 3.4 JavaScript版本
  • 4 总结


1 题目

题源链接

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
【链表】leetcode24. 两两交换链表中的节点(C/C++/Java/Js)_第1张图片
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:

输入:head = []
输出:[]
示例 3:

输入:head = [1]
输出:[1]

提示:

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


2 思路

这道题目正常模拟就可以了。

建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。

对虚拟头结点的操作,还不熟悉的话,可以看这篇
链表:没有路创造路–虚拟头结点
接下来就是交换相邻两个元素了,此时一定要画图, 不画图,操作多个指针很容易乱,而且要操作的先后顺序

初始时,cur指向虚拟头结点,然后进行如下三步:
【链表】leetcode24. 两两交换链表中的节点(C/C++/Java/Js)_第2张图片
操作之后,链表如下:
【链表】leetcode24. 两两交换链表中的节点(C/C++/Java/Js)_第3张图片
看这个可能就更直观一些了:
【链表】leetcode24. 两两交换链表中的节点(C/C++/Java/Js)_第4张图片


3 代码

3.1 C++版本(递归+迭代)

递归:画图理解

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //递归终止条件:不需要交换的条件
        if (!head || !head->next)//头结点不存在或当前结点是尾部,不需要交换
            return head;
        //创建一个结点指针保存头结点下一个结点
        ListNode *newHead = head->next;
        //递归处理,将当前head指向更改完的后序链表
        head->next = swapPairs(newHead->next);
        //将新的头结点指向老头结点
        newHead->next = head;
        return newHead;
    }
};

递归真的很爽!!
可以把递归操作想象成超级操作,相信他一定能完成任务。

迭代:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
 class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            ListNode* tmp = cur->next; // 记录临时节点
            ListNode* tmp1 = cur->next->next->next; // 记录临时节点

            cur->next = cur->next->next;    // 步骤一
            cur->next->next = tmp;          // 步骤二
            cur->next->next->next = tmp1;   // 步骤三

            cur = cur->next->next; // cur移动两位,准备下一轮交换
        }
        return dummyHead->next;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

3.2 C版本(递归+迭代)

递归版本:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
//递归版本
struct ListNode* swapPairs(struct ListNode* head){
    //递归结束条件:头节点不存在或头节点的下一个节点不存在。此时不需要交换,直接返回head
    if(!head || !head->next)
        return head;
    //创建一个节点指针类型保存头结点下一个节点
    struct ListNode *newHead = head->next;
    //更改头结点+2位节点后的值,并将头结点的next指针指向这个更改过的list
    head->next = swapPairs(newHead->next);
    //将新的头结点的next指针指向老的头节点
    newHead->next = head;
    return newHead;
}

迭代版本:

//迭代版本
struct ListNode* swapPairs(struct ListNode* head){
    //使用双指针避免使用中间变量
    typedef struct ListNode ListNode;
    ListNode *fakehead = (ListNode *)malloc(sizeof(ListNode));
    fakehead->next = head;
    ListNode* right = fakehead->next;
    ListNode* left = fakehead;
    while(left && right && right->next ){
        left->next = right->next;
        right->next = left->next->next;
        left->next->next = right;
        left = right;
        right = left->next;
    }
    return fakehead->next;
}

3.3 Java版本(递归+迭代)

// 递归版本
class Solution {
    public ListNode swapPairs(ListNode head) {
        // base case 退出提交
        if(head == null || head.next == null) return head;
        // 获取当前节点的下一个节点
        ListNode next = head.next;
        // 进行递归
        ListNode newNode = swapPairs(next.next);
        // 这里进行交换
        next.next = head;
        head.next = newNode;

        return next;
    }
} 

迭代:

// 虚拟头结点
class Solution {
  public ListNode swapPairs(ListNode head) {

    ListNode dummyNode = new ListNode(0);
    dummyNode.next = head;
    ListNode prev = dummyNode;

    while (prev.next != null && prev.next.next != null) {
      ListNode temp = head.next.next; // 缓存 next
      prev.next = head.next;          // 将 prev 的 next 改为 head 的 next
      head.next.next = head;          // 将 head.next(prev.next) 的next,指向 head
      head.next = temp;               // 将head 的 next 接上缓存的temp
      prev = head;                    // 步进1位
      head = head.next;               // 步进1位
    }
    return dummyNode.next;
  }
}

3.4 JavaScript版本

var swapPairs = function (head) {
  let ret = new ListNode(0, head), temp = ret;
  while (temp.next && temp.next.next) {
    let cur = temp.next.next, pre = temp.next;
    pre.next = cur.next;
    cur.next = pre;
    temp.next = cur;
    temp = pre;
  }
  return ret.next;
};

4 总结

本题迭代法本质就是模拟过程,还是基于对链表本身结构的了解,结合画图便不是难点。

然后你会发现当你熟悉递归的写法后,会更爽!
可以把递归操作想象成超级操作,相信他一定能完成任务。
只要确定好终止条件,具体递归的细节不用深究,(当然,可以简单模拟),写熟练后就不需要了。
养成递归思维是很有必要的,建议大家可以多看Carl老师写递归代码,递归三部曲,领悟透了会非常舒服。


关于链表,你该了解这些!

By – Suki 2023/1/9

你可能感兴趣的:(算法练习,LeetCode题解,链表,c语言,c++,数据结构,leetcode)