一、二叉树的一些概念
1.定义
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个由层次关系的集合。
2.树的特点
每个结点有零个或多个子结点。没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树。
3.节点的度
一个节点含有的子树个数称为该节点的度。
4.叶节点或终端节点
度为0的节点称为叶子节点。
5.兄弟节点
具有相同父节点的节点互称为兄弟节点。
6.树的度
一棵树中,最大节点的度称为树的度。
7.树的高度
树中节点的最大层次。
8.堂兄弟节点
双亲在同一层,但父节点不同的节点互称为堂兄弟节点。
9.节点的祖先
从根到该节点所经分支上的所有节点。
10.森林
由m(m>=0)棵互不相交的树的集合称为森林。
二、树的表示方法
1.孩子兄弟表示法
class Node{
int value;//结点中的数据域
Node firstChild;//保存第一个孩子结点
Node nextChild;//保存同一层的下一个兄弟结点
}
2.双亲表示法
用一组连续的空间来存储树中的结点,在保存结点的同时附设一个指示器指示其双亲结点在表中的wei
class TNode{
int data;//值
int parent;//父结点的下标
}
3.孩子表示法
这种方法通常是把每个结点的孩子结点排列起来,构成一个单链表,称为孩子链表。n个结点共有n个孩子链表(叶子结点的孩子链表为空表),而n个结点的数据和n个孩子链表的头结点的头指针又组成一个顺序表。
//孩子链表结点的定义
class ChildNode{
int child;//孩子结点在线性表中的位置
ChildNode next;//下一个孩子结点
}
//顺序表结点的结构定义
class DataNode{
int data;//结点的值
ChildNode firstChild;//指向第一个孩子结点的引用
}
//树的定义
class ChildTree{
DataNode nodes[MAX];//顺序表
int root;//该树的根结点在线性表中的位置
int num;//该树的结点个数
}
三、二叉树的概念和结构
1.概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵被称为左子树和右子树的二叉树组成。
2.二叉树的特点
1)每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
2)二叉树的子树有左右之分,其子树的次序不能颠倒。
3.二叉树的性质
性质1:在二叉树的第i层上至多有2^(i-1)个结点。
性质2:深度为k的二叉树至多有2^k-1(k>=1)个结点。
性质3:对任意一棵二叉树T,若终端结点数(叶子结点数)为n0,而度数为2的结点数为n2,则n0=n2+1。
4.特殊的二叉树
1)满二叉树:一个二叉树,如果每一层的结点数都达到最大值,那么这个二叉树就是满二叉树。容易发现,满二叉树除叶子结点外,每个结点的度均为2.如果一个二叉树的层数为k,且结点总数是2^k-1,则它是满二叉树。
2)完全二叉树:对于深度为k,有n个结点的二叉树,当且仅当每一个结点都与深度为k的满二叉树中编号从1~n的结点一一对应时,称为完全二叉树。
由此可见,满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树。
3.完全二叉树的性质
性质1:具有n个结点的完全二叉树的深度为[log(n)]+1。(下取整)
性质2:对于具有n个结点的完全二叉树,如果按照从上到下和从左到右的顺序对二叉树中所有结点从1开始顺序编号,则对任意的序号为i的结点有:
若i=1,则序号为i的结点是根结点,无双亲结点;若i>1,则序号为i的结点的双亲结点的标号为[i/2]。
若2i>n,则序号为i的结点无左孩子;若2i<=n,则序号为i的结点的左孩子结点的序号为2i。
若2i+1>n,则序号为i的结点无右孩子;若2i+1<=n,则序号为i的结点的右孩子结点为2i+1。
四、二叉树的存储结构
1.顺序存储
使用数组来存储,一般只适合完全二叉树(不会有空间的浪费)。二叉树顺序存储物理上是一个数组,逻辑上是一棵二叉树。现实中,我们通常把堆(一种二叉树)使用顺序结构的数组来存储。
2.链式存储
用链表来表示一棵二叉树。链式结构又分为二叉链和三叉链。
//二叉链-保存左右孩子
class BinaryNode{
int value;//值域
Node left;//保存左孩子
Node right;//保存右孩子
}
//三叉链-保存双亲结点和左右孩子
class BinaryTreeNode{
int value;//值域
Node parent;//保存双亲结点
Node left;//保存左孩子结点
Node right;//保存右孩子结点
}
五、二叉树的遍历、求结点个数、高度、查找结点、叶子结点个数、第k层的结点数
package com.xunpu.datastruct.tree;
public class TreeMethod {
private static int count=0;
private static class Node{
char val;//值
Node left;//左子树
Node right;//右子树
Node(char val){
this.val=val;
}
}
//前序遍历(根-左-右)
//终止条件:根为空(遇到叶子节点) 先打印根的值,然后遍历左子树、右子树
public static void preOrderTravel(Node root){
if(root==null){
return;
}
if(root!=null){
System.out.print(root.val+" ");
}
preOrderTravel(root.left);
preOrderTravel(root.right);
}
//中序遍历(左-根-右)
//终止条件:根为空(遇到叶子节点)
public static void inOrderTravel(Node root){
if(root==null){
return;
}
inOrderTravel(root.left);
System.out.print(root.val+" ");
inOrderTravel(root.right);
}
//后序遍历(左-右-根)
//终止条件:根为空(遇到叶子节点)
public static void postOrderTravel(Node root){
if(root==null){
return;
}
postOrderTravel(root.left);
postOrderTravel(root.right);
System.out.print(root.val+" ");
}
//求二叉树的结点个数(用遍历的思想解决 前序遍历)
public static int getCounts(Node root){
if(root!=null){
getCounts(root.left);
getCounts(root.right);
count++;
}
return count;
}
//求二叉树的结点个数(子问题思想解决) 分别计算出左右子树的结点个数,然后再加1(根结点)
//终止条件:根结点为空&遇到叶子结点
private static int count(Node root){
if(root==null){
return 0;
}else if(root.left==null&&root.right==null){
return 1;//可选
}else{
return count(root.left)+count(root.right)+1;
}
}
//求叶子结点个数
//子问题思想 分别计算左子树、右子树的叶子结点
//终止条件:根为空 & 遇到叶子结点
public static int leafCount(Node root){
if(root==null){
return 0;
}
if(root.left==null&&root.right==null){
return 1;
}
return leafCount(root.left)+leafCount(root.right);
}
//求树的高度
//分别求出左右子树的高度,取较大值,最后返回较大值+1.
public static int height(Node root){
if(root==null){
return 0;
}
int leftHeight=height(root.left);
int rightHeight=height(root.right);
return (leftHeight>rightHeight?leftHeight
:rightHeight)+1;
}
//求树的第k层的结点个数
//其实就是求第k-1层左右子树之和。递归思想
private static int kLevel(Node root,int k){
if(root==null) {
return 0;
}else if(k==1){
return 1;
}else{
return kLevel(root.left,k-1)+kLevel(root.right,k-1);
}
}
//查找值为v的结点
//遍历思想 先判断根结点是不是,再从左子树、右子树找。
public static Node find(Node root,char v){
if(root==null){
return null;
}
if(root.val==v){
return root;
}
Node left=find(root.left,v);
Node right=find(root.right,v);
if(left!=null){
return left;
}else if(right!=null){
return right;
}
return null;
}
private static Node createTree(){
Node a=new Node('A');
Node b=new Node('B');
Node c=new Node('C');
Node d=new Node('D');
Node e=new Node('E');
Node f=new Node('F');
Node g=new Node('G');
Node h=new Node('H');
a.left=b;a.right=c;
b.left=d;b.right=e;
e.right=h;
c.left=f;
c.right=g;
return a;
}
public static void main(String[] args) {
Node root=createTree();
System.out.print("前序遍历顺序为:");
preOrderTravel(root);
System.out.println();
System.out.print("中序遍历顺序为:");
inOrderTravel(root);
System.out.println();
System.out.print("后序遍历顺序为:");
postOrderTravel(root);
System.out.println();
System.out.println("二叉树的结点个数为:"+count(root));
System.out.println("二叉树的叶子结点个数为:"+leafCount(root));
System.out.println("二叉树的高度为:"+height(root));
System.out.println("值为‘F’的结点为:"+find(root,'F'));
}
}