数据结构:构造二叉树(前跟中跟,中跟后跟)

构造一棵二叉树必须明确以下两种关系:

  • 结点与其双亲结点及孩子结点间的层次关系。
  • 兄弟结点间左或右的次序关系。

关系的确定:

  • 先根遍历和后跟遍历反映双亲与孩子结点之间的层次关系。
  • 中跟遍历反映兄弟结点之间的左右关系。

建立二叉树的方法:

  • 按先根和中根次序遍历序列建立二叉树。
  • 以标明空子树的先根次序遍历序列建立二叉树。
  • 以广义表表示建立二叉树。
  • 建立链式存储结构的完全二叉树。

PS:一个序列无法确定一个二叉树(特殊除外)。

1,前根中根还原二叉树

在二叉树类增加create()方法实现以先根遍历序列prelist[]、中根遍历序列inorder[]建立一颗二叉树的算法,并返回这颗二叉树的根结点。

【原理】先从前序的第一个结点开始,其为根节点,然后在中序中找到该元素,一分为二,中序左边为左子树,右边为右子树,然后从前序中找第二个元素为根结点左子树的根,然后重复上面这个过程,发现出现null,跳到右子树。

  • 前根:ABDGCEFH
  • 中跟:DGBAECHF
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTree1(0, 0, preorder.length-1, preorder, inorder);
    }

    public TreeNode buildTree1(int preStart, int inStart, int inEnd, int[] preorder, int[] inorder) {
        if (inStart > inEnd || preStart > preorder.length)
            return null;
        int rootValue = preorder[preStart];
        TreeNode root = new TreeNode(rootValue);
        int k = 0;
        for (int i = 0; i < preorder.length; i++) {
            if (inorder[i] == rootValue) {
                k = i;
                break;
            }
        }
        root.left = buildTree1(preStart + 1, inStart, k - 1, preorder, inorder);
        root.right = buildTree1(preStart + 1 + k - inStart, k + 1, inEnd, preorder, inorder);
        return root;
    }
}

2,中根后根还原二叉树

【思想】先从后序中的最后一个开始,可知为根节点,然后根据A节点位置同时将中跟和后跟划分为左右子树,然后重新选择根节点,递归。

 

class Solution {
    public TreeNode buildTree(int[] middle, int[] postorder) {
        return buildTree2(middle,0,middle.length-1,postorder,0,postorder.length-1);
    }

    public static TreeNode buildTree2(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) {
        if (inStart > inEnd || postStart > postEnd)
            return null;
        int rootValue = postorder[postEnd];
        TreeNode root = new TreeNode(rootValue);
        int k = 0;
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == rootValue) {
                k = i;
                break;
            }
        }
        root.left = buildTree2(inorder, inStart, k - 1, postorder, postStart, postStart + k - (inStart + 1));
        root.right = buildTree2(inorder, k + 1, inEnd, postorder, postStart + k - inStart, postEnd - 1);
        return root;
    }
}

3,以标明空子树的先根次序遍历序列建立二叉树(LeetCode 297)

仅有先根遍历不能唯一确定一颗二叉树。但是,如果在先根遍历中加入反映兄弟结点间的左右次序的信息(如以“^”标明空子树),则可以唯一确定一颗二叉树。

构造过程:prelist表示一棵二叉树标明空子树的先根序列,构造算法如下:

  • prelist[0]一定是二叉树的根,创建根结点; prelist[1]一定是根的左孩子。
  • 若prelist[i]不是”^”,则创建一个结点,该结点的左孩子结点元素是prelist[i+1],但父母与孩子结点之间的链还未建立;否则当前子树为空,返回上一层结点。
  • 返回到当前结点时,下一个元素prelist[i+1]是当前结点的右孩子结点;当一个结点的左右孩子链都已建立,则以当前结点为根的一棵子树就已建立,返回上一层结点。
  • 重复执行步骤2~3,直到返回根结点,则二叉树建立,使root指向根结点。
