[LeetCode] Convert Binary Search Tree to Sorted Doubly Linked List 将二叉搜索树转为有序双向链表
Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right pointers as synonymous to the previous and next pointers in a doubly-linked list.
Let's take the following BST as an example, it may help you understand the problem better:
We want to transform this BST into a circular doubly linked list. Each node in a doubly linked list has a predecessor and successor. For a circular doubly linked list, the predecessor of the first element is the last element, and the successor of the last element is the first element.
The figure below shows the circular doubly linked list for the BST above. The "head" symbol means the node it points to is the smallest element of the linked list.
Specifically, we want to do the transformation in place. After the transformation, the left pointer of the tree node should point to its predecessor, and the right pointer should point to its successor. We should return the pointer to the first element of the linked list.
The figure below shows the transformed BST. The solid line indicates the successor relationship, while the dashed line means the predecessor relationship.
这道题给了一个二叉搜索树,让我们将其转化为双向链表。并且题目中给了一个带图的例子,帮助理解。题目本身并不难理解,仔细观察下给的示例图。首先,转化成双向链表的每个结点都有 left 和 right 指针指向左右两个结点,不管其原来是否是叶结点还是根结点,转换后统统没有区别。其次,这是个循环双向链表,即首尾结点是相连的,原先的二叉搜索树中的最左结点和最右结点,现在也互相连接起来了。最后,返回的结点不再是原二叉搜索树的根结点 root 了,而是最左结点,即最小值结点。
好,发现了上述规律后,来考虑如何破题。根据博主多年经验,跟二叉搜索树有关的题,肯定要利用其性质,即左<根<右,即左子结点值小于根结点值小于右子结点值。而且十有八九都得用中序遍历来解,因为中序遍历的顺序就是左根右啊,跟性质吻合。观察到原二叉搜索树中结点4连接着结点2和结点5,而在双向链表中,连接的是结点3和结点5,这就是为啥要用中序遍历了,因为只有中序遍历,结点3之后才会遍历到结点4,这时候可以将结点3和结点4串起来。决定了用中序遍历之后,就要考虑是迭代还是递归的写法,博主建议写递归的,一般写起来都比较简洁,而且递归是解树类问题的神器啊,十有八九都是用递归,一定要熟练掌握。再写中序遍历之前,其实还有难点,因为需要把相邻的结点连接起来,所以需要知道上一个遍历到的结点是什么,所以用一个变量 pre,来记录上一个遍历到的结点。还需要一个变量 head,来记录最左结点,这样的话,在递归函数中,先判空,之后对左子结点调用递归,这样会先一直递归到最左结点,此时如果 head 为空的话,说明当前就是最左结点,赋值给 head 和 pre,对于之后的遍历到的结点,那么可以和 pre 相互连接上,然后 pre 赋值为当前结点 node,再对右子结点调用递归即可,参见代码如下:
解法一:
class Solution {
public:
Node* treeToDoublyList(Node* root) {
if (!root) return NULL;
Node *head = NULL, *pre = NULL;
inorder(root, pre, head);
pre->right = head;
head->left = pre;
return head;
}
void inorder(Node* node, Node*& pre, Node*& head) {
if (!node) return;
inorder(node->left, pre, head);
if (!head) {
head = node;
pre = node;
} else {
pre->right = node;
node->left = pre;
pre = node;
}
inorder(node->right, pre, head);
}
};
虽然说树类问题首推递归解法,但是中序遍历是可以用迭代来写的,可以参见博主之前的博客 Binary Tree Inorder Traversal。迭代写法借用了栈,其实整体思路和递归解法没有太大的区别,递归的本质也是将断点存入栈中,以便之后可以返回,这里就不多讲解了,可以参见上面的讲解,参见代码如下:
解法二:
class Solution {
public:
Node* treeToDoublyList(Node* root) {
if (!root) return NULL;
Node *head = NULL, *pre = NULL;
stack<Node*> st;
while (root || !st.empty()) {
while (root) {
st.push(root);
root = root->left;
}
root = st.top(); st.pop();
if (!head) head = root;
if (pre) {
pre->right = root;
root->left = pre;
}
pre = root;
root = root->right;
}
head->left = pre;
pre->right = head;
return head;
}
};
这道题还有一种使用分治法 Divide and Conquer 来做的方法。分治法,顾名思义,就是把一项任务分成两半,用相同的逻辑去分别处理,之后再粘合起来。混合排序 Merge Sort 用的也是这种思路。这里可以对左右子结点调用递归函数,suppose 我们得到了两个各自循环的有序双向链表,然后把根结点跟左右子结点断开,将其左右指针均指向自己,这样就形成了一个单个结点的有序双向链表,虽然只是个光杆司令,但人家仍然是有序双向链表,不是沙雕,就问你叼不叼。那么此时只要再写一个连接两个有序双向链表的子函数,就可以将这三个有序双向链表按顺序链接起来了。
而链接两个有序双向链表的子函数也简单,首先判空,若一个为空,则返回另一个。如果两个都不为空,则把第一个链表的尾结点的右指针链上第二个链表的首结点,同时第二个链表的首结点的左指针链上第一个链表的尾结点。同理,把第二个链表的尾结点的右指针链上第一个链表的首结点,同时第一个链表的首结点的左指针链上第二个链表的尾结点。有木有读晕,可以自己画图,其实很好理解的诶,参见代码如下:
解法三:
class Solution {
public:
Node* treeToDoublyList(Node* root) {
if (!root) return NULL;
Node *leftHead = treeToDoublyList(root->left);
Node *rightHead = treeToDoublyList(root->right);
root->left = root;
root->right = root;
return connect(connect(leftHead, root), rightHead);
}
Node* connect(Node* node1, Node* node2) {
if (!node1) return node2;
if (!node2) return node1;
Node *tail1 = node1->left, *tail2 = node2->left;
tail1->right = node2;
node2->left = tail1;
tail2->right = node1;
node1->left = tail2;
return node1;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/426
类似题目:
参考资料:
https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Convert Binary Search Tree to Sorted Doubly Linked List 将二叉搜索树转为有序双向链表的更多相关文章
- LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List
原题链接在这里:https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/ 题目: C ...
- 【LeetCode】426. Convert Binary Search Tree to Sorted Doubly Linked List 解题报告 (C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 迭代 日期 题目地址:https://leetc ...
- [leetcode]426. Convert Binary Search Tree to Sorted Doubly Linked List二叉搜索树转有序双向链表
Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right pointers ...
- 426. Convert Binary Search Tree to Sorted Doubly Linked List把bst变成双向链表
[抄题]: Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right po ...
- LeetCode426.Convert Binary Search Tree to Sorted Doubly Linked List
题目 Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right point ...
- [LC] 426. Convert Binary Search Tree to Sorted Doubly Linked List
Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right pointers ...
- PAT 1043 Is It a Binary Search Tree (25分) 由前序遍历得到二叉搜索树的后序遍历
题目 A Binary Search Tree (BST) is recursively defined as a binary tree which has the following proper ...
- 第33题:LeetCode255 Verify Preorder Sequence in Binary Search Tree 验证先序遍历是否符合二叉搜索树
题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 考点 1.BST 二叉搜索树 2.递归 思路 1.后序 ...
- LeetCode: Validata Binary Search Tree
LeetCode: Validata Binary Search Tree Given a binary tree, determine if it is a valid binary search ...
随机推荐
- react-router v4中 HashRouter 和 BrowserRouter的使用
遇到的问题 项目中控制路由跳转使用的是BrowserRouter,代码如下: ReactDOM.render(( <BrowserRouter> <div className=&qu ...
- RC522射频卡读写模块驱动(仅读取)
目录 说明 测试结果 main RC522.h RC522.c 说明 更改了网上的源代码,仅保留了读取序列号并通过串口回传的功能.版本号:V1 感谢 https://blog.csdn.net/qq_ ...
- 移动端bug集合
移动端软键盘遮挡输入框&IOS移动端点击输入框字体放大&IOS点击闪烁 移动端软键盘遮挡输入框 ——> 转载自: https://www.jb51.net/article/1 ...
- 第三章 Java的基础程序设计结构
一个简单的 Java 应用程序 访问修饰符 public,private,protected main 方法必须时public修饰的,C#则不必须 数据类型 可以用16进制表示浮点数 可以用2,8,1 ...
- 万维网WWW详解
万维网WWW(World Wide Web)并非某种特殊的计算机网络,万维网是一个个大规模的.联机式的信息储藏所,英文简称Web. 万维网使用链接的方式能非常方便地从英特网上的一个站点访问到一个站点, ...
- jsp注释<%-- --%>和<!-- -->的区别
最近在写JSP页面注释的时候,遇到一个问题,在JSP页面引用的静态属性资源文件时,在浏览器控制台报错,当我把引用的标签注释掉后,用的是<!-- -->.然后浏览器仍然报了之前那个错,经过查 ...
- 《剑指offer》把数组排成最小的数
本题来自<剑指offer> 反转链表 题目: 思路: C++ Code: Python Code: 总结:
- ES6 语法学习(二)
1.类的建立与继承 constructor方法是类的构造函数是默认方法,通过new命令生成对象实例时,自动调用该方法.一个类必须有constructor方法,如果没有显式定义,一个默认的constru ...
- query string parameters 、 Form Data 、 Request Payload
微信小程序ajax向后台传递参数的时候总是报400错误 然后看了一下network 发现是query string parameters,但是我写的header如下 header:{ "Co ...
- bat路径中有空格
例如bat文件中写 C:/Program Files (x86)/Google/Chrome/Application/chrome.exe ./html/index.html pause 会报错, ...