开篇介绍
大家好,我是Java最全面试题库
的提裤姐,今天这篇是数据结构与算法的第八篇,主要介绍查找中的树的查找;在后续,会沿着第一篇开篇的知识线路一直总结下去,做到日更!如果我能做到百日百更,希望你也可以跟着百日百刷,一百天养成一个好习惯。
树的查找
当用线性表作为表的组织形式时,可以有三种查找法,其中二分查找
效率最高。但由于二分查找要求表中结点按关键字有序,且不能用链表作存储结构,因此,当表的插入或删除操作频繁时,为维护表的有序性,势必要移动表中很多结点,这种由移动结点引起的额外时间开销,就会抵消二分查找的优点。即二分查找只适用于静态查找表。若要对动态查找表进行高效率的查找,则可以采用几种特殊的二叉树或树作为表的组织形式。
二叉排序树的查找
1.二叉排序树
二叉排序树( Binary Sort Tree称二叉查找(搜索) Binary Search Tree,
定义:二叉排序树或者是空树,或者是满足如下性质的二叉树:
①若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
②若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
③左、右子树本身又各是一棵二叉排序树。
上述性质简称二叉排序树性质(BST性质),故二叉排序树实际上是满足BST性质的二叉树。BST性质告诉我们,二叉排序树中任一结点x,其左(右)子树中任一结点y(若存在)的关键字必小(大)于x的关键字。如此定义的二叉排序树中各结点关键字是惟一的。但实际应用中,我们不能保证被查找的数据集中各元素的关键字互不相同,所以可将二叉排序树定义中BST性质(1)里的“小于”改为“大于等于”,或将BST质(2)里的“大于”改为“小于等于”,甚至可同时修改这两个性质。
从BST性质可推出二叉排序树的另一个重要性质:按中序遍历该树所得到的中序序列是一个递增有序序列
代码实现:
public class Search {
public class BiTreeNode {
int m_nValue;
BiTreeNode m_pLeft;
BiTreeNode m_pRight;
}
//二叉排序树,二叉查找树,二查搜索树,是一颗具有如下特点的树,树的左边都比它小,树的右边都比它大。
public BiTreeNode BinaryBiSearch(BiTreeNode pHead,int b){
if(pHead==null)
return null;
if(pHead.m_nValue==b)
return pHead;
if(pHead.m_pLeft!=null)
return BinaryBiSearch(pHead.m_pLeft,b);
if(pHead.m_pRight!=null)
return BinaryBiSearch(pHead.m_pRight,b);
return null;
}
}
B树:
在B-树中查找给定关键字的方法是,首先把根结点取来,在根结点所包含的关键字K1,…,Kn查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查找的关键字在Ki与Ki+1之间,Pi为指向子树根节点的指针,此时取指针Pi所指的结点继续查找,直至找到,或指针Pi为空时查找失败。
以上图为例:若查询的数值为5:
第一次磁盘IO:在内存中定位(与17、35比较),比17小,左子树;
第二次磁盘IO:在内存中定位(与8、12比较),比8小,左子树;
第三次磁盘IO:在内存中定位(与3、5比较),找到5,终止。
由于B树相对于二叉树来说矮胖了许多,所以它所涉及的IO操作也相对少了许多。不过根据我们上面的分析,其在查找数据的时候并没有减少比较次数。但是我们知道,我们在比较数据的时候是在内存中进行的,所以相对来说时间上会更加迅速,几乎可以忽略。
插入流程:
在下面的B树中插入key:
第一步:检索key插入的节点位置如上图所示,在3,5之间
第二步:判断节点中的关键码个数节点3,5已经是两元素节点,无法再增加(已经 = 3-1)。父亲节点 2, 6 也是两元素节点,也无法再增加。根节点9是单元素节点,可以升级为两元素节点。
第三步:结点分裂拆分节点3,5与节点2,6,让根节点9升级为两元素节点4,9。节点6独立为根节点的第二个孩子。
删除流程:
第一步:判断该元素是否在叶子结点上。该元素在叶子节点上,可以直接删去,但是删除之后,中间节点12只有一个孩子,不符合B树的定义:每个中间节点都包含k个孩子,(其中 ceil(m/2) <= k <= m)所以需要调整;
第二步:判断其左右兄弟结点中有“多余”的关键字,即:原关键字个数n>=ceil(m/2) - 1;显然结点11的右兄弟节点中有多余的关键字。那么可将右兄弟结点中最小关键字上移至双亲结点。而将双亲结点中小于该上移关键字的关键字下移至被删关键字所在结点中即可