24. 两两交换链表中的节点
题目描述
![e63a9eb3c3d96f9ccaa03739fb9fa258.png](https://i-blog.csdnimg.cn/blog_migrate/5bee33ac9ac38d6563c179e2f39cdbbd.png)
解法1 遍历
思路:
如果将每两个要交换的结点看作一组,那么这一组的结点交换过程需要三个指针,分别指向这一组的两个结点以及上一组的后一个结点。完成该组的结点交换后,将三个指针各自后移两位,则可以进行下一组结点的交换。如此用这三个指针遍历链表,即可完成所有组的结点交换。
实现细节:
设三个指针分别为pre
,first
和second
。其中pre
指向的是在本组结点之前的一个结点,first
和second
分别指向本组内的前后两结点。为方便起见,在初始状态下我们可以在原链表的头结点之前插入一个新的头结点,并将pre
指针指向这个新的头结点,将first
和second
指针指向原链表的前两个结点,这样就可以从头开始交换结点的循环过程。
在循环体内,首先是要交换first
指针指向结点和second
指针指向结点的位置(交换的是结点)。但由于在此过程中两个指针本身指向的位置也发生了变化,因此我们还需要再将两个指针指向的位置交换回来(交换的是指针)。最后要将三个指针分别后移两位,以进行下一组的交换。
代码:
class Solution {public: ListNode* swapPairs(ListNode* head) { if(!head) return NULL; //增加新的头结点 ListNode* newHead = new ListNode(-1); newHead->next = head; ListNode* pre = newHead; ListNode* first = head; ListNode* second = head->next; while(second) { //交换first指针指向的结点与second指针指向的结点的位置 first->next = second->next; pre->next = second; second->next = first; //交换first指针与second指针的位置 ListNode* t = first; first = second; second = t; //将三个指针分别后移两位 pre = pre->next->next; first = first->next->next; if(first) second = second->next->next; else break; } head = newHead->next; delete(newHead); return head; }};
复杂度分析:
设链表的结点数量为。
- 时间复杂度:本算法就是对链表的一次顺序的遍历过程,因此时间复杂度为。
- 空间复杂度:本算法除了增加一个新的头结点以及使用了三个指针外没有额外占用空间,因此空间复杂度为。
解法2 递归
思路:
还是将每两个要交换的结点看作一组。在上一个解法中我们在每组的交换过程中要利用到指向本组之前一个结点的指针,这是因为上一个解法是从前向后顺序的遍历过程,由于在当前组的结点交换过程中后边的链表部分还没有完成交换,因此在之后下一组的结点交换过程中还需再对这组交换后的尾部指针指向的位置做调整。而如果我们选择从后往前逆向解决每个组的子问题,那么在当前组的交换过程中就可以直接让调整后处于后面的结点接到下一组之前,这时下一组的结点已经是交换好的了。因此可以采用递归的方式逆向解决问题。
first
和second
表示每次要交换的两个结点。从链表的头结点head
开始递归,下一次递归传递的是下一组需要交换的结点,如果链表中剩余结点不足一组或无剩余结点则结束递归。在交换两个结点后需要返回second
,因为它是本组交换后在前面的结点,也是当前以本组开头的子链表的头结点。
代码:
class Solution {public: ListNode* swapPairs(ListNode* head) { if(!head || !head->next) return head; ListNode* first = head; ListNode* second = head->next; first->next = swapPairs(second->next); second->next = first; return second; }};
复杂度分析:
设链表的结点数量为。
- 时间复杂度:本算法相当于对链表进行逆向的遍历,因此时间复杂度为。
- 空间复杂度:由于递归过程会使用到堆栈空间,因此空间复杂度为。