定义
二叉树是每个结点最多有两个子树的树结构。它有五种基本形态:二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空
二叉树的性质
性质1:二叉树第k层上的结点数目最多为2^(k-1)(k>=1)
性质2:深度为k的二叉树至多有2^k-1个结点(k>=1)
性质3:包含n个结点的二叉树的高度至少为(log2n)+1
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1
性质4的证明如下:
设二叉树的总结点数为N,叶子节点个数为a,度为2的节点个数为b,度为1的节点个数为c,则有公式(1)N=a+b+c.
现在不看节点,而看节点之间的连线,先从下网上看,则N个节点除了根节点,其他节点上面都连着一根线共有N-1根线,
从上往下看,除了叶子节点,其他节点都有一个或者两个连线,度为2的两个连线,度为1的1个,由此可得公式(2)N-1=2*b+c
根据(1)(2)可得 a=b+1;即叶子节点个数等于度为2节点个数+1
满二叉树
定义:高度为h,并且由2^h-1个结点组成的二叉树,称为满二叉树
完全二叉树
定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下层的叶结点集中在靠左的若干位置上,这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
二叉查找树
定义:二叉查找树又被称为二叉搜索树。设x为二叉查找树中的一个结点,x结点包含关键字key,结点x的key值计为key[x]。如果y是x的左子树中的一个结点,则key[y]<=key[x];如果y是x的右子树的一个结点,则key[y]>=key[x]
在二叉查找树种:
(1)若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
(2)任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
(3)任意结点的左、右子树也分别为二叉查找树。
(4)没有键值相等的结点。
二叉查找树的创建
public class BinaryTree {
private class Node{
int value;
Node lchild;
Node rchild;
public Node(int value,Node lchild,Node rchild){
this.value=value;
this.lchild=lchild;
this.rchild=rchild;
}
}
Node root=null;
private Node insert(Node cur,int value){
if(cur==null)
return new Node(value,null,null);
if(value==cur.value)
return null;
else if(value<cur.value){
cur.lchild= insert(cur.lchild,value);
}
else{
cur.rchild=insert(cur.rchild,value);
}
return cur;
}
/**
* 递归创建二叉树
* @param value
*/
public void insert(int value){
root=insert(root,value);
}
/**
* 非递归创建二叉树
* @param value
*/
public void insertC(int value){
if(root==null)
root=new Node(value,null,null);
else{
Node cur=root;
Node parent=cur;
while(cur!=null){
parent=cur;
if(value==cur.value)return;
else if(value<cur.value)
cur=cur.lchild;
else
cur=cur.rchild;
}
Node node=new Node(value,null,null);
if(value<parent.value)
parent.lchild=node;
else
parent.rchild=node;
}
}
}
这里使用了两种方式,递归和非递归算法。
二叉查找树节点的删除
删除分为三种情况讨论
(1)删除节点没有左子树,这种情况直接将删除节点的父节点指向删除节点的右子树。
(2)删除节点没有右子树,这种情况直接将删除节点的父节点指向删除节点的左子树。
(3)删除节点左右子树都存在,可以采用两种方式,
1:让删除节点左子树的最右侧节点代替当前节点
2:让删除节点右子树的最左侧节点代替当前节点
如下图:
代码实现:
/**
* 删除非递归实现
* @param value
* @return
*/
public int delete(int value){
if(root==null)
throw new RuntimeException("这是一个空树!");
if(value==root.value){
root=null;
return 1;
}
Node cur=root;
Node parent=null;
while(cur!=null){
if(value==cur.value){
//没有左子树
if(cur.lchild==null){
//删除的节点是父节点的左节点
if(value<parent.value)
parent.lchild=cur.rchild;
//删除的节点是父节点的右节点
else
parent.rchild=cur.rchild;
}
//没有右子树
else if(cur.rchild==null){
//删除的节点是父节点的左节点
if(value<parent.value)
parent.lchild=cur.lchild;
//删除的节点是父节点的右节点
else
parent.rchild=cur.lchild;
}
//左右子树都存在,可以采用两种方法,1:让左子树的最右侧节点代替当前节点2:让右子树的最左侧节点代替当前节点
else{
//这里我们采用第1种
Node t=cur.lchild;
Node p=cur;//父节点
while(t.rchild!=null){
p=t;
t=t.rchild;
}
cur.value=t.value;//让左子树的最右侧节点代替当前节点
p.rchild=null;
}
return 1;
}else{
parent=cur;
if(value<cur.value){
cur=cur.lchild;
}
else{
cur=cur.rchild;
}
}
}
return-1;
}
折半查找算法
/**
* 折半查找
* @param value
* @return
*/
public int BinarySearch(int value){
if(root==null)
throw new RuntimeException("这是一个空树!");
Node cur=root;
while(cur!=null){
if(value==cur.value)
return 1;
else if(value<cur.value)
cur=cur.lchild;
else
cur=cur.rchild;
}
return-1;
}
二叉树的遍历
//前序遍历
private void preOrder(Node root){
if(root==null)
return;
System.out.print(root.value+",");
preOrder(root.lchild);
preOrder(root.rchild);
}
public void preOrder(){
preOrder(root);
}
//中序遍历
private void inOrder(Node root){
if(root==null)
return;
inOrder(root.lchild);
System.out.print(root.value+",");
inOrder(root.rchild);
}
public void inOrder(){
inOrder(root);
}
测试代码如下:
public static void main(String[] args){
int[] a={5,3,4,1,2,6,8,};
BinaryTree tree=new BinaryTree();
for(int i=0;i<a.length;i++){
//tree.insert(a[i]);//递归
tree.insertC(a[i]);
}
System.out.println("前序遍历:");
tree.preOrder();
System.out.println();
System.out.println("中序遍历:");
tree.inOrder();
System.out.println();
System.out.println("该数组中是否有3?"+(tree.BinarySearch(3)==1?"yes":"no"));
tree.delete(3);
System.out.println("该数组中是否有3?"+(tree.BinarySearch(3)==1?"yes":"no"));
System.out.println("前序遍历:");
tree.preOrder();
System.out.println();
System.out.println("中序遍历:");
tree.inOrder();
}
输出的结果为:
前序遍历:
5,3,1,2,4,6,8,
中序遍历:
1,2,3,4,5,6,8,
该数组中是否有3?yes
该数组中是否有3?no
前序遍历:
5,2,1,4,6,8,
中序遍历:
1,2,4,5,6,8,