树型结构是一类重要的非线性数据结构,相对于线性结构,树型结构的查找性能与插入性能都比线性结构好一些。其中又以树和二叉树最为常用,直观看来,树是以分支关系定义的层次结构。下面将重点介绍二叉树。
基本概念
基本术语
度:结点拥有的子树数称为该结点的度(degree)。
叶子结点(终端结点):度为0的结点称为叶子结点(leaf)。
分支结点(非终端结点):度不为的结点称为分支结点。除根结点外,分支结点也称为内部结点。
树的度:树的度是树内各结点的度的最大值。
孩子,双亲,兄弟,祖先,子孙:结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(parent),同一个双亲的孩子之间互称兄弟(Sibling),从根到该结点所经分支上的所有结点都是该结点的祖先,反之,以该结点为根的子树中的任一结点都称为该结点的子孙。
结点的层次:结点的层次(Level)从根开始定义,根为第一层,根的孩子为第二层。
深度:树中结点的最大层次称为树的深度或者高度(Depth)。
森林:森林(Forest)是 m(m>=0) 棵互不相交的树的集合。
- 二叉树的定义
二叉树(Binary Tree)是一种特殊的树型结构,它的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。
- 满二叉树
一棵深度为 k 且有 2k-1 个结点的二叉树称为满二叉树。
- 完全二叉树
对满二叉树的结点进行连续编号,约定编号从根结点起,自上而下,自左而右,由此可以引出完全二叉树的定义。
深度 k 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应时,称之为完全二叉树。
- 二叉树的性质
1):在二叉树的第 i 层上至多有 2i-1 个结点(i>=1)。
2):深度为 k 的二叉树至多有 2k-1 个结点(k>=1).
3):对任何一棵二叉树T,如果其终端结点数为 n0,度为 2 的结点数为 n2,则 n0=n2+1。
- 完全二叉树的两个重要特性(以下用[x]表示不大于x的最大整数):
1):具有 n 个结点的完全二叉树的深度为 [log2n]+1。
2):如果对一棵有 n 个结点的完全二叉树(其深度为 [log2n]+1)的结点按层序编号(从第1层到第 [log2n]+1层,每层从左到右),则对任一结点 i (1<=i<=n)有:
- 如果 i=1,则结点 i 是二叉树的根,无双亲;如果 i>1 ,则其双亲PARENT(i) 是结点 [i/2]。
- 如果 2i > n ,则结点 i 无左孩子(结点 i 为叶子结点);否则其左孩子 LCHILD(i) 是结点 2i。
- 如果 2i+1 > n ,则结点 i 无右孩子;否则其右孩子 RCHILD(i) 是结点 2i+1。
二叉树的存储结构
1.顺序存储结构:
顺序存储结构仅适用于完全二叉树;其结点次序有以下特性:
- 第n个元素的左子节点是:2n+1
- 第n个元素的右子节点是:2n+2
- 第n个元素的父节点是:(n-1)/2
下面将通过这种特性实现其顺序存储的底层实现。
2.链式存储结构:
由二叉树的定义可知,二叉树的结点由一个数据元素和分别指向左,右子树的两个分支构成。则表示二叉树的链表中的结点至少包含3个域:数据域,左,右指针域。链表的头指针指向二叉树的根结点。
下面也将实现二叉树的链式存储结构的底层。
二叉树的遍历
定义:按某条搜索路径巡访树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
分类
- 前序遍历
若二叉树为空,则空操作,否则:
访问根结点->前序遍历左子树->前序遍历右子树。
- 中序遍历
若二叉树为空,则空操作,否则:
中序遍历左子树->访问根结点->中序遍历右子树。
- 后序遍历
若二叉树为空,则空操作,否则:
后序遍历左子树->后序遍历右子树->访问根结点。
二叉树的底层实现
二叉树的顺序存储结构实现
ArrayBinaryTree类:
package other;
public class ArrayBinaryTree {
//顺序存储二叉树
//使用数组存储节点数据
int[] data;
public ArrayBinaryTree(int[] data) {
this.data = data;
}
//前序遍历(指定起始位置)
public void frontShow() {
frontshow(0);
}
//从指定位置开始前序遍历
public void frontshow(int index) {
if(data==null||data.length==0) {
return;
}
//遍历当前节点
System.out.print(data[index]+" ");
//遍历左子节点
if(2*index+1<data.length) {
frontshow(2*index+1);
}
//遍历右子节点
if(2*index+2<data.length) {
frontshow(2*index+2);
}
}
}
简单Test:
package classify;
import other.ArrayBinaryTree;
public class ArrayBinaryTreeTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//顺序存储二叉树(应用于另一种数据结构:堆。如堆排序等等)
int[] data = {1,2,3,4,5,6,7};
//创建一颗二叉树
ArrayBinaryTree tree = new ArrayBinaryTree(data);
//前序遍历
System.out.print("前序遍历:");
tree.frontShow(); //前序遍历:1 2 4 5 3 6 7
System.out.println();
}
}
二叉树的链式存储结构实现
结点类(TreeNode):
- void frontShow() 前序遍历
- void midShow() 中序遍历
- void afterShow() 后序遍历
- TreeNode frontSearch(int volue) 前序查找,以前序遍历的方式查找的值为value的结点,并返回该结点
- void delete(int value) 删除以值为value的结点为根的子树
package util;
public class TreeNode {
//链式存储的二叉树
//节点的权
public int value;
//左儿子
TreeNode leftnode;
//右儿子
TreeNode rightnode;
public TreeNode(int value) {
this.value=value;
}
//设置左儿子
public void setleftnode(TreeNode node) {
this.leftnode=node;
}
//设置右儿子
public void setrightnode(TreeNode node) {
this.rightnode=node;
}
//前序遍历
public void frontShow() {
//遍历当前节点的内容
System.out.print(value+" ");
//遍历左节点
if(leftnode!=null) {
leftnode.frontShow();
}
//遍历右节点
if(rightnode!=null) {
rightnode.frontShow();
}
}
//中序遍历
public void midShow() {
//遍历左节点
if(leftnode!=null) {
leftnode.midShow();
}
//遍历当前节点的内容
System.out.print(value+" ");
//遍历右节点
if(rightnode!=null) {
rightnode.midShow();
}
}
//后序遍历
public void afterShow() {
//遍历左节点
if(leftnode!=null) {
leftnode.afterShow();
}
//遍历右节点
if(rightnode!=null) {
rightnode.afterShow();
}
//遍历当前节点的内容
System.out.print(value+" ");
}
//前序查找
public TreeNode frontSearch(int value) {
TreeNode target=null;
//对比当前节点的值
if(this.value==value) {
return this;
}
//当前节点的值不是要查找的节点,先查找左儿子
if(leftnode!=null) {
//有可能可以查到,也可能查不到,如果查不到,target还是bull
target = leftnode.frontSearch(value);
}
//如果查到了,直接返回
if(target!=null) {
return target;
}
//如果还没查到,再查找右儿子
if(rightnode!=null) {
target = rightnode.frontSearch(value);
}
//返回最终结果
return target;
}
//删除子树
public void delete(int value) {
TreeNode parent = this;
//判断删除要节点是否在左儿子
if(parent.leftnode!=null&&parent.leftnode.value==value) {
parent.leftnode=null;
return;
}
//判断删除要节点是否在右儿子
if(parent.rightnode!=null&&parent.rightnode.value==value) {
parent.rightnode=null;
return;
}
//递归检查左儿子
parent=leftnode;
if(parent!=null) {
parent.delete(value);
}
//递归检查右儿子
parent=rightnode;
if(parent!=null) {
parent.delete(value);
}
}
}
二叉树类(BinaryTree):
- TreeNode getRoot() 获取根结点
- void frontshow() 前序遍历
- void midshow() 中序遍历
- void aftershow() 后序遍历
- TreeNode frontsearch(int volue) 前序查找,前序遍历查找指定值的结点
- void delete(int value) 删除以值为value的结点为根的子树
package other;
import util.TreeNode;
public class BinaryTree {
//链式存储的二叉树
//创建根节点
TreeNode root;
//设置根节点
public void setRoot(TreeNode node) {
this.root=node;
}
//获取根节点
public TreeNode getRoot() {
return this.root;
}
//前序遍历
public void frontshow() {
if(root!=null)
root.frontShow();
}
//中序遍历
public void midshow() {
if(root!=null)
root.midShow();
}
//后序遍历
public void aftershow() {
if(root!=null)
root.afterShow();
}
//前序遍历查找指定值的节点
public TreeNode frontsearch(int value) {
return root.frontSearch(value);
}
//删除子树
public void delete(int value) {
if(root.value==value) {
root = null;
}else {
root.delete(value);
}
}
}
简单Test:
package classify;
import other.BinaryTree;
import util.TreeNode;
public class BinaryTreeTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//链式存储的二叉树
//创建一颗二叉树
BinaryTree bintree = new BinaryTree();
//创建一个根节点
TreeNode root = new TreeNode(1);
//把根节点赋给树
bintree.setRoot(root);
//创建一个左节点,把设置为根节点的子节点
TreeNode rootl = new TreeNode(2);
root.setleftnode(rootl);
//创建一个右节点,把设置为根节点的子节点
TreeNode rootr = new TreeNode(3);
root.setrightnode(rootr);
//为第二层的左节点创建两个子节点
rootl.setleftnode(new TreeNode(4));
rootl.setrightnode(new TreeNode(5));
//为第二层的左节点创建两个子节点
rootr.setleftnode(new TreeNode(6));
rootr.setrightnode(new TreeNode(7));
System.out.print("前序遍历:");
//前序遍历树
bintree.frontshow(); // 前序遍历:1 2 4 5 3 6 7
System.out.println();
System.out.print("中序遍历:");
//中序遍历树
bintree.midshow(); // 中序遍历:4 2 5 1 6 3 7
System.out.println();
System.out.print("后序遍历:");
//后序遍历树
bintree.aftershow(); // 后序遍历:4 5 2 6 7 3 1
System.out.println();
//根据值前序查找(同样也有中序查找,后序查找)
TreeNode result = bintree.frontsearch(5);
System.out.println(result);//返回节点地址 util.TreeNode@15db9742
//根据值删除一个节点(即普通二叉树删除子树)
bintree.delete(2);
//检查删除结果
System.out.print("删除后的前序遍历:");
bintree.frontshow(); // 删除以2为根的子树后的前序遍历:1 3 6 7
System.out.println();
}
}