二叉排序树
若左子树不空,则左子树上所有结点的值均<根结点的值
若右子树不空,则右子树上所有结点
的值均>根结点的值它的左、右子树也都是二叉排序树。
二叉排序树的查找过程
若树为空,则结束
若树非空,则进行如下操作
- 若给定值k=根关键字,则查找成功
- 若给定值<根关键字,则继续在左子树进行
- 若给定值>根关键字,则继续在右子树进行
二叉排序树的删除过程
- 若删除的是叶子结点,则直接删除该结点即可。若同时也是根结点,则删除后二叉排序树为空树
- 若被删除结点只有左子树而无右子树 ,可直接将其左子树的根结点替代被删除结点的位置
- 若删除结点只有右子树而无左子树,可直接将其右子树的根结点替代被删除结点的位置
- 若删除结点有 左、右子树,可将被删除结点在中序遍历下的前驱结点(或者后继结点)去代替被删除的结点
BSTree
package Search;
/**
* @description 二叉排序树结构
* @date 2016年1月7日
*/
public class BSTree {
private BiTreeNode root;
public BSTree(){
root = null;
}
public BiTreeNode getRoot() {
return root;
}
/**
* @description 中序遍历二叉树
* @param p
* @author liuquan
* @date 2016年1月7日
*/
public void inOrderTraverse(BiTreeNode p){
if(p != null){
inOrderTraverse(p.getLchild());
System.out.print(p.getData() + " ");
inOrderTraverse(p.getRchild());
}
}
/**
* @description 在二叉排序树中查找关键字为key的结点,若查找成功,则返回该结点
* @param p
* @param key
* @return
* @author liuquan
* @date 2016年1月7日
*/
public Object searchBST(BiTreeNode p, int key){
if(p != null){
if(key == (int)p.getData()){
return p;
}
if(key < (int)p.getData()){
return searchBST(p.getLchild(), key);
}
else{
return searchBST(p.getRchild(), key);
}
}
return null;
}
/**
* @description 插入操作,若插入成功,则返回true。。
* 先比较key值,若已存在,则不用插入;否则,将新结点插入到表中。新插入的结点一定是作为叶子结点添加到表中
* @return
* @author liuquan
* @date 2016年1月7日
*/
public boolean insertBST(BiTreeNode p, int key){
if(p == null){
root = new BiTreeNode(key);
return true;
}
//不插入关键字重复的结点
if(key == (int)p.getData()){
return false;
}
if(key < (int)p.getData()){ //往左子树插入
if(p.getLchild() == null){
p.setLchild(new BiTreeNode(key));
return true;
}
else{
return insertBST(p.getLchild(), key);
}
}
else if(p.getRchild() == null){
p.setRchild(new BiTreeNode(key));
return true;
}
else{
return insertBST(p.getRchild(), key);
}
}
/**
* @description 删除操作。若删除成功,则返回被删除的结点值
* 1.若删除的是叶子结点,则直接删除该结点即可。若同时也是根结点,则删除后二叉排序树为空树
* 2.若被删除结点只有左子树而无右子树 ,可直接将其左子树的根结点替代被删除结点的位置
* 3.若删除结点只有右子树而无左子树,可直接将其右子树的根结点替代被删除结点的位置
* 4.若删除结点有左、右子树,可将被删除结点在中序遍历下的前驱结点(或者后继结点)去代替被删除的结点
* @param p 以p为根的二叉排序树
* @param key
* @param parent 是p的父结点,根结点的父结点是null
* @return
* @author liuquan
* @date 2016年1月7日
*/
public int removeBST(BiTreeNode p, int key,BiTreeNode parent){
if(p != null){
if(key < (int)p.getData()){ //在左子树中删除
return removeBST(p.getLchild(), key, p);
}
else if(key > (int)p.getData()){ //在右子树中删除
return removeBST(p.getRchild(), key, p);
}
else if(p.getLchild()!=null && p.getRchild() != null){ //相等且有左右子树
// 寻找p在中序遍历下的后继结点next
BiTreeNode next = p.getRchild();
//寻找右子树中的最左孩子就是p的中序后继结点
while(next.getLchild() != null){
next = next.getLchild();
}
p.setData(next.getData());
return removeBST(p.getRchild(), (int)p.getData(), p);
}
else{ //p是1度或者叶子结点
if(parent == null){ //p是根
if(p.getLchild() != null){
root = p.getLchild();
}
else{
root = p.getRchild();
}
return (int) p.getData();
}
if(p == parent.getLchild()){ // p是左孩子
if(p.getLchild() != null){
parent.setLchild(p.getLchild());
}
else{
parent.setLchild(p.getRchild());
}
}
else if(p == parent.getRchild()){ //p是右孩子
if(p.getLchild() != null){
parent.setRchild(p.getLchild());
}
else{
parent.setRchild(p.getRchild());
}
}
return (int) p.getData();
}
}
return -1;
}
}
/**
* @description 二叉排序树结点结构
* @date 2016年1月7日
*/
class BiTreeNode {
private Object data;
private BiTreeNode lchild, rchild;
public BiTreeNode() {
this(null);
}
public BiTreeNode(Object data) {
this(data, null, null);
}
public BiTreeNode(Object data, BiTreeNode lchild, BiTreeNode rchild) {
this.data = data;
this.lchild = lchild;
this.rchild = rchild;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public BiTreeNode getLchild() {
return lchild;
}
public void setLchild(BiTreeNode lchild) {
this.lchild = lchild;
}
public BiTreeNode getRchild() {
return rchild;
}
public void setRchild(BiTreeNode rchild) {
this.rchild = rchild;
}
}
Test
package Search;
import Sort.Sort;
public class Test {
public static void main(String[] args) {
int[] a = new int[]{52, 39, 67, 95, 70, 8, 25, 52};
Sort.display(a);
System.out.println("顺序查找" );
System.out.println("95的位置是:" + Search.seqSearch(a, 95));
System.out.println("30的位置是:" + Search.seqSearch(a, 30));
System.out.println();
int[] b = Sort.qSort(a);
Sort.display(b);
System.out.println("二分查找");
System.out.println("95的位置是:" + Search.binarySearch(b, 95));
System.out.println("30的位置是:" + Search.seqSearchWithGuard(b, 30));
System.out.println();
System.out.println("二叉排序树 ");
// 构建二叉排序树
BSTree bs = new BSTree();
for(int i = 0; i < a.length; i++){
bs.insertBST(bs.getRoot(), a[i]);
}
System.out.println("二叉树中根遍历:");
bs.inOrderTraverse(bs.getRoot());
System.out.println();
System.out.println("95的位置是:" + bs.searchBST(bs.getRoot(), 95));
System.out.println("30的位置是:" + bs.searchBST(bs.getRoot(), 30));
System.out.println("删除结点50后的二叉树中根遍历:");
bs.removeBST(bs.getRoot(), 52, null);
bs.inOrderTraverse(bs.getRoot());
}
}
平衡二叉树
平衡二叉树又称为AVL树,它是一颗空树,或者其左子树和右子树都是平衡二叉树且左子树和右子树的深度之差(平衡因子,或平衡度)不会超过1。
失去平衡的原因可归纳为4类:
LL型平衡旋转(单向右旋):
RR型平衡旋转(单向左旋):
LR型平衡旋转(先左旋后右旋):
RL型平衡旋转(先右旋后左旋):
平衡二叉树插入一个记录
若AVL树为空,则插入x作为根结点,树的深度增1
若x与AVL树根结点相等,则不插入
若x<根,则x插入到左子树中,且插入后左子树深度增1就要分列不同情况:
若AVL根平衡度为-1,则将根结点的平衡因子调整为0,且树深度不变
若AVL根平衡度为0,则将根平衡度调为1,树深度增1
若AVL根平衡度为1,则若左子树根结点平衡度为1时需要LL型旋转,为-1时需要LR型旋转
若x>根,参考步骤3的算法
平衡二叉树的优缺点
优点是使树结构更好,提高了查找操作的速度;缺点是插入和删除操作复杂化,降低了速度。