二叉树常见概念、性质、问题以及操作

12 篇文章 0 订阅

参考书籍:数据结构C语言-严蔚敏、吴伟民 Java程序员面试宝典-何昊等

参考链接:http://www.cnblogs.com/bmrs/archive/2010/08/19/SloveTree.html

二叉树的基本概念
:结点所拥有的子树的数目称为该结点的度。
叶结点:度为0的结点称为叶结点。
分支结点:度不为0的结点称为分支结点。一个树除叶结点外,其余都是分支结点。
树的深度:树中所有结点的最大层树称为树的深度。
树的度:树中各结点度的最大值称为该树的度。叶子结点的度为0
结点的层数:规定根结点的层数为1,其余结点的层数等于他的双亲结点的层树加1。
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的一棵树称为满二叉树。
完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点从上至下,从左到右进行编号,如果编号为i(1<=i<=n)的结点与满二叉树中的位置相同,那么这棵二   叉树就是完全二叉树。 完全二叉树的特点是:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。满二叉树肯定是完全二叉树,完全二叉树不一定是满二叉树。
二叉树的性质
性质1:一个非空二叉树第i层上最多有2^(i-1)个结点(i>=1);
性质2:一棵深度为k的二叉树中,最多具有2^k-1个结点,最少有k个结点。
性质3:一棵非空二叉树,度为0的结点(叶子结点)总是比度为2的结点多一个,即如果叶子结点数为n0,度为2的结点数为n2,那么n0 = n2 + 1;
           证明:n0:度为0的结点总数,n1:度为1的结点总数 ,n2:度为2的结点总数,则n = n0+n1+n2,根据二叉树和树的性质,可知n = n1 + 2*n2+1(所有结点的度数之和加1 = 结点总数),所以:n0+n1+n2 = n1+2*n2+1,可以得到n0 = n2 + 1;
性质4:具有n个结点的完全二叉树深度为[log2(n)]向下取整+1或[log2(n+1)]向上取整。
性质5:对于具有n个结点的完全二叉树,如果按照从上至下和从左到右的顺序对二叉树中的所有结点从1开始顺序编号,则对于任意的序号为i的结点,有:
          1、如果i>1,那么序号为i的结点的双亲结点的序号为i/2;如果i=1,那么序号为i的结点是根结点,无双亲结点。
          2、如果2i<=n,那么序号为i的结点的左孩子结点的序号为2i;如果2i > n,那么序号为i的结点无左孩子。
          3、如果2i+1 <=n,那么序号为i的结点的右孩子结点的序号为2i+1;如果2i +1> n,那么序号为i的结点无右孩子。

练习
1 、一棵完全二叉树有1001个结点,其中叶子结点的个数是多少?
二叉树的公式:n = n0+n1+n2 = n0+n1+(n0-1) = 2*n0+n1-1,又因为在完全二叉树中,n1只能取0或1,如果n1 = 1,则2*n0 = 1001得到n0为小数,不符合,所以n1为0,此时2*n0 - 1 = 1001,得到n0为501.

2、如果根的层次为1,具有61个结点的完全二叉树的高度为多少?
答案:6

3、在具有100个结点的树中,其边的数目为多少?
在一棵树中,除了根结点外,每一个结点都有一条入边,因此总边数应该是100-1,即99,所以答案是99.

二叉树的常见操作
package com.Howard.test11.BinaryTree;

public class Node {
     public int data;
     public Node left;
     public Node right;
     //以下两个用于计算结点间最大距离
     //左子树距根结点的最大距离
     public int leftMaxDistance;
     //右子树距根结点的最大距离
     public int rightMaxDistance;
     public Node (int data) {
           this.data = data;
           this.left = null;
           this.right = null;
     }
}
package com.Howard.test11.BinaryTree;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 二叉树
 * @author Howard
 * 2017年4月22日
 */
public class BinaryTree {
     private Node root;
     public BinaryTree() {
           root = null;
     }
     /**
      * 将data插入到排序二叉树中
      * @param data
      */
     public void insert(int data) {
           Node newNode = new Node(data);
           if (root == null) {
                root = newNode;
           }else {
                Node current = root;
                Node parent;
                while(true) {
                     parent = current;
                     if (data < current.data) {
                           current = current.left;
                           if (current == null) {
                                parent.left = newNode;
                                return;
                           }
                     }else {
                           current = current.right;
                           if (current == null) {
                                parent.right = newNode;
                                return;
                           }
                     }
                }
           }

     }
     /**
      * 构建二叉树
      * @param data
      */
     public void buildTree (int[] data) {
           for (int i = 0; i < data.length; i++) {
                insert(data[i]);
           }
     }
     /**
      * 中序遍历
      * @param localRoot
      */
     public void inOrder(Node localRoot) {
           if (localRoot != null) {
                inOrder(localRoot.left);
                System.out.print(localRoot.data + " ");
                inOrder(localRoot.right);
           }
     }
     public void inOrder() {
           this.inOrder(this.root);
     }
     /**
      * 先序遍历
      * @param localRoot
      */
     public void preOrder(Node localRoot) {
           if (localRoot != null) {
                System.out.print(localRoot.data + " ");
                preOrder(localRoot.left);
                preOrder(localRoot.right);
           }
     }
     public void preOrder() {
           this.preOrder(this.root);
     }
     /**
      * 后序遍历
      * @param localRoot
      */
     public void postOrder(Node localRoot) {
           if (localRoot != null) {
                postOrder(localRoot.left);
                postOrder(localRoot.right);
                System.out.print(localRoot.data + " ");
           }
     }

