树的定义:树是n(n>0)个结点的有穷集合。
(1) 有且仅有一个称为根的结点;
(2) 其余结点分为m(m>=0)个互不相交的非空集合T1,T2…Tm,这些集合中的每一个都是一棵树,称为根的子树。
在树上,根结点没有直接前趋。
树形结构的术语及其含义:
(1) 度:树上任一结点所拥有的子树的数目称为该结点的度。
(2) 叶子或终端结点:度为0的结点。
(3) 非终端结点或分支点:度大于0的结点。
(4) 树的度:一棵树中所有结点的度的最大值。
(5) 双亲或父结点:若树中结点A是结点B的直发前趋,则A是B的双亲或父结点。
(6) 孩子或子结点:B为A的孩子或子结点。
(7) 兄弟:父结点相同的结点互称为兄弟。
(8) 子孙:一棵树上的任何结点(不包括根结点)称为根的子孙。
(9) 祖先:若B是A的子孙则A是B的祖先。
(10) 结点的层数:从根结点开始算起,根的层数为1,其余结点的层数为其双亲的层数加1。
(11) 树的高度或深度:一棵树中所有结点层数的最大值。
二叉树
二叉树的定义:二叉树结点的有有穷集合,它或者是空集,或者同时满足下述两个条件:
(1) 有且仅有一个称为根的结点;
(2) 其余结点分为两个互不相交的集合T1、T2,T1与T2都是二叉树,并且T1与T2有顺序关系(T1在T2之前),它们分别称为根的左子树和右子树。
二叉树与树的区别:
(1) 二叉树可以是空集,这种二叉树称为空二叉树。
(2) 二叉树的任一结点都有两棵子树,并且这两棵子树之间有顺序关系,位置不能交换。
(3) 二叉树上任一结点的度定义为该结点的孩子数。
二叉树有五种基本形态:
满二叉树:深度为k(k>=1)且有 -1个结点的二叉树。满二叉树上各层的结点数已达到了二叉树可以容纳的最大值。
完全二叉树:如果在一棵深度为k(k>=1)的满二叉树上删去第k层上最右边的连续j(0<=j< )个结点,就得到一棵深度为k的完全正确二叉树。
二叉树的性质:
(1) 二叉树第i(i>=1)层上至多有 个结点。
(2) 深度为k(k>=1)的二叉树至多有 -1个结点。
(3) 对任何二叉树,若2度结点数为 ,则叶子数 = +1。
(4) 具有n个结点的完全二叉树的深度为 。
(5) 如果将一棵有n个结点的完全二叉树按层编号,则对任一编号为i(1<=i<=n)的结点X有:
<1>若i=1,则结点X是根,若i>1则X的双亲PARENT(X)的编号为 。
<2>若2i>n,则结点X无左孩子(且无右孩子);否则,X的左孩子LCHILD(X)的编号为2i。
<3>若2i+1>n,则结点X无右孩子;否则,X的右孩子RCHILD(X)的编号为2i+1。
二叉树有两种存储结构:顺序存储结构和链式存储结构。
链式存储结构的二叉树称为二叉链表。如下:
Lchild Data Rchild
Data域:称为数据域,用于存储二叉树结点中的数据元素;
lchild域:称为左孩子指针域,用于存放指向本结点左孩子的指针(左指针)。Rchild域也相同。
根指针:每个二叉链表还必须有一个指向根结点的指针,该指针称为根指针。根指针具有标识二叉链表的作用,对二叉链表的访问只能从根指针开始。
注意:二叉链表中每个存储结点的每个指针域必须有一个值,这个值或者是指向该结点的一个孩子的指针,或者是空指针NULL。
typedef struct btnode *bitreptr;
Struct btnode {
datatype data;
bitreptr lchild, rchild;
}
bitreptr root;
若二叉树为空,则root=NULL。
顺序存储结构由一个一维数组构成。
存储策略:对于任何完全二叉树来说,可以采用“以编号为地址”的策略将结点存入作为顺序存储结构的一维数组。在这一存储结构中,由于一结点的存储位置(下标)也就是它的编号,故结点间的逻辑关系可通过它们下标间的数值关系确定。如:在bt中找结点F的左孩子,根据二叉树性质5,从F的下标值5可以算出其左孩子的下标值为10(2*5)。右孩子的下标值为11(2*5+1)。
由于二叉树的性质5对非完全二叉树一般不成立,因此在计算非完全二叉树结点的下标值时,首先必须将其转化为完全二叉树,可以在“残缺”位置上增设“虚结点”。各个“虚结点”在数组中用一个特殊记号“∧”表示。比较浪费空间。
二叉树的遍历:就是按某种次序系统地“访问”二叉树上的所有结点,使每个结点恰好被“访问”一次。并保证只被“访问”一次。
先根遍历:
(1) 访问根结点;
(2) 先根遍历左子树;
(3) 先根遍历右子树;
中根遍历:
(1) 中根遍历左子树;
(2) 访问根结点;
(3) 中根遍历右子树;
后根遍历:
(1) 后根遍历左子树;
(2) 后根遍历右子树;
(3) 访问根结点;
树的存储结构:
(1) 孩子链表表示法:树上的一个结点的内容(数据元素)以及指向该结点所有孩子的指针存储在一起以便于运算的实现。一个孩子链表是一个带头结点的单链表,单链表的头结点含两个域:数据域和指针域。其中,数据域用于存储结点X中的数据元素;指针域用于存放指向该单链表中第一个表结点(首结点)的指针。为了检索方便,所有头结点组织成一个数组,称为表头数组。
(2) 孩子兄弟链表表示法:包含三个域:数据域—用于存储树上结点中的数据元素;孩子域—用于存放指向本结点第一个孩子的指针;兄弟域—用于存放指向本结点下一个兄弟的指针。孩子兄弟链表的结构形式与二叉链表完全相同;但存储结点中指针的含义不同。二叉链表中存储结点的左、右指针分别指向左、右孩子;而孩子兄弟链表中存储结点的两个指针分别指向“长子”和“大弟”。
(3) 双亲表示法:树上每个结点的孩子可以有任意多个,但双新只有一个。通过指向双亲的指针而将树中所有结点组织在一起形成一种存储结构是十分简洁的。在双亲表示法下,每个存储结点由两个域组成:数据域—用于存储树上结点中的数据元素;指针域—用于指示本结点的双亲所在的存储结点。孩子结点的下标值大于其双亲结点的下标值,“弟”结点的下标值大于“兄”结点的下标值。于是若要查找任一下标值为i的结点X的子孙,只需在下标值大于i的结点中去查找。
遍历方法:
先根遍历:
(1) 访问根结点;
(2) 依次先根遍历根的各个子树T1,…,Tm;
后根遍历:
(1) 依次后根遍历根的各个子树T1,…,Tm;
(2) 访问根结点。
层次遍历:
(1) 若树非空,访问根结点;
(2) 若第1,…,i(i>=1)层结点已被访问,且第i+1层结点尚未访问,则从左到右依次访问第i+1层结点。
public class BinTree {
public final static int MAX=40;
private Object data; //数据元数
private BinTree left,right; //指向左,右孩子结点的链
BinTree []elements = new BinTree[MAX];//层次遍历时保存各个节点
int front;//层次遍历时队首
int rear;//层次遍历时队尾
private Object data; //数据元数
private BinTree left,right; //指向左,右孩子结点的链
public BinTree()
{
}
public BinTree(Object data)
{ //构造有值结点
this.data = data;
left = right = null;
}
public BinTree(Object data,BinTree left,BinTree right)
{ //构造有值结点
this.data = data;
this.left = left;
this.right = right;
}
public String toString()
{
return data.toString();
}
//前序遍历二叉树
public static void preOrder(BinTree parent){
if(parent == null)
return;
System.out.print(parent.data+" ");
preOrder(parent.left);
preOrder(parent.right);
}
//中序遍历二叉树
public void inOrder(BinTree parent){
if(parent == null)
return;
inOrder(parent.left);
System.out.print(parent.data+" ");
inOrder(parent.right);
}
//后序遍历二叉树
public void postOrder(BinTree parent){
if(parent == null)
return;
postOrder(parent.left);
postOrder(parent.right);
System.out.print(parent.data+" ");
}
// 层次遍历二叉树
public void LayerOrder(BinTree parent)
{
elements[0]=parent;
front=0;rear=1;
while(front<rear)
{
try
{
if(elements[front].data!=null)
{
System.out.print(elements[front].data + " ");
if(elements[front].left!=null)
elements[rear++]=elements[front].left;
if(elements[front].right!=null)
elements[rear++]=elements[front].right;
front++;
}
}catch(Exception e){break;}
}
}
//返回树的叶节点个数
public int leaves()
{
if(this == null)
return 0;
if(left == null&&right == null)
return 1;
return (left == null ? 0 : left.leaves())+(right == null ? 0 : right.leaves());
}
//结果返回树的高度
public int height()
{
int heightOfTree;
if(this == null)
return -1;
int leftHeight = (left == null ? 0 : left.height());
int rightHeight = (right == null ? 0 : right.height());
heightOfTree = leftHeight<rightHeight?rightHeight:leftHeight;
return 1 + heightOfTree;
}
//如果对象不在树中,结果返回-1;否则结果返回该对象在树中所处的层次,规定根节点为第一层
public int level(Object object)
{
int levelInTree;
if(this == null)
return -1;
if(object == data)
return 1;//规定根节点为第一层
int leftLevel = (left == null?-1:left.level(object));
int rightLevel = (right == null?-1:right.level(object));
if(leftLevel<0&&rightLevel<0)
return -1;
levelInTree = leftLevel<rightLevel?rightLevel:leftLevel;
return 1+levelInTree;
}
//将树中的每个节点的孩子对换位置
public void reflect()
{
if(this == null)
return;
if(left != null)
left.reflect();
if(right != null)
right.reflect();
BinTree temp = left;
left = right;
right = temp;
}
// 将树中的所有节点移走,并输出移走的节点
public void defoliate()
{
if(this == null)
return;
//若本节点是叶节点,则将其移走
if(left==null&&right == null)
{
System.out.print(this + " ");
data = null;
return;
}
//移走左子树若其存在
if(left!=null){
left.defoliate();
left = null;
}
//移走本节点,放在中间表示中跟移走...
innerNode += this + " ";
data = null;
//移走右子树若其存在
if(right!=null){
right.defoliate();
right = null;
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
BinTree e = new BinTree("E");
BinTree g = new BinTree("G");
BinTree h = new BinTree("H");
BinTree i = new BinTree("I");
BinTree d = new BinTree("D",null,g);
BinTree f = new BinTree("F",h,i);
BinTree b = new BinTree("B",d,e);
BinTree c = new BinTree("C",f,null);
BinTree tree = new BinTree("A",b,c);
System.out.println("前序遍历二叉树结果: ");
tree.preOrder(tree);
System.out.println();
System.out.println("中序遍历二叉树结果: ");
tree.inOrder(tree);
System.out.println();
System.out.println("后序遍历二叉树结果: ");
tree.postOrder(tree);
System.out.println();
System.out.println("层次遍历二叉树结果: ");
tree.LayerOrder(tree);
System.out.println();
System.out.println("F所在的层次: "+tree.level("F"));
System.out.println("这棵二叉树的高度: "+tree.height());
System.out.println("--------------------------------------");
tree.reflect();
System.out.println("交换每个节点的孩子节点后......");
System.out.println("前序遍历二叉树结果: ");
tree.preOrder(tree);
System.out.println();
System.out.println("中序遍历二叉树结果: ");
tree.inOrder(tree);
System.out.println();
System.out.println("后序遍历二叉树结果: ");
tree.postOrder(tree);
System.out.println();
System.out.println("层次遍历二叉树结果: ");
tree.LayerOrder(tree);
System.out.println();
System.out.println("F所在的层次: "+tree.level("F"));
System.out.println("这棵二叉树的高度: "+tree.height());
}