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

类似题目:

Binary Tree Inorder Traversal

参考资料:

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/discuss/174111/inorder-vs-divide-and-conquer

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/discuss/151228/Java-Simple-InOrder-no-Global

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/discuss/138621/C%2B%2B-solution

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/discuss/149151/Concise-Java-solution-Beats-100

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/discuss/154659/Divide-and-Conquer-without-Dummy-Node-Java-Solution

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Convert Binary Search Tree to Sorted Doubly Linked List 将二叉搜索树转为有序双向链表的更多相关文章

  1. 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 ...

  2. 【LeetCode】426. Convert Binary Search Tree to Sorted Doubly Linked List 解题报告 (C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 迭代 日期 题目地址:https://leetc ...

  3. [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 ...

  4. 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 ...

  5. 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 ...

  6. [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 ...

  7. 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 ...

  8. 第33题:LeetCode255 Verify Preorder Sequence in Binary Search Tree 验证先序遍历是否符合二叉搜索树

    题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 考点 1.BST 二叉搜索树 2.递归 思路 1.后序 ...

  9. LeetCode: Validata Binary Search Tree

    LeetCode: Validata Binary Search Tree Given a binary tree, determine if it is a valid binary search ...

随机推荐

  1. css样式关键字(initial,inherit,unset,revert,all)

    关键字 在CSS中,有4个关键字理论上可以应用于任何的CSS属性,它们是initial(初始).inherit(继承).unset(未设置).revert(还原).而all的取值只能是以上这4个关键字 ...

  2. EffectiveC++ 第7章 模板与泛型编程

    我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 7 模版与泛型编程 Templates and Gen ...

  3. [转]C语言的int最值问题,以及原码反码及补码

    以2字节为例来说: 对于无符号的数值(原码反码及补码都一样),最大值为1111  1111  1111  1111=65535 最小值为0000  0000  0000  0000=0 对于有符号的来 ...

  4. 【译】索引进阶(六):SQL SERVER索引书签

    [译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正] 原文链接:传送门. 在之前的章节,我们把索引看做一组有序条目的集合,每行数据对应一个索引条目.我们解释了很多关于索引逻辑方面的内容, ...

  5. JAVA中的栈和堆【转】

    原文链接 https://www.cnblogs.com/ibelieve618/p/6380328.html JAVA在程序运行时,在内存中划分5片空间进行数据的存储.分别是:1:寄存器.2:本地方 ...

  6. 【原创】大叔经验分享(18)hive2.0以后通过beeline执行sql没有进度信息

    一 问题 在hive1.2中使用hive或者beeline执行sql都有进度信息,但是升级到hive2.0以后,只有hive执行sql还有进度信息,beeline执行sql完全silence,在等待结 ...

  7. Mysql 常用SQL语句集锦

    基础篇 //查询时间,友好提示 $sql = "select date_format(create_time, '%Y-%m-%d') as day from table_name" ...

  8. Python——plot可视化数据,作业8(python programming)

    subject1k和subject1v的形状相同 # -*- coding: utf-8 -*- import scipy.io as sio raw_K = sio.loadmat('Subject ...

  9. Monkey自动化脚本(一)

    1.Monkey简介 Monkey-猴子,通过Monkey程序模拟用户触摸屏幕.滑动Trackball. 按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常,主要用于Android ...

  10. Python内置函数之-property

    property 是一个内置的装饰器函数,只在面向对象中使用 求一个圆的周长和面积 # 周长和面积都是通过类的方法得到from math import pi class Cricle: def __i ...