一、树的简介
树( tree )是n (n≥0)个结点的有限集。
1)或者是一棵空树( n = 0 ),空树中不包含任何结点。
2) 或者是一棵非空树(n >0 ),此时有且仅有一个特定的称为根( root )的结点;当n > 1时,其余结点可分为m ( m > 0 )个互不相交的有限集T1,T 2 , … ,T m ,其中每一个本身又是一棵树,并且称为根的子树( sub tree ) ).
例如图( a )是一棵空树、( b )是只有一个根节点的树、©是一棵有10个结点的树,其中A是根,其余的结点分成3个不相交的集合:T1 =B,E,F)、T2=(C,G)、T3 ={D,H,L,J},每个集合都构成一棵树,且都是根A的子树。
1.1 结点的度与树的度
结点拥有的子树的数目称为结点的度( Degree ) )。度为0的结点称为叶子( leaf )或终端结点。
度不为0的结点称为非终端结点或分支结点。除根之外的分支结点也称为内部结点。树内各结点的度的最大值称为树的度
1.2 结点的层次和树的深度
结点的层次( level) )从根开始定义,层次数为1的结点是根结点,其子树的根的层次数为2。树中结点的最大层次数称为树的深度(Depth )或高度。
1.3 父亲、儿子、兄弟
父亲( parent ) :一个结点的直接前驱结点
儿子( child ) :一个结点的直接后继结点
兄弟( sibling ) :同一个父亲结点的其他结点
结点A是结点B、C、D的父亲,结点B、C、D是结点A的孩子。由于结点H、I、J有同一个父结点D,因此它们互为兄弟。
1.4 有序树、m叉树、森林
如果将树中结点的各子树看成是从左至右是有次序的,则称该树为有序树;若不考虑子树的顺序则称为无序树。
对于有序树,我们可以明确的定义每个结点的第一个孩子、第二个孩子等,直到最后一个孩子。若不特别指明,一般讨论的树都是有序树。
树中所有结点最大度数为m的有序树称为m叉树。
森林( forest )是m ( m >=0 )棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。树和森林的概念相近。删去一棵树的根,就得到一个森林;反之,加上一个结点作树根,森林就变为一棵树。
二、二叉树
每个结点的度均不超过2的有序树,称为二叉树( binary tree ) .与树的递归定义类似,二叉树的递归定义如下:
二叉树或者是一棵空树,或者是一棵由一个根结点和两棵互不相交的分别称为根的左子树和右子树的子树所组成的非空树。
由以上定义可以看出,二叉树中每个结点的孩子数只能是0、1或2个,并且每个孩子都有左右之分。位于左边的孩子称为左孩子,位于右边的孩子称为右孩子;以左孩子为根的子树称为左子树,以右孩子为根的子树称为右子树。
2.1 满二叉树
高度为k并且有2k*1-1个结点的二叉树。
在满二叉树中,每层结点都达到最大数,即每层结点都是满的,因此称为满二叉树。
2.2完全二叉树
若在一棵满二叉树中,在最下层从最右侧起去掉相邻的若干叶子结点,得到的二叉树即为完全二叉树。
满二叉树必为完全二叉树,而完全二叉树不一定是满二叉树
2.3 二叉树的性质
件质1:在二叉树的第i层上最多有2^(i-1)个结点(根是第1层)
性质2:高度为h的二叉树至多有2^h-1个结点
性质3:对任何一棵二叉树T,如果其终端结点数为no,度为2的结点数为n2,则no = n2+1
性质4:有n个结点的完全二叉树的高度为[log2n]+1,其中[log2n]是向下取整
性质5∶含有n>=1个结点的二叉树的高度至多为n-1;高度至少为[log2n]+1,其中[log2n]是向下取整
性质6:如果对一棵有n个结点的完全二叉树的结点进行编号,则对任一结点i(1≤i ≤n),有
1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲结点PARENT(i)是结点[i/2]
2)如果 2i>n,则结点i无左孩子;否则其左孩子是结点2i。
3) 如果2i+1>n ,则结点i无右孩子;否则其右孩了是结点2i+1。
1.4 二叉树的存储结构
二叉树的存储结构有两种:顺序存储结构和链式存储结构。
顺序存储结构
对于满二叉树和完全二叉树来说,可以将其数据元素逐层存放到一组连续的存储单元中。用一维数组来实现顺序存储结构时,将二叉树中编号为i的结点存放到数组中的第i个分量中。如此根据二叉树性质,可以得到结点i的父结点、左右孩子结点分别存放在、2i以及2i+1分量中.
详细过程如下:
顺序存储二叉树并进行前序、中序、后序遍历
链式存储结构
设计不同的结点结构可构成不同的链式存储结构。
在二叉树中每个结点都有两个孩子,则可以设计每个结点至少包括3个域:数据域、左孩子域和右孩子域。
数据域存放数据元素,左孩子域存放指向左孩子结点的指针,右孩子域存放指向右孩子结点的指针。如图( a )所示。利用此结点结构得到的二叉树存储结构称为二叉链表。
为了方便找到父结点,可以在上述结点结构中增加一个指针域,指向结点的父结点。如图( b )所示。采用此结点结构得到的二叉树存储结构称为三叉链表。
1.5 遍历(Traverse) :
就是按照某种次序访问树中的所有结点,且每个结点恰好访问一次。
也就是说,按照被访问的次序,可以得到由树中所有结点排成的一个序列。树的遍历也可以看成是人为的将非线性结构线性化。
这里的“访问”是广义的,可以是对结点作各种处理,例如输出结点信息、更新结点信息等。
在我们的实现中,并不真正的“访问”这些结点,而是得到一个结点的线性序列,以线性表的形式输出
将整个二叉树看做三部分:根、左子树、右子树。如果规定先遍历左子树、再遍历右子树。
那么根据根的遍历顺序就有三种遍历方式
左子树右子树根
先序/根遍历DLR:根 左子树 右子树
中序/根遍历LDR:左子树 根 右子树
后根/序遍历LRD:左子树 右子树 根
注意:由于树的递归定义,其实对三种遍历的概念其实也是一个递归的描述过程
先序遍历DLR : 1 4 5 2 3 6 7
中序遍历LDR: 4 5 1 3 2 6 7
后序遍历LRD: 5 4 3 7 6 2 1
链式存储二叉树并进行先序、中序、后序遍历
创建节点类
package tree;
public class Node {
int value;//节点值
Node rightchiled;//指向右孩子节点
Node leftchiled;//指向左孩子节点
public Node(int value){
super();
this.value=value;
}
public Node(int value,Node rightchiled,Node leftchiled){
super();
this.value=value;
this.rightchiled=rightchiled;
this.leftchiled=leftchiled;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
", rightchiled=" + rightchiled +
", leftchiled=" + leftchiled +
'}';
}
}
创建接口类
package tree;
//可以有不同的实现类,每个类可以使用不同的·存储结构,比如顺序结构、链式结构
public interface BinaryTree {
/**
* 是否空树
*/
public boolean isEmpty();
/**
* 树节点数量
*/
public int size();
/**
* 获取二叉树高度
*/
public int getHeight();
/**
* 查询指定值的节点
*/
public Node findKey(int value);
/**
* 前序递归遍历
*/
public void preOrderTraverse();
/**
* 中序递归遍历
*/
public void inOrderTraverse();
/**
* 后序递归遍历
*/
public void postOrderTraverse();
}
创建实现类
package tree;
public class LinkedBinaryTree implements BinaryTree{
private Node root;//根节点
public LinkedBinaryTree(){
super();
}
public LinkedBinaryTree( Node root){
super();
this. root=root;
}
@Override
public boolean isEmpty() {
return root==null;
}
//获取二叉树的节点个数
@Override
public int size() {
System.out.println("输出二叉树节点个数:");
return this.size(root);
}
private int size(Node root){
if(root==null){
return 0;
}else{
//获取左子树的节点个数
int countl=this.size(root.leftchiled);
//获取右子树的节点个数
int countr=this.size(root.rightchiled);
//返回左子树+右子树节点+1
return countl+countr+1;
}
}
//获取二叉树的高度
@Override
public int getHeight() {
System.out.println("输出树的高度");
return this.height(root);
}
private int height(Node root){
if(root==null){
return 0;
}else{
//获取左子树高度
int nl=this.height(root.leftchiled);
//获取右子树高度
int nr=this.height(root.rightchiled);
//输出左子树、右子树中较大的高度并加1
return nl>nr?nl+1:nr+1;
}
}
// 查询指定值的节点
@Override
public Node findKey(int value) {
System.out.println("找到的节点为:");
return this.key(value,root);
}
private Node key(int value,Node root){
if(root==null){//递归结束条件一:节点为空,可能是根节点也可能是左右孩子节点
return null;
}else if(root!=null && value==root.value){//递归结束条件二:找到对应值节点
return root;
}else{
//递归查找
Node node1=this.key(value,root.leftchiled);
Node node2=this.key(value,root.rightchiled);
if(node1!=null && node1.value==value){
return node1;
}else if(node2!=null && node2.value==value){
return node2;
}else{
return null;
}
}
}
@Override
public void preOrderTraverse() {
//1、输出根结点的值
if(root!=null) {
System.out.print(root.value+" ");
//2、对左子树进行先序遍历
//构建二叉树,跟是左子树的根
BinaryTree leftTree=new LinkedBinaryTree(root.leftchiled);
leftTree.preOrderTraverse();
//3、对右子树进行先序遍历
BinaryTree rightTree=new LinkedBinaryTree(root.rightchiled);
rightTree.preOrderTraverse();
}
}
//中序遍历
@Override
public void inOrderTraverse() {
System.out.println("输出中序遍历结果:");
this.inOrder(root);//调用inOrder方法
System.out.println();
}
//中序遍历
private void inOrder(Node root){
if(root!=null){
//遍历左子树
this.inOrder(root.leftchiled);
//输出根节点
System.out.print(root.value+" ");
//遍历右子树
this.inOrder(root.rightchiled);
}
}
//后序遍历
@Override
public void postOrderTraverse() {
System.out.println("输出后序遍历结果:");
this.postOrder(root);
System.out.println();
}
private void postOrder(Node root){
if(root!=null){
//遍历左子树
this.postOrder(root.leftchiled);
//遍历右子树
this.postOrder(root.rightchiled);
//输出根节点
System.out.print(root.value+" ");
}
}
}
创建测试类
package tree;
public class BinaryTest {
public static void main(String[] args) {
//创建一个二叉树
Node node5=new Node(5,null,null);
Node node4=new Node(4,node5,null);
Node node7=new Node(7,null,null);
Node node6=new Node(6,node7,null);
Node node3=new Node(3,null,null);
Node node2=new Node(2,node6,node3);
Node node1=new Node(1,node2,node4);
BinaryTree btree=new LinkedBinaryTree(node1);
//判断二叉树是否为空
//先序遍历递归
System.out.println("输出先序遍历结果:");
btree.preOrderTraverse();
System.out.println();
//中序遍历递归
btree.inOrderTraverse();
//后序遍历递归
btree.postOrderTraverse();
//获取树的高度
int height=btree.getHeight();
System.out.println(height);
//获取树的节点数量
int count=btree.size();
System.out.println(count);
//查找指定结点的值
Node node=btree.findKey(7);
System.out.println(node);
}
}
测试结果
输出先序遍历结果:
1 4 5 2 3 6 7
输出中序遍历结果:
4 5 1 3 2 6 7
输出后序遍历结果:
5 4 3 7 6 2 1
输出树的高度
4
输出二叉树节点个数:
7
找到的节点为:
Node{value=7, rightchiled=null, leftchiled=null}
Process finished with exit code 0