题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
比如输入图 4.12 中左边的二叉搜索树,则输出转换之后的排序现向链表。
解题思路:
在二叉树中,每个结点都有两个指向子结点的指针。在双向链表中,每个结点也有两个指针,它们分别指向前一个结点和后一个结点。由于这两种结点的结构相似,同时二叉搜索树也是一种排序的数据结构,因此在理论上有可能实现二叉搜索树和排序的双向链表的转换。在搜索二叉树中,左子结点的值总是小于父结点的值,右子结点的值总是大于父结点的值。因此我们在转换成排序双向链表时,原先指向左子结点的指针调整为链表中指向前一个结点的指针,原先指向右子结点的指针调整为链表中指向后一个结点指针。接下来我们考虑该如何转换。
由于要求转换之后的链表是排好序的,我们可以中序遍历树中的每一个结点, 这是因为中序遍历算法的特点是按照从小到大的顺序遍历二叉树的每一个结点。当遍历到根结点的时候,我们把树看成三部分:值为 10 的结点、根结点值为 6 的左子树、根结点值为 14 的右子树。根据排序链表的定义,值为 10 的结点将和它的左子树的最大一个结点(即值为 8 的结点)链接起来,同时它还将和右子树最小的结点(即值为 12 的结点)链接起来,如图 4.13 所示。
按照中序遍历的顺序, 当我们遍历转换到根结点(值为 10 的结点)时,它的左子树已经转换成一个排序的链表了, 并且处在链表中的最后一个结点是当前值最大的结点。我们把值为 8 的结点和根结点链接起来,此时链表中的最后一个结点就是 10 了。接着我们去地历转换右子树, 并把根结点和右子树中最小的结点链接起来。至于怎么去转换它的左子树和右子树,由于遍历和转换过程是一样的,我们很自然地想到可以用递归。
注意!!!!!!!!!!按照剑指offer书上的代码写java版代码会报错,最后头节点指针还是指向null,并没有更新,
实际运行中,每次递归调用ConvertNode()的时候都新建了一个lastNode的副本,而此处对引用指向的对象没有进行修改,而是修改引用的指向。但是对于形参副本的修改
是无法反映在实参上面的。因此为了实现类似于C中的指针的效果,将引用副本指向的修改保留下来,就声明了数组来保存。
代码:
package BinaryTree;
public class Offer27 {
public static class BinaryTreeNode{
int value;
BinaryTreeNode left;
BinaryTreeNode right;
BinaryTreeNode(){}
BinaryTreeNode(int val){
this.value = val;
}
}
//private BinaryTreeNode lastNodeInList = null;//指向双向链表的尾节点
public BinaryTreeNode convert(BinaryTreeNode root){
//已经处理好的双向链表的尾结点,使用一个长度为1的数组,类似C++中的二级指针
BinaryTreeNode [] lastNodeInList = new BinaryTreeNode[1];//指向双向链表的尾节点
convertNode(root, lastNodeInList);
//System.out.println(lastNodeInList);
//我们需要返回头节点,新建一个头节点用于指向双向链表的表头,若是不见,也不会报错,但是输出结果是错的
BinaryTreeNode headNodeOfList = lastNodeInList[0];
while(headNodeOfList != null && headNodeOfList.left != null){
headNodeOfList = headNodeOfList.left;
}
return headNodeOfList;
}
public void convertNode(BinaryTreeNode node, BinaryTreeNode [] lastNodeInList) {
if(node == null)
return;
BinaryTreeNode currentNode = node;
// 如果有左子树就先处理左子树
if(currentNode.left != null)
convertNode(currentNode.left, lastNodeInList);
// 将当前结点的前驱指向已经处理好的双向链表(由当前结点的左子树构成)的尾结点
currentNode.left = lastNodeInList[0];
// 如果左子树转换成的双向链表不为空,设置尾结点的后继
if(lastNodeInList[0] != null)
lastNodeInList[0].right = currentNode;//双向链表指向后一个节点
// 记录当前结点为尾结点
lastNodeInList[0] = currentNode;
//转换右子树
if(currentNode.right != null)
convertNode(currentNode.right, lastNodeInList);
//System.out.println(lastNodeInList);
}
//为了使方便测试,这里封装了一个设置指定根节点的左孩子和右孩子节点的方法:
public static void setSubTreeNode(BinaryTreeNode root, BinaryTreeNode left, BinaryTreeNode right){
if(root == null)
return;
root.left = left;
root.right = right;
}
public static void printList(BinaryTreeNode root){
while(root != null){
System.out.print(root.value+"->");
root = root.right;
}
System.out.print("null");
}
public static void printTree(BinaryTreeNode root){
if(root!= null){
printTree(root.left);
System.out.print(root.value+"->");
printTree(root.right);
}
}
public static void main(String[] args) {
Offer27 of27 = new Offer27();
//功能测试,1,输入的二叉树是完全二叉树
// 10
// / \
// 6 14
// /\ /\
// 4 8 12 16
BinaryTreeNode node11 = new BinaryTreeNode(10);
BinaryTreeNode node12 = new BinaryTreeNode(6);
BinaryTreeNode node13 = new BinaryTreeNode(14);
BinaryTreeNode node14 = new BinaryTreeNode(4);
BinaryTreeNode node15 = new BinaryTreeNode(8);
BinaryTreeNode node16 = new BinaryTreeNode(12);
BinaryTreeNode node17 = new BinaryTreeNode(16);
setSubTreeNode(node11, node12, node13);
setSubTreeNode(node12, node14, node15);
setSubTreeNode(node13, node16, node17);
System.out.println("测试用例1:");
System.out.print("Before convert: ");
printTree(node11);
System.out.println("null");
BinaryTreeNode tempNode11 = of27.convert(node11);
System.out.print("After convert: ");
printList(tempNode11);
System.out.println();
//功能测试
// 02.二叉树的所有节点都没有左子树
// 10
// \
// 14
// \
// 16
BinaryTreeNode node21 = new BinaryTreeNode(10);
BinaryTreeNode node22 = new BinaryTreeNode(14);
BinaryTreeNode node23 = new BinaryTreeNode(16);
node21.right = node22;
node22.right = node23;
System.out.println("测试用例2:");
System.out.print("Before convert: ");
printTree(node21);
System.out.println("null");
BinaryTreeNode tempNode21 = of27.convert(node21);
System.out.print("After convert: ");
printList(tempNode21);
System.out.println();
//功能测试
// 03.二叉树的所有节点都没有右子树
// 10
// /
// 6
// /
// 4
BinaryTreeNode node31 = new BinaryTreeNode(10);
BinaryTreeNode node32 = new BinaryTreeNode(6);
BinaryTreeNode node33 = new BinaryTreeNode(4);
node31.left = node32;
node32.left = node33;
System.out.println("测试用例3:");
System.out.print("Before convert: ");
printTree(node31);
System.out.println("null");
BinaryTreeNode tempNode31 = of27.convert(node31);
System.out.print("After convert: ");
printList(tempNode31);
System.out.println();
//功能测试
// 04.只有一个节点的二叉树
// 10
BinaryTreeNode node41 = new BinaryTreeNode(10);
System.out.println("测试用例4:");
System.out.print("Before convert: ");
printTree(node41);
System.out.println("null");
BinaryTreeNode tempNode41 = of27.convert(node41);
System.out.print("After convert: ");
printList(tempNode41);
System.out.println();
//特殊输入测试,5
//指向二叉树的根节点的指针为NULL
BinaryTreeNode node51 = null;
System.out.println("测试用例5:");
System.out.print("Before convert: ");
printTree(node51);
System.out.println("null");
BinaryTreeNode tempNode51 = of27.convert(node51);
System.out.print("After convert: ");
printList(tempNode51);
System.out.println();
}
}
运行结果:
测试用例1:
Before convert: 4->6->8->10->12->14->16->null
After convert: 4->6->8->10->12->14->16->null
测试用例2:
Before convert: 10->14->16->null
After convert: 10->14->16->null
测试用例3:
Before convert: 4->6->10->null
After convert: 4->6->10->null
测试用例4:
Before convert: 10->null
After convert: 10->null
测试用例5:
Before convert: null
After convert: null
第二种解法,参考https://blog.youkuaiyun.com/lilianforever/article/details/51853960?utm_source=blogxgwz7