树和二叉树(Java)
树
树是重要的非线性数据结构,元素之间存在一对多的关系,直观的看也是以分支关系定义的层次结构,在客观世界中也广泛存在如文件层次结构等。
1.树的定义
- 树结构 (除了一个称为根的结点外)每个元素都有且仅有一
个直接前趋,有且仅有零个或多个直接后继。树是递归结构
2.树的特点
- 树的 根结点没有前驱结点 , 除根结点之外的所有结点
有且只有一个前驱结点 。 - 树中所有结点可以有零个或多个后继结点
3.基本概念
- 结点(node)—— 表示树中的元素,包括数据项及若干指向
其子树的分支;- 结点的度(degree)—— 结点拥有的子树个数;
- 树的度—— 各个结点的度的最大值 ;
- 叶子(leaf)—— 度为0 的结点;
- 孩子(child)—— 结点子树的根 称为该结点的孩子;
- 双亲(parents)—— 孩子 结点的上层结点 叫该结点的双亲
- 兄弟(sibling)—— 同一双亲的孩子;
- 结点的层次(level)—— 从根结点算起,根为第一层,它的
孩子结点为第二层,以此类推 ;- 深度(depth)—— 树中结点的最大层次数;
- 森林(forest)——m(m>=0) 棵互不相交的树的集合。
二叉树
1.二叉树定义
- 二叉树是n(n>=0) 个结点的有限集合 ,或为空树(n=0), 或由一个根结点和两棵分别 称为左子树和右子树的互不相交的二叉树构成
2.二叉树特点
- 二叉树中每个结点最多有两棵子树;二叉树每个于结点的度小于等于2;
- 左、右子树不能颠倒—— 有序树;
- 二叉树是递归的结构,在二叉树的定义中又用到了二叉树的概念。
3.二叉树性质
- 一棵非空在二叉树的第 i 层上至多有 2 i-1 个
点 结点(i >=1)。- 一棵深度为k 的二叉树中,最多具有2 k -1个 结点,具有2 k-1个结点的树,就是满二叉树。
- 对于一棵非空的二叉树,如果叶子结点数为n 0 ,度数为2 的结点数为n 2 有 ,则有: n 0 = n 2 +1。
- 具有n 个结点的完全二叉树的深度k为 为 ⌊ log n ⌋ +1(log以2为底n的对数) 。
-若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意 一个编号为 i 的结点有以下性质(i>=0):
- 节点为i 的左孩子结点编号为2i+1,右孩子结点编号为 2i+2; ;
- 若 i=0 ,则该结点是二叉树的根,无双亲;否则i>1 ,其双亲结点的编号为 ⌊(i-1)/2⌋ ;
- 若 2i+1>n ,则该结点无左孩子; 否则,编号为 2i =1的结点为其左孩子结点;
- 若 2i+2>n ,则该结点无右孩子结点;否则,编号为2i+2 的结点为其右孩子结点。
满二叉树
在一棵二叉树中,如果 所有分支结点都存在左子树和
右子树 ,并且 所有叶子结点都在同一层上 ,这样的一
棵二叉树称作满二叉树
完全二叉树
对满二叉树从上到下从左到右编号,则任意一棵二叉树都可以和同深度的满二叉树对比,假如一棵包 含n 个结点的二叉树中每个结点都可以和满二叉树中 编号为1 至n 的结点一一对应,则称这类二叉树为完全二叉树。
- 完全二叉树是一种 叶子结点只能出现在最下层和次下层且最下层的叶子结点集中在树的左边 的特殊二叉树
- 完全二叉树中如果有度为1 的结点,只可能有一个,且该结点只有左孩子
- 深度为k 的完全二叉树在k- 1 层上一定是满二叉树
二叉树遍历方式
1.前序遍历
先序遍历二叉树的操作定义:若二叉树为空,则空操作;否则:
- (1) 访问根结点;
- (2) 先序 遍历左子树;
- (3) 先序遍历右子树
public void preOrder() {
System.out.println(this); //先输出父结点
//递归向左子树前序遍历
if(this.left != null) {
this.left.preOrder();
}
//递归向右子树前序遍历
if(this.right != null) {
this.right.preOrder();
}
}
2.中序遍历
中序遍历二叉树的操作定义:若二叉树为空,则空操作;否则:
- (1) 中序遍历左子树;
- (2) 访问根结点;
- (3) 中序遍历右子树。
public void infixOrder() {
//递归向左子树中序遍历
if(this.left != null) {
this.left.infixOrder();
}
//输出父结点
System.out.println(this);
//递归向右子树中序遍历
if(this.right != null) {
this.right.infixOrder();
}
}
3.后序遍历
后序遍历二叉树的操作定义:若二叉树为空,则空操作;否则:
- (1) 后序遍历左子树;
- (2) 后序遍历右子树;
- (3) 访问根结点。
//后序遍历
public void postOrder() {
if(this.left != null) {
this.left.postOrder();
}
if(this.right != null) {
this.right.postOrder();
}
System.out.println(this);
}
- 一棵二叉树序 具有唯一的先序( 中序、后序) 序列 ;
- 不同的二叉树可以具有相同的先序或中序或后序
- 已知 前序 和 中序 ,可以唯一确定二叉树
- 已知 后序 和 中序 ,可以唯一确定二叉树
- 已知 前序 和 后序 , 不能 唯一确定二叉树
删除结点
public void delNode(String no) {
if(this.left != null && this.left.no == no) {//若当前结点的左子结点不为空,并且左子结点 就是要删除结点
this.left = null;
return;
}
if(this.right != null && this.right.no == no) {//当前结点的右子结点不为空,并且右子结点 就是要删除结点
this.right = null;
return;
}
if(this.left != null) {//向左子树进行递归删除
this.left.delNode(no);
}
if(this.right != null) {//右子树进行递归删除
this.right.delNode(no);
}
}
完整代码实现
以 下图二叉树为例
public class Binarytree2 {
public static void main(String[] args) {
Binarytree binarytree=new Binarytree();//创建二叉树
Node root = new Node( "A");//创建结点
Node node2 = new Node( "B");
Node node3 = new Node( "C");
Node node4 = new Node( "D");
Node node5 = new Node( "E");
Node node6 = new Node("F");
root.setLeft( node2);//建立结点间的关系
root.setRight(node3);
node2.setLeft(node5);
node3.setLeft(node4);
node3.setRight(node6);
binarytree.setRoot(root);
System.out.println("前序遍历结果为:");
binarytree.preOrder();
System.out.println("中序遍历结果为:");
binarytree.infixOrder();
System.out.println("后序遍历结果为:");
binarytree.postOrder();
binarytree.delNode("C");
System.out.println("删除结点C后,前序遍历结果为:");
binarytree.preOrder();
}
}
class Binarytree {//定义二叉树
private Node root;
public void setRoot(Node root) {
this.root = root;
}
public void delNode(String no) {//删除结点
if(root != null) {
if(root.getNo() == no) {
root = null;
} else {
root.delNode(no);
}
}else{
System.out.println("二叉树为空!");
}
}
public void preOrder() { //前序遍历
if(this.root != null) {
this.root.preOrder();
}else {
System.out.println("二叉树为空!");
}
}
public void infixOrder() { //中序遍历
if(this.root != null) {
this.root.infixOrder();
}else {
System.out.println("二叉树为空!");
}
}
//后序遍历
public void postOrder() {
if(this.root != null) {
this.root.postOrder();
}else {
System.out.println("二叉树为空!");
}
}
}
class Node {
private String no;
private Node left;
private Node right;
public Node( String no) {
this.no = no;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node [no=" + no + "]";
}
public void delNode(String no) {
if(this.left != null && this.left.no == no) {//若当前结点的左子结点不为空,并且左子结点 就是要删除结点
this.left = null;
return;
}
if(this.right != null && this.right.no == no) {//当前结点的右子结点不为空,并且右子结点 就是要删除结点
this.right = null;
return;
}
if(this.left != null) {//向左子树进行递归删除
this.left.delNode(no);
}
if(this.right != null) {//右子树进行递归删除
this.right.delNode(no);
}
}
public void preOrder() {//前序遍历
System.out.println(this); //输出根结点
if(this.left != null) {//左子树遍历
this.left.preOrder();
}
if(this.right != null) {//右子树遍历
this.right.preOrder();
}
}
public void infixOrder() {//中序遍历
if(this.left != null) {//左子树遍历
this.left.infixOrder();
}
System.out.println(this);//输出根结点
if(this.right != null) {//右子树遍历
this.right.infixOrder();
}
}
public void postOrder() {//后序遍历
if(this.left != null) {//左子树遍历
this.left.postOrder();
}
if(this.right != null) {//右子树遍历
this.right.postOrder();
}
System.out.println(this);//输出根结点
}
}
运行结果
前序遍历结果为:
Node [no=A]
Node [no=B]
Node [no=E]
Node [no=C]
Node [no=D]
Node [no=F]
中序遍历结果为:
Node [no=E]
Node [no=B]
Node [no=A]
Node [no=D]
Node [no=C]
Node [no=F]
后序遍历结果为:
Node [no=E]
Node [no=B]
Node [no=D]
Node [no=F]
Node [no=C]
Node [no=A]
删除结点C后,前序遍历结果为:
Node [no=A]
Node [no=B]
Node [no=E]