文章目录
概念
- 查找表:同一类型的数据元素构成的集合
- 操作:查询/检索/插入/删除
- 查找速度——平均查找长度ASL
静态查找表
顺序查找表
- 组织方式:线性表
- 查找方法:从表的一端顺序找到另一端
- 存储结构/元素顺序均无要求
算法实现
define max 100;
typedef struct{
keytype key;
}elemtype;
typedef struct{
elemtype elem[max];
int length;
}SqList;
SqList L;
int search(SqList L,keytype x)
{
int i;
for(i=0;i<L.length;i++)
if(L.elem[i].key==x)return i+1;
return 0;
//加入哨兵项
'''
int k=L.length;
L.elem[0].key=x; //低端/高端均可
while(x!=L.elem[k].key)
k--;
return k;'''
}
ASL分析(每个元素查找成功的概率相同)
- 查找成功的ASL=(n+1)/2
- 查找一次平均检索长度的ASL=3(n+1)/4
- 查找一次成功和失败的概率相同
有序查找表(二分查找表)
-
查找表组织有序(递增或递减),顺序存储
-
二分查找可以用二分查找判定树表示
算法分析
- 关键字排序时间复杂度为: O(nlogn)
- 二分查找只适用顺序存储结构(不便于插入和删除),适用于一经建立就很少改动而又经常需要查找的线性表
- 查找少而改动多的情况,适合采用链表进行顺序查找;链表无法实现二分查找
算法实现
int binaryS(SqList L, KeyType x) { int low=0, high=L.length-1, m; while(low<=high) { m=(low+high)/2; if(L.elem[m].key==x) return m+1; if(L.elem[m].key>x) high=m-1; else low=m+1; } return 0; }
ASL分析
- 查找成功时ASL近似等于 log(n+1) - 1
- 查找失败时所需比较的关键字个数不超过(<=)判定树的深度,最坏的情况下比较次数为 log2(n) + 1
索引顺序表(不是很懂)
策略(还需要理解)?????
- 对索引表采用二分查找或顺序查找,确定待查记录所在的块
- 在线性表查找k,诺其在线性表存在,且位于第 i 块,那么一定有:第 i-1块的最大键值 < k <= 第i块的最大键值
- 然后再相应的块中进行顺序查找
动态查找表
二叉排序树和平衡二叉树
二叉排序树
定义
- 左子树所有结点均小于根节点
- 右子树所有结点均大于根节点
- 左右子树都是二叉排序树
要点
- 二叉排序树中序遍历结果递增有序(可用于判断一颗二叉树是否为二叉排序树)
- 可以将无序序列变有序
- 同一组数据,输入顺序不同,所建立的二叉排序树不同
查找 x 过程
- 二叉树为空,失败
- 否则,x 与根节点比较:相等,查找成功结束;否则,
- x 小于根节点,在左子树上继续执行 1
- x 大于根节点,在右子树上继续执行 1
查找算法实现
//二叉链表作为二叉排序树的存储结构
typedef struct NODE
{
int key;
struct NODE *lc,*rc;
}BiNode,*BiTree;
//二叉排序树的检索算法
BiTree Search(BiNode *t,int x)
{
BiTree p=t;
while(p!=p->key)
{
if(x<p->key) p=p->lc;
else p=p->rc;
}
return p;
}
ASL分析(重要)
插入算法实现(需要理解)
新插入的结点一定是作为叶子结点添加上
int Insert(Bitree &t,int x)//二叉排序树的插入算法
{ BiTree q, p, s;
q=NULL; p=t; ;//p为正在查看的节点,初始从根节点开始;q为p的双亲节点,根节点无双亲节点
while(p!=NULL)
{ if (x==p->key) return 0;//在当前的二叉排序树中找到x,直接返回,不再做插入
q=p;
if (x<p->key) p=p->lc;
else p=p->rc;
}
s=(BiTree)malloc(sizeof(Binode)) ;//没找到x,做插入:先申请节点空间
s->key=x;s->lc=NULL; s->rc=NULL ;//存放x,设为叶节点
if (q==NULL)t=s; ;//若原先的二叉树是一棵空树,新插入的x对应的节点s为插入 后二叉树的根节点
else if (x<q->key) q->lc=s; ;//插入s为q的孩子
else q->rc=s; return 1;
}
删除算法实现
实现策略:p 为待删除结点
- p 为叶节点,只管删除即可
- p 只有左子树或只有右子树,将
pl
或pr
替换被删结点p - p 左右子树均有,按照中序遍历保持有序
- 用 pl 上的最大值 Smax 替换 p, 在删除 p 或者
- 用 pr 上的最小值 Smin 替换 p, 再删除 p
最佳二叉排序树
- 前边提到,不同输入顺序建立的二叉排序树不同,称其中平均查找性能最高的为最佳二叉排序树
- 按照二分查找判定树的建立过程建立的二排序树为最佳二叉排序树(还需要理解)
- 二叉排序树的查找速度一般比顺序查找块,但如果是单支树一样快
平衡二叉树(AVL树)
注:我们希望建立的二叉排序树均为平衡二叉树
- 空树,或每个结点的平衡因子 BF 不超过1
- **结点的平衡因子 BF ** :结点左子树的深度-右子树深度
构造平衡二叉树
插入过程中采用平衡旋转法
- LL平衡旋转:单向右旋(指针指向的改变)
- A的左孩子B右旋成为新的根结点
- A右旋成为B的右孩子
- B的原右孩子成为A的左孩子·
- RR平衡旋转:单向左旋
- LR平衡旋转:先左后右 (左子树左旋)
- RL平衡旋转:先右后左
旋转操作特点
- 对不平衡的最小子树操作
- 旋转后子树根节点平衡因子为 0
- 旋转后子树深度不变故不影响全树,也不影响插入路径上所有祖先结点的平衡度
性能分析(记忆)
平衡树查找的时间复杂度为 O(log n)
查找和二叉排序树一致,比较的次数不超过平衡二叉树的深度
B-树和B+树(自学)
哈希表
哈希函数:关键字的值和存储位置的对应关系
同义词:发生冲突的两个数据称为同义词
哈希函数构造方法
直接定址法
数字分析法
适用于:能预先估计出全体关键字的每一位上各种数字出现的频度
平方取中法
取平方,扩大差距
除留余数法
随机数法
处理冲突的方法
为产生冲突的地址寻找下一个哈希地址
(理想是通过关键字,不进行比较直接得到存放位置,但是由于冲突的存在还是要进行比较,知识比较的次数变少了)
开放定址法
设定一截探测序列,为发生冲突的数据寻找存放位置
- 线性探测:冲突顺序往后看
- 平方(二次)探测再散列: +1,-1,+4,-4…
- 随机探测再散列
增量d应具备完备性:
- 产生h均不相同,且遂产生m-1个h值能覆盖哈希表中所有地址
- 平方探测时表长m必须形如4*j+1的素数
- 随机探测时m和d没有公因子
再哈希法
链地址法
将所有哈希地址相同的数据(同义词)都链接在同一链表中
建立一个公共溢出区
哈希表查找分析
ASL:平均查找次数 决定因素:
- 选用的哈希函数
- 选用的处理冲突的方法
- 哈希表饱和的程度,装载因子 α =n/m的大小(n—数据数,m—表的长度)
结论
- 哈希表的ASL是α的函数,而不是n的函数
- 哈希表构造查找表时可以选择一个适当的装填因子,使得平均查找长度限定在某个范围内
哈希表删除分析
开放地址法
删除一个数据后为保证查找工作的正常进行不能真删除,需要添加删除标志,删除时只添加删除标记
决定因素:
- 选用的哈希函数
- 选用的处理冲突的方法
- 哈希表饱和的程度,装载因子 α =n/m的大小(n—数据数,m—表的长度)
结论
- 哈希表的ASL是α的函数,而不是n的函数
- 哈希表构造查找表时可以选择一个适当的装填因子,使得平均查找长度限定在某个范围内
哈希表删除分析
[外链图片转存中…(img-k41x78kc-1717853081538)]
开放地址法
删除一个数据后为保证查找工作的正常进行不能真删除,需要添加删除标志,删除时只添加删除标记