文章目录
链表
06. 从尾到头打印链表
题意
- 逆序输出单向链表
解法1 借助栈
逆序输出 --》 先进后出 --》 栈
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
stack<int> st;
vector<int> res;
while(head!=NULL)
{
st.push(head->val);
head=head->next;
}
while(!st.empty())
{
res.push_back(st.top());
st.pop();
}
return res;
}
};
复杂度分析
时间复杂度 O(N):遍历链表;
空间复杂度 O(N):临时存储变量的栈。
解法2 逆序 vector
利用 reverse
函数 逆序 数组。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
stack<int> st;
vector<int> res;
while(head!=NULL)
{
res.push_back(head->val);
head=head->next;
}
reverse(res.begin(), res.end());
return res;
}
};
复杂度分析
时间复杂度 O(N):遍历链表;
空间复杂度 O(N):临时存储变量的 vector。
reverse
:
可以原地反转
vector
/string
/char[]
。
解法3 递归
/**
* 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 {
private:
vector<int> res;
public:
void recur(ListNode* head)
{
if(head == nullptr)
return;
recur(head->next);
res.emplace_back(head->val);
}
vector<int> reverseBookList(ListNode* head) {
recur(head);
return res;
}
};
复杂度分析
时间复杂度:
O
(
N
)
O(N)
O(N),遍历链表;
空间复杂度:
O
(
N
)
O(N)
O(N),递归栈共
n
n
n 层。
24. 反转链表
题意
- 反转链表
解法1 新建反转链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* ans=NULL;
while(head != NULL)
{
if(ans == NULL)
ans = new ListNode(head->val);
else
{
ListNode* tmp = new ListNode(head->val); // new,本质上是对指针做初始化
tmp->next = ans; // 链表指针这里next存的是指针指向的空间地址?对,next本身就是一个指针,所以本质上next的这个内存空间存的是指针指向的空间地址。
ans = tmp;
}
head = head->next;
}
return ans;
}
};
复杂度分析
时间复杂度 O(N):遍历链表;
空间复杂度 O(N):新建链表。
解法2 原地修改
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* ans;
if(head == NULL) return NULL;
ListNode* q = head->next;
ListNode* p = NULL;
while(head!=NULL)
{
q = head->next;
head->next = p;
p = head;
head = q;
}
return p;
}
};
复杂度分析
时间复杂度 O(N):遍历链表;
空间复杂度 O(1):原地修改。
解法3 递归
假设 nk+1 到 nm 都实现了反转,也就是说,现在要反转nk -> nk+1 为 nk <- nk+1,那么只要 nk->next->next=nk; nk->next=nullptr;
而为了得到反转后的链表头 head
,从递归最尾端开始,将 newHead
返回。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr || head->next == nullptr) return head;
ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}
};
复杂度分析
时间复杂度 O(N):对链表的每个节点实现递归;
空间复杂度 O(N):空间复杂度主要取决于递归调用的栈空间,最多为 N 层。
35. 复杂链表的复制
题意
- 复制一个含有
next
和random
的链表。
解答1 利用哈希表实现
因为 random
指向的节点可能是还没复制到的节点,所以 next
和 random
需要分开复制。
- 第一次遍历,复制
val
和next
; - 第二次遍历,复制
random
。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==nullptr) return nullptr;
Node* newHead = new Node(head->val);
Node* cur = newHead, *old = head;
unordered_map<Node*, Node*> mp;
while(old!=nullptr)
{
Node* nxt = nullptr;
if(old->next!=nullptr)
nxt = new Node(old->next->val);
cur->next = nxt;
mp[old] = cur;
cur = cur->next;
old = old->next;
}
mp[nullptr] = nullptr;
cur = newHead;
while(head!=nullptr)
{
cur->random = mp[head->random];
head = head->next;
cur = cur->next;
}
return newHead;
}
};
复杂度分析
时间复杂度 O(N):两次遍历;
空间复杂度 O(N):复制的链表和 map
。
解答2 节点拆分
在每个节点 n 后复制一个节点 n’,然后将链表拆分成原链表和复制后的链表。
但是同样的,next
和 random
需要分两次遍历复制,再加上拆分,一共遍历 三 轮。
ps. 注意 nullptr
的处理。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}
for(Node* node=head; node!=nullptr; node=node->next->next)
{
Node* nodeNew = new Node(node->val);
nodeNew->next = node->next;
node->next = nodeNew;
}
for(Node* node=head; node!=nullptr; node=node->next->next)
{
Node* nodeNew = node->next;
nodeNew->random = node->random == nullptr ? nullptr : node->random->next;
}
Node* headNew = head->next;
for(Node* node=head; node!=nullptr; node=node->next)
{
Node* nodeNew = node->next;
node->next = node->next->next;
nodeNew->next = nodeNew->next == nullptr ? nullptr : nodeNew->next->next;
}
return headNew;
}
};
复杂度分析
时间复杂度 O(N):三次遍历;
空间复杂度 O(N):复制的链表。
解法3 回溯
实际上这样的回溯本质上还是先实现了 next
的复制,然后回溯到了 newHead
进行 random
的复制。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
unordered_map<Node*, Node*> mp;
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}
if(!mp.count(head))
{
Node* headNew = new Node(head->val);
mp[head] = headNew;
headNew->next = copyRandomList(head->next);
headNew->random = copyRandomList(head->random);
}
return mp[head];
}
};
复杂度分析
时间复杂度 O(N):每个节点都要调用两次复制函数 copyRandomList
;
空间复杂度 O(N):复制的链表和哈希表。
总结
A. reverse
:
可以原地反转
vector
/string
/char[]
。
B. 指针与 new
p->next
应当被视为一个指针。
->
是指针用来访问结构体内成员的,所以实际上p->next
表示的是指针p
指向的结构体中的next
成员,而next
成员正是一个指针,因此,p->next
应当被视为一个指针。