23、二叉搜索树与双链表
题目
输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向,比如输入下图中左边的二叉搜索树,则输出转换之后的排序双向链表
思路
在二叉树中,每个结点都有两个指向子结点的指针。在双向链表中,每个结点也有两个指针,它们分别指向前一个结点和后一个结点。故链表和二叉搜索树的结构是相似的。在二叉搜索树中,左子结点的值总是小于父结点的值,右子结点的值总是大于父结点的值。因此在转换成排序双向链表时,原先指向左子结点的指针调整为链表中指向前一个结点的指针,原先指向右子结点的指针调整为链表中指向后一个结点的指针。
要求转换之后是有序的,我们知道二叉搜索树的中序遍历结果也是有序的。当中序遍历到根结点的时候,我们把二叉搜索树看成3部分:值为10的结点,根结点为6的左子树,根结点为14的右子树。根据排序链表的定义,值为10的结点将和它的左子树的最大一个结点值为8的结点链接起来,同时它还将和右子树中最小的结点值为12的结点链接起来。如下图:
按照中序递归遍历中,当我们遍历转换到根结点时,它的左子树已经转换成了一个排序的链表,并且此时链表尾部的值为左子树中值最大的结点的值(8)。我们将它(8)和根结点(10)链接起来,此时根结点(10)变成了链表尾部,接着去遍历右子树,我们知道中序遍历根结点(10)后的一个结点此时为右子树值最小的结点(12),我们将它和根结点链接起来。左右子树再用这样的办法,即递归即可解决问题。
代码
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null || (pRootOfTree.left == null && pRootOfTree.right == null))
return pRootOfTree;
ArrayList<TreeNode> nodeList = new ArrayList<>();
BuildArrayList(pRootOfTree,nodeList);//这个函数执行后,数组中每个元素按照大小前后排序
for(int i=0;i<nodeList.size();i++)
{
if(i == 0)
{//数组的第一个节点处理,只有右子树指向下一个节点
nodeList.get(0).right = nodeList.get(1);
}
else if(i == nodeList.size()-1)
{//数组的最后一个节点,只有左子树指向前一个节点
nodeList.get(i).left = nodeList.get(i-1);
}
else{//数组中的中间节点,左子树指向上一个节点,右子树指向数组的下一个节点
nodeList.get(i).left = nodeList.get(i-1);
nodeList.get(i).right = nodeList.get(i+1);
}
}
return nodeList.get(0);
}
public void BuildArrayList(TreeNode root,ArrayList<TreeNode> nodeList)
{//二叉搜索的中序遍历,并把每个节点存入数组中
if(root == null)
return;
if(root.left != null)//左子树
BuildArrayList(root.left,nodeList);
if(root != null)//根节点
nodeList.add(root);
if(root.right != null)//右子树
BuildArrayList(root.right,nodeList);
}
}