1.题目描述
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例2:
输入:head = [1]
输出:[1]
示例3:
输入:head = []
输出:[]
2.题目分析
2.1 迭代法解题
- 这道题就是一个普通的链表节点交换问题,难点在于控制节点交换过程中的
next
指针指向的频繁更新,甚至有时候前一步的改动会对后一步的操作产生决定性的影响,因此我们在实际处理问题时应该时刻注意指针指向的变化所带来的操作上的改变。 - 此外为了避免在指向频繁改动中产生的不可估计的影响,我们通常选择将待指向的新节点预先用临时变量保存,这样后面的改动对这些提前定义的临时变量是没有影响的。
- 接下来我们来看怎么实现解题。我们知道第一部的交换会使得
head
节点位置改变,为了保证操作的顺利和返回值的正确我们选择定义一个虚拟头节点dhead
来辅助操作(虚拟头节点一般会使得链表的操作更加便捷)。
我们选择的交换模型如下图所示,首先令cur工作指针指向第一个1节点,然后根据下面的图示进行指向转换,就完成了节点1和节点2的交换,我们继续讲cur指针向前移动两个位置进行下一轮的交换知道最后只有一个元素或者没有元素时停止。
在实际解题中编码其实是比较容易搞混的,比如上面第一步修改完cur
的next
指针的指向2后我们就会发现节点1的位置难以确定,为了避免这种类似情况的发生我们在每次迭代之前先将三个需要被指向的指针(用作右值)进行临时存储。
ListNode* curn = cur->next;
ListNode* curnn = curn->next;
ListNode* curnnn = curnn->next;
这样我们需要改变方向的时候可以直接使用它们,注意它们不能出现在左边,可以思考思考为什么?
这是因为它们是作为固定临时节点存在的,左边我们进行修改指向的节点都是与当前状态的
cur
指针密切相关的。也就是此时的左值是时刻变化的。
2.2 递归解法
做前面一道题时我们提到递归与链表具有较密切的联系,我们继续尝试利用递归思想来解决这道题。我们知道递归分为递和归两个阶段。首先我们先确定边界条件,也就是当链表为空或者只有一个节点时它就不做任何改变直接返回。如果是有一系列节点[1–>2–>3–>4],这时就要将求解问题拆解,那么怎么拆解呢?根据题意处理节点是成对处理的,所以我们先拆出这个链表最前面的一对来进行处理,此时表结构变成了[2–>1–>3–>4],我们发现这时的1和3之间的顺序出现错误,这正是我们的递归解法解决的部分,一般递归解法的总体结构就是递–>中间处理–>归,其中的递是拆解,归是合并,中间处理就是对继续拆解的子问题的求解,我们继续思考前面的问题,1–>2的顺序确定之后,后面的顺序如果没确定就会导致顺序错乱,所以我们将1和2的处理也按照递归结构分为三步:
1. 2-->1
2. 处理3和4的交换
3. 1-->4
这样一个递归算法就写出来了,伪代码如下
- if 节点为空或者只有一个节点,则返回head;否则继续如下步骤;
- 定义一个临时节点
newHead
保存当前head->next
的位置 -
head
节点的next
值更新为新一轮递归调用的返回值(后面两个节点交换后的靠前位置节点)(head->next = swapPairs(newHead->next);) - newHead的next 指针指向head节点;
- 返回newHead即为新的头节点
3.题目解答
3.1 迭代
/**
* 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* dhead = new ListNode(0,head);
ListNode* cur = dhead;
while(cur->next != nullptr &&cur->next->next!=nullptr){
ListNode* curn = cur->next;
ListNode* curnn = curn->next;
ListNode* curnnn = curnn->next;
cur->next= curnn;
cur->next->next = curn;
cur->next->next->next = curnnn;
cur = cur->next->next;
}
return dhead->next;
}
};
3.2 递归
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr){
return head;
}
ListNode* newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}