目录
想法来源于学院的算法与数据结构实践题。
内容
- 构造规则:维持二叉平衡搜索树的一个诱人方法是维持两个二叉搜索子树 。操作的关键在于构建两棵树,若要添加新的key值,将其插入可以让该结点所在层次较小的树,如果待插入结点在两棵树中的高度相同,就将其插入第一棵树。
- 针对上述规则修改的二叉搜索树规则:
该二叉搜索树是一颗空二叉树,或者是一颗满足一下要求的树:
(1)二叉树中所有结点的关键字的值可以相同;
(2)根结点如果存在左子树,那么左子树中所有结点关键字值都小于等于根结点关键字的值;
(3) 根结点如果存在右子树,那么右子树中所有结点关键字值都大于根结点关键字的值;
(4)根结点的左右子树都应是一颗满足上述要求的二叉搜索树。
相关算法实现
-
构建的数据结构(Java类形式给出)
class BSTNode{ //内部类,结点类
char key; //关键字
public int height; //当前阶段所在层次
BSTNode left; //左孩
BSTNode right; //右孩
}
public class BSTree {
private BSTNode firstRoot ; //第一棵树的根节点
private BSTNode secondRoot; //第二棵树的根节点
}
-
Insert函数
-
private void InsertFirst(char ch) { if(this.firstRoot == null) { //如果树根为空,就直接插到根结点 this.firstRoot = new BSTNode(ch,null,null); return ; //插完,返回 } BSTNode currentNode = this.firstRoot ; BSTNode parentNode = null ; int tmpHeight = 0; //记录parentNode的层数 boolean isLeft = true; //标志量 while(currentNode != null) { //当前结点不为空,就一直循环 parentNode = currentNode; //更新父节点值,当前结点准备下降 if(ch > currentNode.key ) { //根据搜索树定义来判断待插结点位置 tmpHeight ++ ; //更新父节点层次 currentNode = currentNode.right ; //向当前结点的右子树下降 isLeft = false; //由于向右下降,标志量为false } else { tmpHeight ++; currentNode = currentNode.left ; //否则向左子树下降 isLeft = true; } } BSTNode newNode = new BSTNode(ch,null,null); //构造关键字为key的结点 if(isLeft) { //根据标志量插入到正确位置 parentNode.left = newNode; //插入左树 newNode.height = tmpHeight + 1; //更新新建结点的层次 } else { parentNode.right = newNode; //插入右树 newNode.height = tmpHeight + 1; } }
其实Insert函数的原型可以写为void Insert(char ch, BSTNode root); 但是在运行过程中我发现给参数表里的root传入firstRoot或是secondRoot,都不能真正实现插入。根据分析应该是传入变量时,root并未真正指向树根。所以简单地我就写成两个函数,分别处理两个子树的插入操作。secondRoot对应的插入操作类似上面。
-
buildTree():
根据构造规则,将结点插入正确的子树
public void buildBSTree(String str1 ) {
String str = str1;
int index = 0; //字符串的下标,0~length
while(index < str.length()) {
int choice = this.getWhichInsert(str.charAt(index)); //获取返回结果
if ( choice == 1) { //如果返回结果是1就插入左树
this.InsertFirst(str.charAt(index));
index++;
}
else if (choice == 2) { //如果是2就插入右树
this.InsertSecond(str.charAt(index));
index++;
}
}
}
-
getWhichInsert():
判断结点插入哪棵子树会获得更小的深度
-
private int getWhichInsert(char ch) { int firstHeight = 0; BSTNode currentNode = this.firstRoot; //当前节点初始化为树根 while (currentNode != null) { //当当前节点不为空是循环执行 firstHeight ++; if ( ch > currentNode.key) { //如果待插入节点的值大于当前节点值 currentNode = currentNode.right; //则要插入右子树,当前节点更改为右子树 } else { //小于等于插入左子树 currentNode = currentNode.left; //插入当前节点的左子树 } } int secondHeight = 0; currentNode = this.secondRoot ; while (currentNode != null) { //当当前节点不为空是循环执行 secondHeight ++; if ( ch > currentNode.key) { //如果待插入节点的值大于当前节点值 currentNode = currentNode.right; //则要插入右子树,当前节点更改为右子树 } else { //小于等于插入左子树 currentNode = currentNode.left; //插入当前节点的左子树 } } if(firstHeight <=secondHeight) //如果插入左树结点深度更小 return 1; //返回1 else return 2; //否则返回2 }
可以看出,实现原理就是插入中的定位带插入结点位置的那部分,没什么难度。
总结
其实,使用两棵子树来维持搜索树的平衡的做法实现起来并不难。而且这也提供了另一种实现搜索树平衡的思路。