题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
比如如下图中的二叉搜索树,则输出转换之后的排序双向链表为:
在二叉树中,每个结点都有两个指向子节点的指针。在双向链表中,每个结点也有两个指针,他们分别指向前一个结点和后一个结点。由于这两种结点的结构相似,同时二叉搜索树也是一种排序的数据结构,因此在理论上有可能实现二叉搜索树和排序的双向链表的转换。在搜索二叉树中,左子结点的值总是小于父节点的值,右子节点的值总是大于父节点的值。因此我们在转换称排序的双向链表时,原先指向的左子结点的指针调整为链表中指向前一个结点的指针,原先指向右子节点的指针调整为俩表为指向后一个结点的指针。
由于要求转换之后的链表是排好序的,我们可疑中序遍历树中的每一个结点,这是因为中序遍历算法的特点是按照从小到达的顺序遍历二叉树的每一个结点。当遍历到根节点的时候,我们把树看成3部分:值为10的结点,根节点为6的左子树、根节点为14的右子树。根据排序链表的定义,值为10的结点将和它的左子树的最大的一个结点(即值为8的结点)链接起来,同时它还将和右子树最小的结点(即值为12的结点)链接起来,如下图所示:
将上诉过程转换为代码如下:
解题思路:
1
.将左子树构造成双链表,并返回链表头节点;
2
.定位至左子树双链表最后一个节点;
3
.如果左子树链表不为空的话,将当前root追加到左子树链表;
4
.将右子树构造成双链表,并返回链表头节点;
5
.如果右子树链表不为空的话,将该链表追加到root节点之后;
6
.根据左子树链表是否为空确定返回的节点。
/**
* 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
* 要求不能创建任何新的结点,只能调整树中结点指针的指向
*
* 思路:二叉搜索树的中序遍历
*/
public class ConvertTreeToDoubleLinkedList_Method1 {
public class TreeNode{
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val){
this.val = val;
}
}
public TreeNode Convert(TreeNode root){
if(root == null){
return null;
}
if(root.left == null && root.right == null){
return root;
}
// 1、将左子树构造成双链表,并返回链表头节点
TreeNode left = Convert(root.left);
TreeNode p = left;
// 2、定位至左子树双向链表中的最后一个节点
while(p != null && p.right != null){
p = p.right;
}
// 3、如果左子树链表不为空的话,将当前root追加到左子树链表的末尾
if(left != null){
p.right = root;
root.left = p;
}
// 4、将右子树构造成双向链表,并返回链表头节点
TreeNode right = Convert(root.right);
// 5、如果右子树链表不为空的话,将该链表追加到 root 节点之后
if(right != null){
right.left = root;
root.right = right;
}
return left != null ? left : root;
}
}
上面的代码虽然不是很简洁,但是比较简单易懂。因为树的指针表达方式是用 right 和 left ,而链表表示指针一般用 next,所以可能会有所不适应。但是只要记住递归时,node 的 right 节点是比它大的,node 的 left 节点是比它小的。使用中序遍历,只需要找到根节点和它左子树中最后一个节点以及右子树中的第一个节点之间的关系,然后递归这个过程即可。
下面再提供一种牛客网上比较简洁的代码实现方法(我感觉理解起来没有上面的那种直观):
/**
* 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
* 要求不能创建任何新的结点,只能调整树中结点指针的指向
*
* 思路:二叉搜索树的中序遍历
*/
public class ConvertTreeToDoubleLinkedList {
public class TreeNode{
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val){
this.val = val;
}
}
// 双向链表中的左右头节点
TreeNode leftHead = null;
TreeNode rightHead = null;
public TreeNode Convert(TreeNode root){
// 递归终止条件:递归调用叶子节点的左右子节点,返回null
if(root == null){
return null;
}
// 第一次运行时,它会使最左叶子节点成为链表中的头节点
Convert(root.left);
if(rightHead == null){
leftHead = rightHead = root;
}else{
// 把根节点插入到双向链表右边,rightHead向后移动
rightHead.right = root;
root.left = rightHead;
rightHead = root;
}
// 把右子节点也插入到双向链表(rightHead已确定,直接插入即可)
Convert(root.right);
// 返回左边的头节点
return leftHead;
}
}