167人参与 • 2024-08-06 • 数据结构
查找表
由同一类型的数据元素(或记录)构成的集合
查找表的操作包括:
关键字
:是**数据元素(或记录)**中某个数据项的值,用以标识(识别)一个数据元素(或记录)
根据给定的值,在查找表中确定一个其关键字等于给定值的数据元素或记录
查找速度:平均查找长度asl(average search length)-为确定记录在表中的位置,需要与给定值进行比较的次数的期望值叫查找算法的平均查找长度
查找方法:从表的一端顺序找到表的另一端 (以线性表表示静态查找表
)
//顺序表定义
#define max 100
typedef struct{
keytype key; ;//说明:keytype代表关键字的类型
//……;
}elemtype
typedef struct{
elemtype elem[max];
int length;
}sqlist;
sqlist l;
顺序查找:
int search(sqlist l, keytypex){
//keytype代表关键字的类型
for(int i=0;i<l.length;i++)
if(l.elem[i].key==x)
return i+1;//找到数据的位置 +1为了方便表示第几个数据
return 0;
}
//说明:返回0说明没找到;若找到,返回是x线性表的第几个数据元素
为提高查找速度,设置哨兵项;查找表组织成线性表,线性表的数据元素从下标为1的数组元素开始存放;下标为0的数组元素作为哨兵项(说明:哨兵可设在低端,也可以设在高端)
查找x,首先将x放在下标为0的数组元素(哨兵),从表尾开始比较。若在哨兵
处相等,说明没找到,否则找到
int srearch(sqlist l,keytypex)//keytype代表关键字的类型
{
int k=l.length;
l.elem[0].key=x;
while(x!=l.elem[k].key)
k--;
return k;
}
//l.elem[0]哨兵项
//说明:返回0说明没找到;若找到,返回是x线性表的第几个数据元素
//说明2:哨兵也可以设在线性表的表尾
采用二分查找的要求:查找表组织成有序线性表(递增或递减),且采用顺序存储结构
特点:通过一次比较,将查找范围缩小一半
二分查找通用模板:
// 在单调递增序列a中查找>=x的数中最小的一个(即x或x的后继)
while (low < high)
{
int mid = (low + high) / 2;
if (a[mid] >= x)
high = mid;
else
low = mid + 1;
}
// 在单调递增序列a中查找<=x的数中最大的一个(即x或x的前驱)
while (low < high)
{
int mid = (low + high + 1) / 2;
if (a[mid] <= x)
low = mid;
else
high = mid - 1;
}
二分查找算法原理:
//有序(递增或递减),顺序存储结构
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; //若找到,返回是x线性表的第几个数据元素
if(l.elem[m].key>x)
high=m-1;
else
low=m+1;
}
return 0;
}
//说明:返回0说明没找到;若找到,返回是x线性表的第几个数据元素
■ 二分查找的效率高,但是要将表按关键字排序。而排序本身是一种很费时的运算。既使采用高效率的排序方法也要花费
o
(
n
l
o
g
n
)
o(nlogn)
o(nlogn)的时间。
■ 二分查找只适用顺序存储结构。为保持表的有序性,在顺序结构里插入和删除都必须移动大量的数据元素。因此,二分查找特别适用于那种一经建立就很少改动、而又经常需要查找的线性表。
■ 对那些查找少而又经常需要改动的线性表,可采用链表作存储结构,进行顺序查找。链表上无法实现二分查找。(*)
线性表分成若干块,每一块中的键值存储顺序是任意的,块与块之间****按键值排序,即后一块中的所有记录的关键字的值均大于前一块中最大键值。
建立一个索引表,该表中的每一项对应于线性表中的一块,每一项由键域(存放相应块的最大键值)和链域(存放指向本块地一个结点的指针)组成。
适用条件:分块有序表
索引顺序表的查找:
数据结构(结构体):
typedef struct{
keytype key; //关键字域
//…… ; // 其它域
}elemtype;//数据结构
typedef struct{
elemtype *elem; // 数据元素存储空间基址
int length; // 表的长度
}sstable;//顺序表
typedef struct{
keytype maxkey; //本块最大关键字
int firstlink; //本块第一个结点下标
}indextable;//索引表的类型
int seach_index(sstable st,indextable it[],int b,int n,keytype k){
int i=1,j;
//b 为块儿数 maxkey为每块儿中的最大值
//索引表查找
while((k>it[i].maxkey)&&(i<=b))
i++;
if(i>b){
printf("\nnot found");
return(0);
}
j=it[i].firstlink;
//块儿内查找
while((k!=st.elem[j].key)&&(st.elem[j].key<=it[i].maxkey) &&(j<n))
j++;
//输出查找结果
if(k!=st.elem[j].key){
j=0;
printf("\nnot found");
}
return(j);
}
■ 表长为n的线性表,整个表分为b块,前 b-1块中的记录数为s=⎡n/b⎤,第b块中的记录数小于或等于s。
■
a
s
l
=
(
b
+
1
)
/
2
+
(
s
+
1
)
/
2
asl=(b+1)/2+(s+1)/2
asl=(b+1)/2+(s+1)/2
(*)
■ 当 s = 根号n ,asl最小 根号n +1
二叉排序树的中序遍历结果递增有序
判断一棵二叉树是否为二叉排序树:看其中序遍历结果是否递增有序
因此,一个无序序列,可通过构建一棵二叉排序树而成为有序序列。
二叉排序树的主要作用与操作
检索:
//二叉链表作为二叉排序树的存储结构
typedef struct node{
int key;//数据元素的关键字
//… ;//数据元素的其他数据项
struct node *lc,*rc;
}binode,*bitree;
//二叉排序树的检索算法
bitree search(binode *t, int x){
bitree p; //
p=t;
while(p!=null){ //没找到就是返回空指针
if (x==p->key) //找到就返回
return p;
if (x < p->key) //左右搜索
p=p->lc;
else
p=p->rc;
}
return p;
}//函数返回查找结果,没找到为空指针!
建立一棵二叉排序树则是逐个插入结点的过程。(*)
同一组数据,输入顺序不同,所建二叉排序树不同!
//
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;
}
//最后没找到的话 就是插入到 q的左孩子或右孩子处
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;
}
从二叉排序树中删除一个结点之后,使其仍能保持二叉排序树的特性。
pr
或只有左子树 pl
,此时,只需将pr
或pl
替换被删结点即可。pl
又有右子树pr
,可按中序遍历保持有序进行调整。左右孩子均存在
用左子树中最大
的结点q
的值替换被删结点,再删"q"
或 用右子树中最小
的结点w
的值替换被删结点,再删"w"
结点q (和w)的特点是最多只有一个孩子
左子树中最大结点的特点是 左子树的根节点一直向右下
直到p->rchild==null
为真,此时p
就是那个左子树中的最大结点。
右子树中最小结点的特点是右子树的根节点一直向左下
,直到p->lchild==null
为真,此时p
就是那个右子树中的最小结点。
二叉排序树查找过程与二分查找判定树相似
n个数据元素按照不同的输入顺序构造的二叉排序树不同,其中平均查找性能最高的为最佳二叉排序树.
按照二分查找判定树的建立过程建立的二叉排序树为最佳二叉排序树。
二叉排序树的查找速度一般比顺序查找快,但如果是单枝树则一样快。
注意单枝树的情况
希望所建的二叉排序树均为平衡二叉树 (avl树)
注意需要时刻考虑到 空树 以及 单枝树 的情况
结点的平衡因子
b
f
=
结点的左子树深度
−
右子树深度
结点的平衡因子bf=结点的左子树深度-右子树深度
结点的平衡因子bf=结点的左子树深度−右子树深度
平衡二叉树每个结点的平衡因子的绝对值不超过1
如果在一棵avl树中插入一个新结点,就有可能造成失衡,此时必须重新调整树的结构,使之恢复平衡。我们称调整平衡过程为平衡旋转。
平衡树查找的时间复杂度为o(logn)
ll型—单向右旋
rr型—单向左旋
lr型—先左旋后右旋:
rl型—先右旋后左旋
在平衡树上进行查找的过程和二叉排序树相同,因此,查找过程中和给定值进行比较的关键字的个数不超过平衡二叉树的深度。
(*)
动态查找表,允许:查找,插入和删除操作
b-树是一种平衡的多路查找树。
不同的表示方法,其差别仅在于:关键字和给定值进行比较的顺序不同
哈希函数是一个映象,即:将关键字的集合映射到某个地址集合上,它的设置很灵活,只要这个地址集合的大小不超出允许范围即可
哈希表构造方法:
实际应用中不保证总能找到一个不产生冲突的哈希函数。一般情况下,只能选择恰当的哈希函数,使冲突尽可能少地产生。
d i d_i di称为增量,有三种取法:
增量
d
i
d_i
di 应具有“完备性”(*)
将发生冲突的数据元素顺序存放于一个公共的溢出区
查找过程和造表过程一致
哈希表饱和的程度,装载因子
α
=
n
/
m
α=n/m
α=n/m 值的大小(n—数据数,m—表的长度)
开放定址法:删除一个数据,为保证查找工作的正常进行不能真删----加删除标志
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论