206. 反转链表
方法一:三指针
第一个指针prev
指向前一个结点,第二个指针cur
指向当前结点,第三个指针tmp
指向后一个结点,将cur
的下一个结点修改为prev
,由于修改后就获取不到cur
原先的下一个结点,所以需要用tmp
先保存cur
原先的下一个结点。
思路
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(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* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
else {
ListNode* prev = nullptr;
ListNode* cur = head;
ListNode* tmp;
while (cur != nullptr) {
tmp = cur->next;
cur->next = prev;
prev = cur;
cur = tmp;
}
return prev;
}
}
};
方法二:递归
思路
其实本质上的思路与方法一一样,只是使用了递归的写法。若cur
不为空指针则不断的翻转。
- 时间复杂度: O ( n ) O(n) O(n)。
- 空间复杂度: O ( n ) O(n) O(n),递归要使用n层栈空间。
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* reverseList(ListNode* head) {
return this->reverse(nullptr, head);
}
ListNode* reverse(ListNode* prev, ListNode*cur) {
if (cur == nullptr) return prev;
ListNode* tmp = cur->next;
cur->next = prev;
return reverse(cur, tmp);
}
};
看完讲解的思考
关键在于要使用额外的指针来保存原先的下一个结点,因为修改后就获取不到原先的下一个结点了。
代码实现遇到的问题
无
24. 两两交换链表中的节点
方法一:迭代(自己写出来的复杂的版本)
思路
自己的想法:因为每次交换两个结点会影响四个位置的结点,即交换的两个结点以及两个结点的前后结点,所以设置了四个指针对应这四个结点,然后更改连接顺序。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(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 == nullptr || head->next == nullptr) return head;
ListNode* node1 = nullptr;
ListNode* node2 = head;
ListNode* node3 = head->next;
ListNode* node4 = head->next->next;
head = node3;
while (node3 != nullptr) {
node3->next = node2;
node2->next = node4;
if (node1 != nullptr) node1->next = node3;
node1 = node2;
node2 = node4;
node3 = node4 == nullptr ? nullptr : node4->next;
node4 = node3 == nullptr ? nullptr : node3->next;
}
return head;
}
};
方法二:迭代(简化版)
思路
自己想的方法实际上复杂了,不用设置4个指针来操作,只用设置3个指针即可。
首先设置一个虚拟头结点,方便后续的操作。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(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) {
ListNode* dummyNode = new ListNode(0, head);
ListNode* cur = dummyNode;
while (cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp1 = cur->next;
ListNode* tmp2 = cur->next->next;
cur->next = tmp2;
tmp1->next = tmp2->next;
tmp2->next = tmp1;
cur = cur->next->next;
}
return dummyNode->next;
}
};
方法三:递归
思路
每一层递归先将每一对结点交换位置,然后连接上下一对结点,连接的位置由下一层递归的结果决定,直到剩余结点不足一对时结束递归。文字较难描述,结合代码画图易懂。
- 时间复杂度: O ( n ) O(n) O(n)。
- 空间复杂度: O ( n ) O(n) O(n),递归需要n层栈空间。
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 == nullptr || head->next == nullptr) return head;
ListNode* node1 = head;
ListNode* node2 = node1->next;
ListNode* node3 = node2->next;
node2->next = node1;
node1->next = this->swapPairs(node3);
return node2;
}
};
看完讲解的思考
交换链表位置的题目一般都需要设置一些临时指针来记录位置,防止修改完结点的指针后获取不到原先的下一个结点。
涉及操作链表的题目一定要考虑到可不可以使用虚拟头结点简化逻辑。
代码实现遇到的问题
无
19. 删除链表的倒数第N个节点
方法一:两次遍历
思路
第一次遍历获取链表长度,第二次删除结点。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(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* removeNthFromEnd(ListNode* head, int n) {
int size = this->getSize(head);
ListNode* dummyNode = new ListNode(0, head);
ListNode* cur = dummyNode;
while (n++ < size) cur = cur->next;
ListNode* delNode = cur->next;
cur->next = delNode->next;
delete delNode;
delNode = nullptr;
head = dummyNode->next;
delete dummyNode;
dummyNode = nullptr;
return head;
}
int getSize(ListNode* head) {
// 获取数组的长度
int size = 0;
while (head != nullptr) {
++size;
head = head->next;
}
return size;
}
};
方法二:快慢双指针
思路
快指针先走n步,然后再快慢指针一起走直到快指针走到终点,此时慢指针所在位置就是倒数第n个。方法一和二实际访问链表结点次数是一样多的,只是方法一看起来更粗暴,方法二看起来更优雅。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(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* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0, head);
ListNode* slow = dummyNode;
ListNode* fast = dummyNode;
// 寻找倒数第n个结点的前一个结点
++n;
while (n--) fast = fast->next;
while (fast != nullptr) {
slow = slow->next;
fast = fast->next;
}
// 删除倒数第n个结点
ListNode* delNode = slow->next;
slow->next = delNode->next;
delete delNode;
delNode = nullptr;
// 释放虚拟头结点内存空间
head = dummyNode->next;
delete dummyNode;
dummyNode = nullptr;
return head;
}
};
看完讲解的思考
连着做几道链表的题目,又忘记考虑数组题目的解题方法了,双指针很常用的方法也没有想到。
代码实现遇到的问题
无
最后的碎碎念
今日刷题较为顺利,但是刷题速度还是有很大提升空间。连续刷题一周达成✌,做题找回以前的感觉了。