public class Codec {
    String a = "";
    public String serialize(TreeNode root) {
        if (root == null) {
            return "";
        }
        preorderTraversal(root);
        return a;
    }
    public TreeNode deserialize(String data) {
        if (data.equals("")) {
            return null;
        }
        String[] f = data.split(" ");
        return create(f);
    }
    private int i = 0;
    private TreeNode create(String[] prelist) {
        TreeNode p = null;
        if (i < prelist.length) {
            String elem = prelist[i];
            i++;
            if (!elem.equals("^")) {      //不能elem!="∧",因为T不一定是String
                p = new TreeNode(Integer.parseInt(elem));   //创建叶子结点
                p.left = create(prelist);      //创建p的左子树,递归调用,实际参数与形式参数相同
                p.right = create(prelist);      //创建p的右子树,递归调用,实际参数与形式参数相同
            }
        }
        return p;
    }
    public String preorderTraversal(TreeNode root) {
        if (root != null) {
            a += root.val + " ";
            preorderTraversal(root.left);    //按先根次序遍历p的左子树,递归调用,参数为左孩子
            preorderTraversal(root.right);     //按先根次序遍历p的右子树,递归调用,参数为右孩子
        } else {
            a += "^ ";
        }
        return a;
    }
}

4,广义表表示建立二叉树

以广义表形式可以表示一棵树,但不能唯一表示一棵二叉树,因为无法明确左右子树。二叉树的广义表表示语法如下图,其中元素表示结点,“^”表示空子树。

输出二叉树的广义表表示 

public void printGenList(){      //输出二叉树的广义表表示字符串
    System.out.print("二叉树的广义表表示:");
    printGenList(this.root);
    System.out.println();
}
//输出以p结点为根的一棵子树的广义表表示字符串,先根次序遍历,递归算法
private void printGenList(BinaryNode<T> p){
    if (p==null)                   //若二叉树空
        System.out.print("∧");    //输出空子树标记
    else{
        System.out.print(p.data.toString());    //访问当前结点
        if (p.left!=null || p.right!=null){     //非叶子结点,有子树
            System.out.print("(");
            printGenList(p.left);               //输出p的左子树,递归调用
            System.out.print(",");
            printGenList(p.right);              //输出p的右子树,递归调用
            System.out.print(")");
        }
   }
}

按广义表次序遍历序列建立二叉树

public class BinaryTree_genlist{    
    private static int i=0;
    public static BinaryTree<String> createByGenList(String glist){  //以广义表表示构造二叉树
        BinaryTree<String> bitree = new BinaryTree<String>();
        i=0;
        if (glist.length()>0)
            bitree.root = create(glist);     //创建子树,子树根值是glist[0]
        return bitree;
    } 
    private static BinaryNode<String> create(String glist){
        BinaryNode<String> p=null;
        char ch=glist.charAt(i);
        if (ch>='A' && ch<='Z'){                     //大写字母
            p = new BinaryNode<String>(ch+"");       //创建叶子结点
            i++;
            if (glist.charAt(i)=='('){
                i++;                                 //跳过'('
                p.left = create(glist);              //创建左子树,递归调用
                i++;                                 //跳过','
                p.right = create(glist);             //创建右子树,递归调用
                i++;                                 //跳过')'
            }
        }
        if (ch=='^')
            i++;                                     //跳过'^'
        return p;                                   //空串返回null
    }
}

5,以完全二叉树的层次遍历序列建立二叉树

一棵具有n个结点的完全二叉树,对序号为i0≤in)的结点,有:

  • i=0,则 i 为根结点,无父母结点;
  • i>0,则 i 的父母结点序号为 (i-1)/2
  • 2i+1< n,则 i 的左孩子结点序号为 2i+1;否则 i 无左孩子。
  • 2i+2<n,则 i 的右孩子结点序号为 2i+2;否则 i 无右孩子。
//二叉链表表示的完全二叉树类,继承二叉树类 
public class CompleteBinaryTree<T> extends BinaryTree<T>{
    public CompleteBinaryTree(){           //构造空二叉树
        super();
    }

    //以完全二叉树的层次序列构造完全二叉树,levellist指定层次序列
    public CompleteBinaryTree(T[] levellist){
        this.root = create(levellist, 0);
    }

    //创建以levellist[i]为根的一棵子完全二叉树,返回所建子树的根结点
    private BinaryNode<T> create(T[] levellist, int i){
        BinaryNode<T> p = null;
        if (i<levellist.length){
            p = new BinaryNode<T>(levellist[i]);           //创建叶子结点
            p.left = create(levellist, 2*i+1);             //创建p的左子树
            p.right = create(levellist, 2*i+2);            //创建p的右子树
        }
        return p;
    }
}
  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燕双嘤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值