     public void postOrder() {
           this.postOrder(this.root);
     }
     /**
      * 层序遍历
      * 利用队列实现
      * 先将根结点放入队列中,然后每次都从队列中取出一个结点打印该结点的值,若这个结点有子结点,则将他
      * 的子结点放入队列尾,直到队列为空
      */
     public void layerTranverse() {
           if (this.root == null) {
                return;
           }
           Queue<Node> queue = new LinkedList<>();
           queue.add(this.root);
           while (!queue.isEmpty()) {
                Node node = queue.poll();
                System.out.print(node.data + " ");
                if (node.left != null) {
                     queue.add(node.left);
                }
                if (node.right != null) {
                     queue.add(node.right);
                }
           }
     }

     public  int maxlen = 0;
     private int getMax(int a,int b) {
           return a > b ? a : b;
     }
     /**
      * 求二叉树中结点的最大距离
      * 结点的距离是值这两个结点之间边的个数 求一棵二叉树中相距最远的两个结点之间的距离
      * 思路:首先求左子树距根结点的最大距离 ,其次求右子树距根结点的最大距离
      * 则二叉树中结点的最大距离是两者之和
      * 可以用递归实现
      * @param root
      */
     public void FindMaxDistance(Node root) {
           if (root == null) {
                return;
           }
           if (root.left == null) {
                root.leftMaxDistance = 0;
           }
           if (root.right == null) {
                root.rightMaxDistance = 0;
           }
           if (root.left != null) {
                FindMaxDistance(root.left);
           }
           if (root.right != null) {
                FindMaxDistance(root.right);
           }
           //计算左子树中距离根结点的最大距离
           if (root.left != null) {
                root.leftMaxDistance = getMax(root.left.leftMaxDistance, root.left.rightMaxDistance) + 1;
           }
           //计算右子树中距离根结点的最大距离
           if (root.right != null) {
                root.rightMaxDistance = getMax(root.right.leftMaxDistance, root.right.rightMaxDistance) + 1;
           }
           if (root.leftMaxDistance + root.rightMaxDistance > maxlen) {
                maxlen = root.leftMaxDistance + root.rightMaxDistance;
           }
     }
     /**
      * 测试
      * @param args
      */
     public static void main(String[] args) {
           BinaryTree biTree = new BinaryTree();
           int[] data = {2,8,7,4,9,3,1,6,7,5};
           biTree.buildTree(data);
           System.out.println("中序遍历:");
           biTree.inOrder();
           System.out.println();
           System.out.println("先序遍历:");
           biTree.preOrder();
           System.out.println();
           System.out.println("后序遍历:");
           biTree.postOrder();
           System.out.println();
           System.out.println("层序遍历:");
           biTree.layerTranverse();
           System.out.println();
           biTree.FindMaxDistance(biTree.root);
           System.out.println(biTree.maxlen);
     }
}


另外,对于二叉树还有一种常见的题,即:
已知中序遍历和先序(或后序),求解树。
(如果只知道树的先序和后序,是无法完整的求出中序的)
一、已知二叉树的前序序列和中序序列,求解树。
1、确定树的根节点。树根是当前树中所有元素在前序遍历中最先出现的元素。
2、求解树的子树。找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树。若根节点左边或右边为空,则该方向子树为空;若根节点左边和右边都为空,则根节点已经为叶子节点。
3、递归求解树。将左子树和右子树分别看成一棵二叉树,重复1、2、3步,直到所有的节点完成定位。
二、已知二叉树的后序序列和中序序列,求解树。
1、确定树的根。树根是当前树中所有元素在后序遍历中最后出现的元素。
2、求解树的子树。找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树。若根节点左边或右边为空,则该方向子树为空;若根节点左边和右边都为空,则根节点已经为叶子节点。
3、递归求解树。将左子树和右子树分别看成一棵二叉树,重复1、2、3步,直到所有的节点完成定位。
举例说明:根据已知求解二叉树
中序序列 HLDBEKAFCG
后序序列 LHDKEBFGCA
1、在后序序列LHDKEBFGCA中最后出现的元素为A,HLDBEK| A|FCG
2、在后序序列LHDKEB中最后出现的元素为B,HLD| B|EK|A|FCG
3、在后序序列LHD中最后出现的元素为D,HL| D|B|EK|A|FCG
4、在后序序列LH中最后出现的元素为H, H|L|D|B|EK|A|FCG
5、在后序序列KE中最后出现的元素为E,H|L|D|B| E|K|A|FCG
5、在后序序列FGC中最后出现的元素为C,H|L|D|B|E|K|A|F| C|G
6、所有元素都已经定位,二叉树求解完成。
                 A
              /     \
             B       C
            / \     /  \
           D  E     F   G
          /    \
         H      K                   
          \                         
           L          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值