原理: 注意第一个位置上有值;从后面往前面找。
哨兵问题: 避免每次查找位置是否越界。
ASL: n - i + 1求和。
int Search(int a[] , int n , int key){ // n代表数据长度//
int i = n;
a[0] = key;
while(a[i] != key){
i --;
}
return i;
}
原理: 注意判定树问题。
ASL:
成功:所有圆的就是成功的,所以计算每层圆的个数 * 层数 / 圆的个数
失败:所有方的就是失败的,方的层数 * 层的方的个数 / 方的个数
int Binary_Search(SqList L, ElemType key , int n){
int low = 0 , high = n - 1 , mid; // 从0开始
while(low <= high){
mid = (low + high) / 2; // 向下取整
if(L.elem[mid] == key){ // 正好查到
return mid;
}
else if(L.elem[mid] > key){ // 比它大,右缩小范围
high = mid - 1;
}
else{
low = mid + 1; // 比它小, 左缩小范围
}
}
return -1;
}
Q:什么是平衡二叉树?
平衡二叉树的左子树-右子树的深度不超过1。
struct node{
int data , height;
node* lchild , rchild;
}
//求出结点的高度
int get_Height(node* root){
if(root == NULL) return 0;
else root->height;
}
//计算平衡因子
int get_BalanceFactor(node* root){
return get_Height(root->lchild) - get_Height(root->rchild);
}
Q:为什么平衡二叉树的ASL与logn为同样的数量级?
根据前面树的四个公式已知(公式链接),n个结点的二叉树的深度为logn向下取整+1,在树中查找一个数据,最多不会超过树的深度吧,所以平均查找长度也就和logn为同样的一个数量级。
Q:如何构建平衡二叉树?
如果是那种RL的那种,记得从下往上依次更新即可。
//Right Rotation
void Right_Rotation(node* &root){
node * temp = root->lchild; // temp当前指向的是B,root是A
root->lchild = temp->rchild; // 让B结点的右孩子成为A结点的左孩子
temp->rchild = root; // 让A结点成为B结点的右孩子
update_Height(root); //分别更新AB结点的高度
update_Height(temp);
root = temp; //根节点变化
}
//更新高度函数,其左右孩子的高度的最大值+1
void update_Height(node* root){
root->height = max(get_Height(root->lchild) , get_Height(root->rchild) + 1);
}
文件系统搜索常用的一种数据结构。
B树的查找方式类似二叉排序树,关键字可以理解为就是一种分割点。
对于第二种查找方式,关键问题还是结点的深度。第一层有1个结点,第二层按照定义有2个结点;再往下,由于非叶子结点的子树的个数是 ⌈m/2⌉ ,所以下一层的结点的个数是 2 ∗ ( ⌈ m / 2 ⌉ ) 2*( ⌈m/2⌉) 2∗(⌈m/2⌉);再往下,结点数需要再乘 ⌈m/2⌉,得到 2 ∗ ( ⌈ m / 2 ⌉ ) 2 2*( ⌈m/2⌉)^{2} 2∗(⌈m/2⌉)2次,所以按照这个规律往下递推,第l层有 2 ∗ ( ⌈ m / 2 ⌉ ) l − 2 2*( ⌈m/2⌉)^{l-2} 2∗(⌈m/2⌉)l−2次,第l+1层为叶子结点,有 2 ∗ ( ⌈ m / 2 ⌉ ) l − 1 2*( ⌈m/2⌉)^{l-1} 2∗(⌈m/2⌉)l−1次。
当前B树有 N N N个关键字,如果查找不成功,则结点个数为 N + 1 N+1 N+1个,(我的理解是,当前的关键字的个数和结点个数的值是一致的。)这个数值肯定比当前结点数值要小嘛,所以可以得到
N + 1 ≤ 2 ∗ ⌈ m / 2 ⌉ l − 1 N+1\leq{2*\lceil m/2 \rceil}^{l-1} N+1≤2∗⌈m/2⌉l−1
推导可以得到一个关于 l l l的公式,即
l ≤ l o g ⌈ m / 2 ⌉ ( N + 1 2 ) + 1 l\leq log_{\lceil m/2 \rceil}(\frac{N+1}{2})+1 l≤log⌈m/2⌉(2N+1)+1
Q1:何时产生分裂?为什么说该结点关键字个数不超过 m − 1 m-1 m−1时不需要分裂?
根据树的定义,树的每个结点不能有超过m棵子树,m棵子树对应的根会有 m − 1 m-1 m−1个关键字。(可以想想5个手指算上左右的空隙会有6个空隙。)
Q2:怎么插?
删除的本质其实在于:如何在维持B树的定义的情况下删除一个关键字。
如果删除的是非终端叶子结点,将该结点子树的最小的叶子结点进行交换即可。所以书上写着有三种情况。
根据树的定义:除根以外的所有的结点都是 ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉当差了一个1,正好可以维持。所以当缺少了一个数值的时候,可以从左右借。
B+树的数据都存在在叶子结点上,然后用一个链表把所有的叶子结点都串了起来。当数据存储的离根节点很近的时候,B树效果比B+树效果要好,B+数据都存在叶子结点上。
查找分为两种,一种是上面介绍的通过比较的查找,而另一种则是根据计算直接得出。关键字 k e y key key 和其对应的hash函数的 h a s h ( k e y ) hash(key) hash(key)合成一个表,成为hash表或者散列表。
hash表非常容易算错,一定要仔细仔细再仔细,算的是余数不是商。
从上面可以看到hash函数的问题主要是出现在hash函数的设计,处理冲突的方法,以及当前hash函数已经存储的状态。所以同样hash查找也可以利用ASL进行分析。