前言
欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
今天是五月集训第十三天:双向链表🔥🔥🔥🔥
一、打卡啦
2022.5.13.10:47:
第一题就复盘了俩小时,虽然时间很久但是搞清楚一道题那种开心感是真的不错的,后面的几题对现在我来说还有点难,还得慢慢复盘了。可能感觉后面的训练难度会越来越难,希望自己能尽量做出一题或者两题。😢
2022.5.13.13:29:
今天的第二第三题利用了午休时间过辣,芜湖~😄
2022.5.24.19:29:
24号的题目太难了回过来填坑了😄
二、练习题目
1472. 设计浏览器历史记录
430. 扁平化多级双向链表
剑指 Offer II 028. 展平多级双向链表
剑指 Offer 36. 二叉搜索树与双向链表
三、算法思路
- 1、1472. 设计浏览器历史记录:😄花了2小时搞明白了这一题,模拟题的帮助下懂了双向链表的妙用,链表最头疼要搞清楚指针指向移动,写的时候经常会指到null的地方去。
- 2、430. 扁平化多级双向链表:😄题目的理解不难,而且也知道用啥双向链表加dfs,就是怎么去翻译成代码的问题了。
题目意思如下:now->childFirst->···->childLast->(now->next) 代码思路也是这么写的。 - 3、剑指 Offer II 028. 展平多级双向链表:😄今天买一送一题。
- 4、剑指 Offer 36. 二叉搜索树与双向链表:二叉搜索树转链表,利用二叉搜索树的特性,中序遍历就是一个顺序排列来做。
四、源码剖析
// 1472. 设计浏览器历史记录
class BrowserHistory {
struct Node{
string val;
Node* prev;
Node* next;
}; //(1)
public:
Node *List, *Current;
BrowserHistory(string homepage) {
List = new Node();
List->prev = List->next = nullptr;
List->val = homepage;
Current = List;
} //(2)
void visit(string url) {
Node* Next = Current->next;
if(Next == nullptr) {
Current->next = new Node();
Current->next->next = nullptr;
Current->next->prev = Current;
}
else {
Node* tmp = Next->next;
Next->next = nullptr;
// 释放内存空间
while (tmp)
{
Node* node = tmp->next;
delete tmp;
tmp = node;
}
}
Current->next->val = url;
Current = Current->next;
} //(3)
string back(int steps) {
string str = Current->val;
Node* pre;
while (steps-- && Current)
{
pre = Current;
Current = Current->prev;
if(Current) str = Current->val;
}
if(!Current) {
Current = pre;
}
return str;
}
string forward(int steps) {
string str = Current->val;
Node* pre;
while (steps-- && Current)
{
pre = Current;
Current = Current->next;
if(Current) str = Current->val;
}
if(!Current) {
Current = pre;
}
return str;
} //(4)
};
- 1、创建双向链表的数据结构。有值,前指针,后指针;
- 2、链表的初始化定义头结点和当前结点;
- 3、visit:要分情况讨论,如果当前结点没有进行前进或者后退操作,直接当前结点的next进行链接,否则的话先将当前结点之后的记录全部删除掉,进行内存回收,再对当前结点的next进行操作;
- 4、back和forward是类似的,不断找steps步内的next或者prev,但是这也有个条件Current要存在并且我们能访问,如果Current不是空指针的话,我们更新Current去它的next或者prev,同时要返回的网页string更新为当前结点的值,否则如果Current根本不存在你也移动不了,老老实实回到上一个地方吧。
// 430. 扁平化多级双向链表
class Solution {
public:
Node* dfs(Node* head) {
Node *now = head;
Node *last = nullptr; //(2)
while(now)
{
Node *clast;
if(now->child) {
clast = dfs(now->child); //(3)
Node* next = now->next;
now->next = now->child;
now->child = nullptr;
now->next->prev = now;
if(next) {
next->prev = clast;
}
clast->next = next; //(4)
}
if(now->next == nullptr) {
last = now;
} //(5)
now = now->next;
}
return last;
}
Node* flatten(Node* head) {
if(!head) {
return nullptr;
}
Node* last = dfs(head); //(1)
last->next = nullptr;
return head;
}
};
- 1、递归调用的入口,返回一个值;
- 2、定义一个当前结点,和最终返回的结点;
- 3、判断是否有子结点,有的话继续递归子结点;
- 4、这一块是这么个过程now->childFirst->···->childLast->(now->next) :
(1) 定义一个next用来先把当前的now->next存下来;
(2) now–>next我要指向孩子,now->next的前指针指回now,形成完整的双向链表;
(3) 之前next不是原来的now->next嘛,如果不空接到孩子结点的后面去,否则的话孩子结点的末尾就是结束,在孩子结点末尾加null; - 5、如果now->next已经是空了,那last就到now这就结束了,也不需要考虑是不是有孩子结点,有孩子前面递归就把它处理了。
// 剑指 Offer II 028. 展平多级双向链表
class Solution {
public:
Node* dfs(Node* head) {
Node *now = head;
Node *last = nullptr;
while(now)
{
Node *clast;
if(now->child) {
clast = dfs(now->child);
Node* next = now->next;
now->next = now->child;
now->child = nullptr;
now->next->prev = now;
if(next) {
next->prev = clast;
}
clast->next = next;
}
if(now->next == nullptr) {
if(now->child) {
last = clast;
} else {
last = now;
}
}
now = now->next;
}
return last;
}
Node* flatten(Node* head) {
if(!head) {
return nullptr;
}
Node* last = dfs(head);
last->next = nullptr;
return head;
}
};
- 1、同上一题。
// 剑指 Offer 36. 二叉搜索树与双向链表
class Solution {
void dfs(Node *root, Node** minnode, Node** maxnode) {
if(root == nullptr) {
*minnode = nullptr;
*maxnode = nullptr;
} //(2)
Node *lminnode, *lmaxnode, *rminnode, *rmaxnode;
if(root->left) {
dfs(root->left, &lminnode, &lmaxnode);
lmaxnode->right = root;
root->left = lmaxnode;
*minnode = lminnode; //(3)
} else {
*minnode = root;
}
if(root->right) {
dfs(root->right, &rminnode, &rmaxnode);
rminnode->left = root;
root->right = rminnode;
*maxnode = rmaxnode; //(4)
} else {
*maxnode = root;
}
}
public:
Node* treeToDoublyList(Node* root) {
if(root == nullptr) {
return nullptr;
}
Node *minnode, *maxnode; //(1)
dfs(root, &minnode, &maxnode);
minnode->left = maxnode;
maxnode->right = minnode;
return minnode;
}
};
- 1、构成双向链表,先定义一个头指针和尾指针,准备将链表循环到一起;
- 2、处理下结点为空的情况;
- 3、根据平衡二叉树的特性,左子树<根<右子树,即中序遍历一遍会发现是一个递增的序列,所以找到左子树的最小结点和最大结点,最小结点肯定也是整棵平衡二叉树的最小结点,左边最大的结点一定会比根小,所以左边最大结点的尾指针指向根节点,根节点的头指针指向左边最大结点。
- 4、根据平衡二叉树的特性,左子树<根<右子树,即中序遍历一遍会发现是一个递增的序列,所以找到右子树的最小结点和最大结点,最大结点肯定也是整棵平衡二叉树的最大结点,右边最小的结点一定会比根大,所以右边最小结点的头指针指向根节点,根节点的尾指针指向右边最小结点;
- 4、在主函数中还要把首尾指针串在一起形成一个双向链表。