查找
关键词
- 关键字
- 主关键字
- 次关键字
平均查找长度
定义: 需和给定值比较的关键字的个数的期望值,成为查找成功时的平均查找长度
对于有n个关键字的表,其平均查找长度如下:
其中是查找第i个关键字的概率,可知 (个人理解给定值出现在位置i上的概率)
是在i位置查找到目标值时,已经比较过的关键字的个数.
静态查找表
静态查找表的顺序存储结构
typedef struct{
Elemtype *list;
int ncount;
}SSTable;
顺序查找的实现
从后往前找,零号位置作为哨兵,这样不用没找一次比较一次,当查找量大于1000的时候,是可以提高一倍效率的.
int search(SSTable &list,Elemtype keyword){
(list.list)[0]=keyword;
int i=list.ncount;
while ((list.list)[i]==keyword) i--;
return i;
}
性能分析
如果在第一个位置需要查找n次,如果在最后一个位置需要查找1次,于是可以归纳出在第i个位置需要查找n-i+1次
一般情况下,出现在每个目标出现在每个位置的概率时相等的,即
tip:上述讨论是忽略了查找不成功的情况的,一般情况下查找不成功的概率确实小的可以忽略不计.
有序表的查找
折半查找算法(实际上就是二分查找)
int search_bin(SSTable &list,Elemtype key){
int l=1,r=list.ncount;
while (l<=r>){
int mid=(l+r)/2;
if (key==(list.list)[mid]) return mid;
else if(key>(list.list)[mid]) l=mid+1;
else r=mid-1;
}
return 0;
}
性能分析
通过判定树来分析
有n个节点的树的最大深度是
访问第h层的节点总共需要访问个节点(最坏情况)
二叉树第h层有个节点
h层的二叉树有个节点
等比数列公式:
为了方便计算,假设一个深度为h的满二叉树,此时,并且目标值出现在每个位置的概率是相同的
可以得到:
当n比较大的时候()时,可以近似等于
动态查找树表
动态查找表的特点:表结构本身是在查找过程中动态生成的,即若存在返回索引,不存在插入
二叉排序树(BST)
定义:
二叉排序树或者是一颗空树,或者是符合以下定义的树:
(1)若它的左子树不为空,则左子树上所有的节点均小于根节点上的值
(2)若它的右子树不为空,则右子树上所有的节点均大于根节点上的值
(3)它的左子树和右子树同样是二叉排序树
储存结构:(和二叉树其实是一样的)
typedef struct BiTNode{
elemtype data;
struct BiTNode *lchild,*rchild;
}BiTNODE,*BiTree;
查找算法
BiTree search(BiTree root,elemtype key,BiTree &pre,BiTree &p){
//p是要传给外部的参数
if (!root) {p=pre;return NULL;} //返回上一个
if (root->data==key) return root;
else if(key>root->data) search(root->rchild,key,root);
else search(root->lchild,key,root,p);
}
插入算法
int add(BiTree &T,elemtype key){
BiTree temp;
if (!search(T,key,T,temp)){
//生成节点
BiTree newnode=(BiTree)malloc(sizeof(BiTNode));
*newnode={key,NULL,NULL};
if (!temp) T=newnode;
else{
if (key>T->data) temp->lchild=newnode;
else temp->rchild=newnode;
}
}
else return -1;
return 0;
}
删除算法
分为三种
(1)被删除的只有叶子节点
(2)被删除的只有左子树或者右子树
(3)被删除的节点既有左子树又有右子树
(1)将双亲节点的对应的指针域改成空
(2)双亲节点指向被删除节点的对应的左子树或者右子树
(3)可以从左子树操作也可以从右子树操作,这里就以左子树为例.
- 找到左子树里面最大的关键字,替换被删除节点
- 刚刚用来替换的那个最大的关键字的右子树一定是空的,用(2)处理以下就可以了
int delete_1(BiTree& t,elemtype key){
BiTree &pre,temp;
temp=search(T,key,T,pre);
if (!temp) return -1;
else{
if (!(temp->rchild)) {pre->next=temp->left;free(temp);}
else if(!(temp->lchild)) {pre->next=temp->right;free(temp);}//这里的pre->next指所对应的左子树或者右子树
else{
BiTree temp2;
pre=temp;
temp2=temp->lchild;
while (temp2->rchild){pre=temp2; temp2=temp2->rchild;}
temp->data=temp2->data;
pre->next=temp2->right;
free(temp2);
}
}
}
tip:这里的代码可能有问题,重点看一下思路吧
conclude: 对于经常需要删除和添加的有序表,使用二叉排序树是比较合适的
查找性能分析
对于不同的插入顺序,构造出来的二叉排序树是不一样的,于是查找的复杂度也是不一样的,于是这里直接给出计算后的平均值
二叉平衡树
定义: 平衡二叉树又称AVL树.它或者是一棵空树,或者是一棵具有如下性质的树:
(1) 它的左子树和右子树都是平衡二叉树
(2) 他的左右子树深度之差的绝对值不超过1
平衡因子
左子树的深度与右子树深度的差值(注意先后顺序)
特点
平衡因子的绝对值不大于1
构造-平衡旋转技术
平衡旋转技只针对最小不平衡子树
平均查找长度在log级别上
4种平衡旋转
LL型,右旋一次,被折下去的那个节点的左子树会空出来,由定义可以知道,被折下去的部分一定是大于左边部分的,于是空出来的位置可以接上折上去的节点的右子树.
RR型,左旋一次,其余原理同LL型是一致的.
LR型
RL型
tip:怎么插入的这个节点其实和如何旋转对应上了
B-树
(待续)