基于树的查找方法是将待查表组织成特定的树结构,并在树结构的基础上实现查找的方法。
1.二叉查找树
1.1 查找算法
基本查找方法是从根结点开始,递归的缩小查找范围,直到发现目标元素为止(查找成功)或查找范围缩小为空树(查找失败)。
基本思想是:
若查找树不为空,将待查关键字与根节点元素进行比较——> 若相等直接返回根结点——> 否则判断待查关键字与根结点的大小——> 如果待查关键字小,则递归在左子树查找——>否则在右子树查找
/**
*
* 二叉查找树 —— 递归形式
*/
public Node search(Object ele){
return binTSearchRe(root,ele);
}
private Node binTSearchRe(BinTreeNode rt, Object ele) {
if(rt == null){
return null;
}
switch (strategy.compare(ele,rt.getData())) {
case 0:
return rt;
case -1:
return binTSearchRe(rt.getLChild(), ele);//小于
case 1:
return binTSearchRe(rt.getRChild(), ele);
}
return null;
}
/**
* 二叉查找树——非递归形式
*/
private Node binTSearch(BinTreeNode rt, Object ele) {
while(rt != null){
switch (strategy.compare(ele,rt.getData())) {
case 0:
return rt;
case -1:
rt = rt.getLChild();//小于
break;
case 1:
rt = rt.getRChild();
break;
}
}
return null;
}
1.2 最大最小值
在二叉查找树中,最小元素总是能够通过根结点向左不断深入,直到到达最左的一个叶子结点找到;而最大元素总是能够通过根结点向右不断深入,直到到达最右的一个叶子结点找到。
public Node min(BinTreeNode v){
if(v != null){
while(v.hasLChild()){
v = v.getLChild();
}
}
return v;
}
public Node max(BinTreeNode v) {
if(v != null){
while(v.hasRChild()){
v = v.getRChild();
}
}
return v;
}
1.3 前驱和后继
基本思想:
- 如果结点v有右子树,那么v的后续结点是v的右子树中关键字最小的
- 如果V右子树为空,并且b的后续结点存在,那么v的后续结点是从v到根的路径上第一个作为左孩子结点的父结点。
/**
* 获得后续结点
* @param v 根结点
* @return v在中序遍历序列中的后续结点
*/
private BinTreeNode getSuccessor(BinTreeNode v) {
if(v == null){
return null;
}
if(v.hasRChild()){
return (BinTreeNode) min(v.getRChild());
}
while(v.isRChild()){//如果最小的值是右结点,后继应该是其父结点--中序遍历顺序是 左父右
v = v.getParent();
}
return v.getParent();
}
/**
* 获得前驱结点
* @param v 根结点
* @return v在中序遍历序列中的后续结点
*/
private BinTreeNode getPredecessor(BinTreeNode v) {
if(v == null){
return null;
}
if(v.hasLChild()){
return (BinTreeNode) max(v.getRChild());
}
while(v.isLChild()){//如果最小的值是右结点,后继应该是其父结点--中序遍历顺序是 左父右
v = v.getParent();
}
return v.getParent();
}
1.4 插入算法
/**
*
* @param ele 待插元素
*/
public void insert(Object ele){
BinTreeNode p = null;
BinTreeNode current = root;
while(current != null){
p = current;
if(strategy.compare(ele, current.getData()) < 0){
current = current.getLChild();
}else{
current = current.getRChild();
}
}
// startBN = p;//待平衡出发点
if(p == null){
root = new BinTreeNode(ele);//树为空
}else if(strategy.compare(ele, p.getData()) < 0){
p.setLChild(new BinTreeNode(ele));
}else{
p.setRChild(new BinTreeNode(ele));
}
}
1.5 删除算法
- 如果是叶子结点,直接删除
- 如果只有一个子树时,v直接替换为这一个子树值
- 有两棵树,要保证中序遍历的有序性,先用v的前驱或后继替换v,然后删除其前驱或后继结点即可,然后按照之前规则继续
/**
*
* @param ele 待删除元素
* @return
*/
public Object remove(Object ele) {
BinTreeNode v = (BinTreeNode) binTSearch(root, ele);
if(v == null){
return null;
}
BinTreeNode del = null;//待删结点
BinTreeNode subT = null;//待删结点的子树
if(!v.hasLChild() || !v.hasRChild()){//确定待删结点
del = v;
}else{
del = getPredecessor(v);
Object old = v.getData();
v.setData(del.getData());
del.setData(old);
}
// startBN = del.getParent();//待平衡出发点
//此时待删结点只有左子树或右子树
if(del.hasLChild()){
subT = del.getLChild();
}else{
subT = del.getRChild();
}
//若待删结点为根
if(del == root){
if(subT != null){
subT.sever();
}
root = subT;
}else if(subT != null){
//del为非叶子结点
if(del.isLChild()){
del.getParent().setLChild(subT);
}else{
del.getParent().setRChild(subT);
}
}else{
//del为叶子结点
del.sever();
}
return del.getData();
}
2.AVL树
2.1 旋转操作
2.2 失去平衡后的重新平衡
2.3 旋转操作的统一实现方法
/**
* rotate
* @param z 失衡的结点
* @return 平衡后子树的根节点
*/
private BinTreeNode rotate(BinTreeNode z) {
BinTreeNode y = higherSubT(z);//取y为z更高的孩子
BinTreeNode x = higherSubT(y);//取x为y更高的孩子
boolean isLeft = z.isLChild();//记录:z是否左孩子
BinTreeNode p = z.getParent();//p为z的父亲
BinTreeNode a,b,c;//自左向右,三个结点
BinTreeNode t0,t1,t2,t3;//自左向右,四棵子树
//以下分四种情况重命名
if(y.isLChild()){//若y是左孩子
c = z;
t3 = z.getRChild();
if(x.isLChild()){//若x是左孩子(左左失衡)
b = y;
t2 = y.getRChild();
a = x;
t1 = x.getRChild();
t0 = x.getLChild();
}else{//若x是左孩子(左右失衡)
a = y;
t0 = y.getLChild();
b = x;
t1 = x.getLChild();
t2 = x.getRChild();
}
}else{//若y是右孩子
a = z;
t0 = z.getLChild();
if(x.isRChild()){//若x是右孩子(右右失衡)
b = y;
t1 = y.getLChild();
c = x;
t2 = x.getLChild();
t3 = x.getRChild();
}else{//若x是右孩子(右左失衡)
c = y;
t3 = y.getRChild();
b = x;
t1 = x.getLChild();
t2 = x.getRChild();
}
}
//摘下三个结点
z.sever();
y.sever();
x.sever();
//摘下四棵子树
if(t0 != null){
t0.sever();
}
if(t1 != null){
t1.sever();
}
if(t2 != null){
t2.sever();
}
if(t3 != null){
t3.sever();
}
//重新链接
a.setLChild(t0);
a.setRChild(t1);
c.setLChild(t2);
c.setRChild(t3);
b.setLChild(a);
b.setRChild(c);
//子树重新接入原树
if(p != null){
if(isLeft){
p.setLChild(b);
}else{
p.setRChild(b);
}
}
return b;//返回新的子树根
}
//返回结点v较高的子树
private BinTreeNode higherSubT(BinTreeNode v) {
if(v == null){
return null;
}
int lH = (v.hasLChild()) ? v.getLChild().getHeight() : -1;
int rH = (v.hasRChild()) ? v.getRChild().getHeight() : -1;
if(lH > rH){
return v.getLChild();
}
if(lH < rH){
return v.getRChild();
}
if(v.isLChild()){
return v.getLChild();
}else{
return v.getRChild();
}
}
2.4 AVL树的Java实现
/**
*
* @param ele 待插元素
*/
public void insertAVL(Object ele){
insert(ele);//调用插入方法
root = reBalance(startBN);
}
//从v开始重新平衡AVL树
private BinTreeNode reBalance(BinTreeNode v) {
if(v == null){
return root;
}
BinTreeNode c = v;
while(v != null){//从v开始,向上逐一检查z的祖先
if(!isBalance(v)){//若V失衡,则旋转使之重新平衡
v = rotate(v);
}
c = v;
v = v.getParent();//继续检查其父亲
}
return c;
}
//判断一个结点是否失衡
private boolean isBalance(BinTreeNode v) {
if(v == null){
return true;
}
int lH = (v.hasLChild())?v.getLChild().getHeight():-1;
int rH = (v.hasRChild())?v.getRChild().getHeight():-1;
return (Math.abs(lH-rH) <= 1);
}
/**
*
* @param ele 待删元素
* @return
*/
public Object removeAVL(Object ele) {
Object obj = remove(ele);
root = reBalance(startBN);
return obj;
}
3.B-树
3.1 查找
3.2 关键字的插入操作
3.3 关键字的删除操作