两两交换链表中的节点-递归及迭代实现
题目链接:两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4] 输出:[2,1,4,3]
示例 2:
输入:head = [] 输出:[]
示例 3:
输入:head = [1] 输出:[1]
1.递归实现
STEP1:重复子问题,及递归函数的设计
我们要相信swapPairs
函数一定能帮助我们完成链表两两交换的工作,且一定能返回交换后子链表的头结点。
于是代码如下:
ListNode* Next = head->next;
ListNode* ret = swapPairs(Next->next); // 函数传参传两个节点的后面那个节点
STEP2:只关心当前子问题做的事情
“只关心”建立在递归函数完成了工作,子问题应该做如下工作:
head->next = ret;
Next->next = head;
STEP3:递归终止条件
没有元素的话,递归终止;只有一个元素的话,递归也终止,返回当前头结点。有两个元素递归才继续。
if(head == nullptr || head->next == nullptr) return head;
最终代码实现如下:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr)
return head;
ListNode* Next = head->next;
ListNode* ret = swapPairs(Next->next);
head->next = ret;
Next->next = head;
return Next;
}
};
时间复杂度: O(n)。每一层函数栈帧,都需要对节点进行更新指针,这个操作是常数次,如果n层栈帧,就是操作 n 的常数倍次,总体算来时间复杂度是O(n)。
空间复杂度: O(n)。递归调用函数会消耗栈空间,消耗栈空间的大小和递归调用函数的深度有关,当递归调用结束时,swapPairs
函数最多被调用 n 的常数倍次。
2.迭代实现
step1:首先定义一个哨兵位的头结点prehead
,cur从prehead
开始走,及主要记录一下图示的temp1和temp2,不然我后面链接的时候找不到节点就很难受。
// 定义哨兵位头结点
ListNode* prehead = new ListNode(-1);
prehead->next = head;
// 记录cur,temp1,temp2
ListNode* cur = prehead, *temp1 = cur->next, *temp2 = temp1->next->next;
step2:让cur指向2号节点;再让2号节点指向1号节点;最后1号节点指向3号节点
cur->next = temp1->next; // -1 --> 2
cur->next->next = temp1; // 2 --> 1
temp1->next = temp2; // 1 --> 3
step3:cur,temp1,temp2的偏移:cur一定要在待处理的两个节点的前一个位置;temp1在待处理节点的第一个节点;temp2在待处理的两个节点的后一个节点。
cur = temp1;
temp1 = temp2;
temp2 = temp1->next->next;
step4:循环条件的判断:注意到cur一定是要处理的两个节点的前一个节点,cur->next
为空说明没有节点要处理;cur->next->next
为空说明只有一个节点,不需要处理。
最终执行代码如下:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
// 定义哨兵位头结点
ListNode* prehead = new ListNode(-1);
prehead->next = head;
ListNode* cur = prehead;
while(cur->next && cur->next->next)
{
// 记录cur,temp1,temp2
ListNode* temp1 = cur->next, *temp2 = temp1->next->next;
// 注意:temp1和temp2的定义最好写在while循环内部,后面就不会需要额外
// 去更新它们,这样能经过一次循环的判断,不会造成空指针访问的错误。
// 节点指针的更新
cur->next = temp1->next;
cur->next->next = temp1;
temp1->next = temp2;
// 更新节点位置
cur = temp1;
}
return prehead->next;
}
};
时间复杂度: O(n)。只需要对每个节点进行更新指针的操作。
空间复杂度: O(1)。只开辟了哨兵为头结点和其他若干个变量的空间。