【数据结构】二叉树
2016-05-03 11:34
423 查看
特点
1、二叉树的每个结点至多只有二棵子树(不存在度大于2的结点);
2、在非空二叉树的k层上,至多有2^(k-1)个节点(k>0);
3、高度为k的二叉树中,最多有2^k-1个节点(k>0);
4、对于任何一棵非空的二叉树,如果叶节点个数为n0,度数为2的节点个数为n2,则有: n0 = n2 + 1。
注:通过网上学习得知,国外的教材对于数的高度和深度通常都是从0开始,而国内比较偏向从1开始,所以要注意这个概念上的定义,例如我上文定义的(K>1)。
(从二叉树特点判断,以上二图中的树都为二叉树)
满二叉树
满二叉树是二叉树的一种,除了具备二叉树的特征,还需要满足的条件是:若一棵深度为k,且有2^k-1个节点称之为满二叉树。就是一个挂满节点的二叉树,叫满二叉树,如下图:
以上二叉树为满二叉树,其中深度K=3,总节点数为2^3-1 = 7。
完全二叉树
除基本二叉树特点外,还需要满足的条件是除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。换句话说,叶子结点只可能在最大的两层上出现,对任意结点,若其右分支下的子孙最大层次为L,则其左分支下的子孙的最大层次必为L 或 L+1;
(从完全二叉树特点判断,以上二图中的树都为完全二叉树)
由于二叉树的特点可以得出:
1、满二叉树是一颗挂满节点的数,所以满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树。
2、根据公式进行推导,假设n0是度为0的结点总数(即叶子结点数),n1是度为1的结点总数,n2是度为2的结点总数,由二叉树的性质可知:n0=n2+1,则n= n0+n1+n2(其中n为完全二叉树的结点总数),由上述公式把n2消去得:n= 2n0+n1-1,由于完全二叉树中度为1的结点数只有0或1两种可能,由此得到n0=(n+1)/2或n0=n/2。总结起来,就是 n0=[n/2],其中[]表示上取整。如上图左边的完全二叉树,n = 6;得知n0 = [6/2] = 3;右边的同理,n0 = [5/2] = 3。
二叉树的实现练习
对树的基本运算操作有获取树的高度、获取树的节点数、获取节点的双亲节点和对树遍历,其中,遍历二叉树 是指以一定的次序访问二叉树中的每个结点,遍历二叉树的过程实质是把二叉树的结点进行线性排列的过程,那么遍历的结果得到一个线性序列。从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
(1)访问结点本身(N),
(2)遍历该结点的左子树(L),
(3)遍历该结点的右子树(R)。
以上三种操作有六种执行次序:NLR、LNR、LRN、NRL、RNL、RLN。因为前三种次序与后三种次序对称,故只需要讨论先左后右的前三种次序并加以解析。NLR、LNR和LRN分别又称为先根遍历(前序遍历DLR)、中根遍历(中序遍历LDR)和后根遍历(后续遍历LRD)。
前序遍历:ABDECF
前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
中序遍历:DBEAFC
中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。
后序遍历:DEBFCA
后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。
Java实现例子
执行结果:
tree height:3
ABDECF---前序遍历
DBEAFC---中序遍历
DEBFCA---后序遍历
1、二叉树的每个结点至多只有二棵子树(不存在度大于2的结点);
2、在非空二叉树的k层上,至多有2^(k-1)个节点(k>0);
3、高度为k的二叉树中,最多有2^k-1个节点(k>0);
4、对于任何一棵非空的二叉树,如果叶节点个数为n0,度数为2的节点个数为n2,则有: n0 = n2 + 1。
注:通过网上学习得知,国外的教材对于数的高度和深度通常都是从0开始,而国内比较偏向从1开始,所以要注意这个概念上的定义,例如我上文定义的(K>1)。
(从二叉树特点判断,以上二图中的树都为二叉树)
满二叉树
满二叉树是二叉树的一种,除了具备二叉树的特征,还需要满足的条件是:若一棵深度为k,且有2^k-1个节点称之为满二叉树。就是一个挂满节点的二叉树,叫满二叉树,如下图:
以上二叉树为满二叉树,其中深度K=3,总节点数为2^3-1 = 7。
完全二叉树
除基本二叉树特点外,还需要满足的条件是除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。换句话说,叶子结点只可能在最大的两层上出现,对任意结点,若其右分支下的子孙最大层次为L,则其左分支下的子孙的最大层次必为L 或 L+1;
(从完全二叉树特点判断,以上二图中的树都为完全二叉树)
由于二叉树的特点可以得出:
1、满二叉树是一颗挂满节点的数,所以满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树。
2、根据公式进行推导,假设n0是度为0的结点总数(即叶子结点数),n1是度为1的结点总数,n2是度为2的结点总数,由二叉树的性质可知:n0=n2+1,则n= n0+n1+n2(其中n为完全二叉树的结点总数),由上述公式把n2消去得:n= 2n0+n1-1,由于完全二叉树中度为1的结点数只有0或1两种可能,由此得到n0=(n+1)/2或n0=n/2。总结起来,就是 n0=[n/2],其中[]表示上取整。如上图左边的完全二叉树,n = 6;得知n0 = [6/2] = 3;右边的同理,n0 = [5/2] = 3。
二叉树的实现练习
对树的基本运算操作有获取树的高度、获取树的节点数、获取节点的双亲节点和对树遍历,其中,遍历二叉树 是指以一定的次序访问二叉树中的每个结点,遍历二叉树的过程实质是把二叉树的结点进行线性排列的过程,那么遍历的结果得到一个线性序列。从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
(1)访问结点本身(N),
(2)遍历该结点的左子树(L),
(3)遍历该结点的右子树(R)。
以上三种操作有六种执行次序:NLR、LNR、LRN、NRL、RNL、RLN。因为前三种次序与后三种次序对称,故只需要讨论先左后右的前三种次序并加以解析。NLR、LNR和LRN分别又称为先根遍历(前序遍历DLR)、中根遍历(中序遍历LDR)和后根遍历(后续遍历LRD)。
前序遍历:ABDECF
前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
中序遍历:DBEAFC
中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树。
后序遍历:DEBFCA
后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。
Java实现例子
public class BinaryTree { private TreeNode root=null; /** * 创建一棵二叉树 * A * B C * D E F */ public void createBinTree(){ TreeNode root_A = new TreeNode("A"); TreeNode node_B = new TreeNode("B"); TreeNode node_C = new TreeNode("C"); TreeNode node_D = new TreeNode("D"); TreeNode node_E = new TreeNode("E"); TreeNode node_F = new TreeNode("F"); root = root_A; root.leftChild=node_B; root.rightChild=node_C; root.leftChild.leftChild=node_D; root.leftChild.rightChild=node_E; root.rightChild.rightChild=node_F; } /** * 获取树的高度 * @param subTree * @return */ private int height(TreeNode subTree){ if(subTree==null) return 0;//递归结束:空树高度为0 else{ int i=height(subTree.leftChild); int j=height(subTree.rightChild); return (i<j)?(j+1):(i+1); } } /** * 获取数的节点数 * @param subTree * @return */ private int size(TreeNode subTree){ if(subTree==null){ return 0; }else{ return 1+size(subTree.leftChild)+size(subTree.rightChild); } } /** * 获取节点的双亲节点 * @param element * @return */ public TreeNode parent(TreeNode element){ return (root==null|| root==element)?null:parent(root, element); } public TreeNode parent(TreeNode subTree,TreeNode element){ if(subTree==null) return null; if(subTree.leftChild==element||subTree.rightChild==element) //返回父结点地址 return subTree; TreeNode p; //现在左子树中找,如果左子树中没有找到,才到右子树去找 if((p=parent(subTree.leftChild, element))!=null) //递归在左子树中搜索 return p; else //递归在右子树中搜索 return parent(subTree.rightChild, element); } //前序遍历 public void preOrder(TreeNode subTree){ if(subTree!=null){ visted(subTree); preOrder(subTree.leftChild); preOrder(subTree.rightChild); } } //中序遍历 public void inOrder(TreeNode subTree){ if(subTree!=null){ inOrder(subTree.leftChild); visted(subTree); inOrder(subTree.rightChild); } } //后续遍历 public void postOrder(TreeNode subTree) { if (subTree != null) { postOrder(subTree.leftChild); postOrder(subTree.rightChild); visted(subTree); } } public void visted(TreeNode subTree){ System.out.print(subTree.data); } /** * 二叉树的节点数据结构 */ private class TreeNode{ private String data=null; private TreeNode leftChild=null; private TreeNode rightChild=null; public TreeNode(){} public TreeNode(String data){ this.data=data; } } public static void main(String[] args) { BinaryTree bt = new BinaryTree(); bt.createBinTree(); System.out.println("tree height:"+bt.height(bt.root)); bt.preOrder(bt.root); System.out.println("---前序遍历"); bt.inOrder(bt.root); System.out.println("---中序遍历"); bt.postOrder(bt.root); System.out.println("---后序遍历"); } }
执行结果:
tree height:3
ABDECF---前序遍历
DBEAFC---中序遍历
DEBFCA---后序遍历
相关文章推荐
- 数据结构--数组和ArrayList
- 【数据结构】 二叉树
- 数据结构之优先级队列(二叉堆)
- 常用树类数据结构总结-二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)的性能分析
- C++算法与数据结构学习笔记------单链表实现多项式
- 数据结构初学过程中对算法效率度量的理解
- 【数据结构】二叉树相关操作
- Java千百问_06数据结构(006)_java基本数据类型如何转换
- 二叉树主要知识点总结
- HDU 2874 LCA转RMQ+并查集
- HDU 2586 LCA转RMQ
- ZOJ 3195 LCA转RMQ
- 数据结构 并查集
- Java千百问_06数据结构(005)_数值中为什么会出现下划线
- 数据结构之排序:归并排序
- 数据结构之排序:堆排序
- 数据结构之排序:直接选择排序
- nginx的高级数据结构
- LINUX 数据结构 &算法 网络协议 & 网络编程 多任务编程
- 02-线性结构1 一元多项式的乘法与加法运算[网易云课堂-数据结构]