什么是二叉搜索树:
二叉搜索树是一棵有序的二叉树,所以我们也可以称它为二叉排序树。具有以下性质的二叉树我们称之为二叉搜索树:若它的左子树不为空,那么左子树上的所有值均小于它的根节点;若它的右子树不为空,那么右子树上所有值均大于它的根节点。它的左子树和右子树分别也为二叉搜索树。
二叉搜索树的中序遍历是:左=>根=>右; 二叉搜索树的中序遍历从小到大是有序的。
中序遍历代码模版:
//打印中序遍历
void dfs(TreeNode* root )
{
if(!root) return;
dfs(root->left); //左
print(root->val); //根
dfs(root->right); //右
}
思路:
本体要求我们将一颗二叉搜索树变成排序的循环双向链表。
二叉搜索树的中序遍历就是有序的,因此这道题就是在中序递归遍历的基础上改了一点。
具体过程如下:
- 首先定义两个指针pre和head,pre指着是用于保存中序遍历的前一个节点,也就是后一个比它大的节点要指向的节点;head节点则用来保存排序链表的头结点。
- 中序遍历二叉搜索树,遍历顺序就是双向链表的建立顺序。我们只需要在中序遍历过程中,修改每个节点的左右指针,将节点连接成双向循环链表。
- 首先遍历二叉树的左子树
dfs(root.left)
- 当前驱结点pre为空时,说明此时正访问的是链表的头结点,所以记
head=root
, 保存头结点。if (pre == null) head = root
- 当前驱结点pre不为空时,说明此时的pre指向了正在遍历的root节点的前一个更小的节点,所以将pre的右指针指向当前根节点root,即
else if (pre != null) pre.right = root
- 当前驱结点pre为空时,说明此时正访问的是链表的头结点,所以记
- 每一个root节点被访问之时,它的左子树一定被访问并且排好序了,所以此时可以放心修改它的left指针,将root节点的left指针指向它的前驱节点,即
root.left=pre
。这样pre的右指针指向后一个大的root节点,root节点的左指针指向前一个小的节点,即双向指针修改完毕。 - 接着前驱结点pre右移到当前root节点
pre = root
,再去遍历到右子树重复上述操作dfs(root.right)
。 - 最后,就讲二叉搜索树变成了双向排序链表,在这之后,我们还需要将链表的首尾连接在一起,使其变为双向循环排序链表。
head->left = pre; pre->right = head;
图文理解
完整代码如下:
class Solution {
Node pre, head;
public Node treeToDoublyList(Node root) {
if (root == null) return null;
dfs(root);
// 头尾相连接
head.left = pre;
pre.right = head;
return head;
}
// 中序遍历二叉搜索树,可以得到递增序列
public void dfs(Node root) {
if (root == null) return;
// 先遍历左子树
dfs(root.left);
// 遍历根节点
if (pre == null) head = root;
else if (pre != null) pre.right = root;
root.left = pre;
pre = root;
// 最后遍历右子树
dfs(root.right);
}
}