链表篇——三
反转链表
206. 反转链表
自解
读题之后发现中心思想就是调换原指针-cur和下一个cur-next的方向,使cur->next变成cur->pre,由于这对于每一步都是相同的变化,因此想到可以用迭代的方法——从后往前翻转指针。
代码
/**
* 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* reverseList(ListNode* head) {
if(head == NULL) return NULL;
if(head->next == NULL) return head;
ListNode *last = reverseList(head->next);
head->next->next = head;
head->next= NULL;
return last;
}
};
思想解读:
对于这个单链表,为了将指针域反转,采用迭代——则不断用head->next
来进行。其次,在反转过程中,利用head->next->next = head;head->next= NULL;
表示节点指针域的指向变成相反了。
双指针解法
/**
* 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* reverseList(ListNode* head) {
ListNode *tmp ;// 保存cur的下一个节点
ListNode *pre = NULL;
ListNode *cur = head;
while(cur){
tmp = cur->next;
cur->next = pre;//反转指针
pre = cur;
cur = tmp;
}
return pre;
}
};
思考
说是双指针,因为该种解题方法设计到pre和cur两个指针,通过用cur->next的语句从前往后反转指针。避免了重新定义链表指针的内存浪费行为。
两两交换链表中的节点
24. 两两交换链表中的节点
题解1——虚拟头节点
为了省去每次都对头节点的特殊操作,在这里采用虚拟头节点的操作,对于这类链表转换问题,链表指针的变化是必不可少的,因此采用如下规律解题:
- 首先需要画图,对该流程有一个认识
- 其次操作的先后顺序需要考虑
思路图
代码
//时间复杂度:O(n)
//空间复杂度:O(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 *dummy_head = new ListNode(0);//创建虚拟头节点
dummy_head->next = head;
ListNode *cur = dummy_head;
while(cur->next != NULL && cur->next->next != NULL){
ListNode *tmp = cur->next;//1号节点
ListNode *Node = cur->next->next->next;//3号节点
cur->next = tmp->next;
cur->next->next = tmp;
cur->next->next->next = Node;
cur = cur->next->next;
}
return dummy_head->next;
}
};
误区
- 在进行交换指针的操作时,发现
ListNode *tmp = cur->next;//1号节点
ListNode *Node = cur->next->next->next;//3号节点
这一步是确定原来指针所指向的节点
而在记录新的指针时,必须以虚拟头节点为原点向后衍生——等号的右侧可以是临时节点的变化。
题解2——递归
思路
对于这类都是两两交换的固定步骤,递归莫过于是一个较好的选择
//时间复杂度:O(n)
//空间复杂度:O(n)
/* 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 == nullptr || head->next == nullptr){
return head;
}
ListNode *Newhead = head->next;
head->next = swapPairs(Newhead->next);
Newhead->next = head;
return Newhead;
}
};
思考
- 递归的终止条件是链表中没有节点,或者链表中只有一个节点,此时无法进行交换。
- 循环步骤:果链表中至少有两个节点,则在两两交换链表中的节点之后,原始链表的头节点变成新的链表的第二个节点,原始链表的第二个节点变成新的链表的头节点
思考
- 递归的终止条件是链表中没有节点,或者链表中只有一个节点,此时无法进行交换。
- 循环步骤:果链表中至少有两个节点,则在两两交换链表中的节点之后,原始链表的头节点变成新的链表的第二个节点,原始链表的第二个节点变成新的链表的头节点