第6章 查找与排序Word文档下载推荐.docx
- 文档编号:22850707
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:49
- 大小:85.72KB
第6章 查找与排序Word文档下载推荐.docx
《第6章 查找与排序Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《第6章 查找与排序Word文档下载推荐.docx(49页珍藏版)》请在冰豆网上搜索。
本书仅介绍内查找。
平均查找长度ASL——查找算法的效率,主要是看要查找的值与关键字的比较次数,通常用平均查找长度来衡量。
对一个含n个数据元素的表,查找成功时:
其中:
Pi为找到表中第i个数据元素的概率,且有:
Ci为查找表中第i个数据元素所用到的比较次数。
不同的查找方法有不同的Ci。
查找是许多程序中最消耗时间的一部分。
因而,一个好的查找方法会大大提高运行速度。
静态查找表是数据元素的线性表,可以是基于数组的顺序存储或者以链表存储。
6.1.1顺序查找
顺序查找又称线性查找,是最基本的查找方法之一。
顺序查找既适用于顺序表,也适用于链表。
1.基本思想
从表的一端开始,顺序扫描线性表,依次按给定值kx与关键字(Key)进行比较,若相等,则查找成功,并给出数据元素在表中的位置;
若整个表查找完毕,仍未找到与kx相同的关键字,则查找失败,给出失败信息。
2.算法的实现
现以顺序存储为例,数据元素从下标为1的数组单元开始存放,0号单元作为监测哨,用来存放待找的值kx。
例6.1顺序查找算法。
voidSeqSearch()/*顺序查找*/
{
inta[N],i,x,y;
printf("
\n\t\t建立一个整数的顺序表(以回车为间隔,以-1结束):
\n"
);
for(i=1;
i<
=MAXLEN;
i++)
{
scanf("
%d"
&
a[i]);
if(a[i]==-1)
{
y=i;
break;
}
}
请输入要查找的数据:
"
scanf("
x);
a[0]=x;
i=y-1;
while(i>
=0&
&
a[i]!
=x)
i--;
if(i==0)
printf("
没有找到\n"
else
已找到,在第%d的位置上\n"
i);
}
监测哨的作用:
(1)省去判定循环中下标越界的条件,从而节约比较时间;
(2)保存查找值的副本,查找时若遇到它,则表示查找不成功。
这样在从后向前查找失败时,不必判别查找表是否检测完,从而达到算法统一。
3.顺序查找性能分析
对一个含n个数据元素的表,查找成功时
就上述算法而言,对于n个数据元素的表,给定值kx与表中第i个元素关键字相等,即定位第i个记录时,需进行n-i+1次关键字比较,即Ci=n-i+1。
则查找成功时,顺序查找的平均查找长度为:
设每个数据元素的查找概率相等,即Pi=
,则等概率情况下有
查找不成功时,关键字的比较次数总是n+1次。
算法中的基本工作就是关键字的比较,因此,查找长度的量级就是查找算法的时间复杂度,其为O(n)。
顺序查找缺点是当n很大时,平均查找长度较大,效率低;
优点是对表中数据元素的存储没有要求。
另外,对于线性链表,只能进行顺序查找。
4.改写后的顺序查找算法
(1)数据元素类型定义
Typedefstruct
{keytypekey;
/*关键字域*/
…/*其他域*/
}elemtype;
(2)顺序表的存储结构定义
#defineMax100
{elemtypeelem[Max];
/*0号单元留作监视哨*/
Intlength;
}SSTable;
(3)顺序查找算法
Intsearch_seq(SSTablest,keytypekey)
{/*在顺序表st中查找其关键字等于key的数据元素。
若找到,则函数值为该元素在表中的位置;
否则,为0。
*/
st.elem[0].key=key;
/*哨兵*/
For(i=st.length;
st.elem[i].key!
=key;
--i);
Returni;
实训:
编程实现顺序表的顺序查找算法。
/*顺序查找*/
#include"
stdio.h"
stdlib.h"
string.h"
/*数据元素类型定义*/
typedefstruct{
intkey;
/*学号,关键字域*/
charname[10];
/*姓名*/
intscore;
/*成绩*/
/*静态查找表的顺序存储结构类型定义*/
#defineMax100/*顺序表最大的存储长度*/
elemtypeelem[Max];
/*用一维数组存储顺序表各元素,0号单元留作监视哨*/
intlength;
}sstable;
/*顺序表的查找算法----顺序查找算法*/
intsearch_seq(sstablest,intkey)
{inti;
/*哨兵*/
for(i=st.length;
returni;
/*主程序*/
voidmain()
{sstablest;
inti,searchnum;
\n请输入3个学生的信息:
=3;
\n请输入学号:
st.elem[i].key);
getchar();
/*读取成绩后的回车符号,否则将被姓名读取*/
\n请输入姓名:
gets(st.elem[i].name);
\n请输入成绩:
st.elem[i].score);
st.length=i-1;
\n请输入要查找的学生学号:
searchnum);
i=search_seq(st,searchnum);
if(i==0)
\n没有找到!
\n已找到,在第%d的位置上。
6.1.2折半查找
折半查找也叫二分查找,是一种效率较高的查找方法,但前提是表中元素必须按关键字有序(按关键字递增或递减)排列。
1.折半查找的基本思想
在有序表中,取中间元素作为比较对象,若给定值与中间元素的关键字相等,则查找成功;
若给定值小于中间元素的关键字,则在中间元素的左半区继续查找;
若给定值大于中间元素的关键字,则在中间元素的右半区继续查找。
不断重复上述查找过程,直到查找成功;
或所查找的区域无数据元素,则查找失败。
2.查找的步骤
举例讲解:
例6.2有序表按关键字排列如下:
5,14,18,21,23,29,31,35,38,42,46,49,52
在表中查找关键字为14和22的数据元素。
⑴查找关键字为14的过程
⑵查找关键字为22的过程
具体算法如下:
voidBinSearch()/*二分查找*/
intR[MAXLEN],i,k,low,mid,high,m,nn;
charch;
建立递增有序的查找顺序表(以回车间隔,以-1结束):
for(i=0;
MAXLEN;
R[i]);
if(R[i]==-1)
{
nn=i;
请输入要查找的数据:
k);
low=0;
high=nn-1;
m=0;
while(low<
=high)
mid=(low+high)/2;
m++;
if(R[mid]>
k)high=mid-1;
elseif(R[mid]<
k)low=mid+1;
elsebreak;
if(low>
high)
共进行%d次比较。
m);
if(R[mid]<
k)
mid++;
可将此数插入到%d的位置上。
\n"
mid+1);
要找的数据%d在第%d的位置上。
k,mid+1);
3.二分查找性能分析
从二分查找的过程看,每次查找都是以表的中点为比较对象,并以中点将表分割为两个子表,对定位到的子表继续作同样的操作。
二分查找的时间复杂度为:
O(log2n)。
二分查找的优点是效率高;
其缺点是必须按关键字排序、只适用顺序存储结构,所以进行插入、删除操作必须移动大量的结点。
二分查找适用于那种一经建立就很少改动,而又经常需要查找的线性表。
对于那些经常需要改动的线性表,可以采用链表存储结构,进行顺序查找。
4.改写后的折半查找算法
Intsearch_bin(SSTablest,keytypekey)
{/*在有序表st中折半查找其关键字等于key的数据元素,若找到,则函数的值为该元素在表中的位置,否则为0*/
Low=1;
high=st.length;
While(low<
{mid=(low+high)/2;
If(key=st.elem[mid].key)
Returnmid;
Elseif(key<
st.elem[mid].key)
High=mid-1;
Elselow=mid+1;
Return0;
编程实现顺序表的折半查找算法。
/*有序表的折半查找*/
intelem[Max];
intlength;
intsearch_bin(sstablest,intkey)
{intlow,high,mid;
low=1;
{mid=(low+high)/2;
if(key==st.elem[mid])
returnmid;
elseif(key<
st.elem[mid])
high=mid-1;
elselow=mid+1;
return0;
sstablest;
inti=1,x;
\n建立递增有序的查找顺序表(以-1结束):
st.elem[i]);
while(st.elem[i]!
=-1)
{i++;
\n请输入要查找的数据:
i=search_bin(st,x);
\n没有找到!
6.2常用数据排序算法
6.2.1基本概念
排序(Sorting)——将数据元素(或记录)的任意序列,重新排列成一个按关键字有序(递增或递减)的序列的过程称为排序。
排序方法的稳定和不稳定——若对任意的数据元素序列,使用某个排序方法,对它按关键字进行排序,若对原先具有相同键值元素间的位置关系,排序前与排序后保持一致,称此排序方法是稳定的;
反之,则称为不稳定的。
例如:
对数据键值为:
5,3,8,3,6,6,排序。
若排序后的序列为:
3,3,5,6,6,8,其相同键值的元素位置依旧是3在3前,6在6前,与排序前保持一致,则表示这种排序法是稳定的;
3,3,5,6,6,8,则表示这种排序法是不稳定的。
内排序——整个排序过程都在内存进行的排序称为内排序。
外排序——待排序的数据元素量大,以致内存一次不能容纳全部记录,在排序过程中需要对外存进行访问的排序称为外排序。
限于篇幅,本书仅讨论内排序。
关于外排序的内容可参考其它有关的数据结构教材。
另外,为了便于描述,假设本章所有算法均按递增次序排列。
6.2.2插入排序
1. 直接插入排序
(1)基本思想
直接插入排序(StraightInsertionSort)是一种最简单的排序方法,它的基本操作是将一个记录插到已排序好的有序表中,从而得到一个新的,记录数增1的有序表。
插入前:
(1358)[27496]
有序无序
插入后:
(12358)[7496]
有序无序
(2)排序举例
例6.3 输入元素序列为:
39,28,55,80,75,6,17,45,28按从小到大的序列排序。
第一个取39,作为第一个假设有序的记录,第二个取28,28<
39,则交换。
此后,每取来一个记录就与有序表最后一个关键字比较,若大于或等于最后一个关键字,则插入在其后;
若小于最后一个关键字,则把取来的记录再与前一个关键字比较……,其过程如图6.2所示。
初始关键字:
(39)[285580756174528]
i=1
i=2,取出28(2839)[5580756174528]
i=3,取出55(283955)[80756174528]
i=4,取出80(28395580)[756174528]
i=5,取出75(2839557580)[6174528]
i=6,取出6(62839557580)[174528]
i=7,取出17(6172839557580)[4528]
i=8,取出45(617283945557580)[28]
i=9,取出28(61728283945557580)
监视哨r[0]
图6.2直接插入排序过程
排序以后,相同关键字元素的28和28与排序前的位置保持一致,即28仍然在28之前,所以直接插入排序方法是稳定的。
voidInsertsort()
{for(i=2;
=L;
i++)/*依次插入R[2],R[3],……R[n]*/
if(R[i].key<
R[i-1].key)
{R[0]=R[i];
/*置监视哨*/
j=i-1;
while(R[0].key<
R[j].key)/*查找r[i]的位置*/
{R[j+1]=R[j];
/*向后移动记录*/
j--;
}
R[j+1]=R[0];
/*插入r[i]*/
}
举例:
模拟运行
R[0]R[1]R[2]R[3]
8106i=3
(3)效率分析
直接插入排序的时间复杂度为O(n2),辅助空间为O
(1);
直接插入排序是稳定的排序方法;
直接插入排序最适合待排序关键字基本有序的序列。
编程实现对顺序表的直接插入排序:
voidinsertsort(sstable*st)
{inti,j;
for(i=2;
=st->
length;
{st->
elem[0]=st->
elem[i];
/*置监视哨*/
j=i-1;
while(st->
elem[0]<
st->
elem[j])
elem[j+1]=st->
elem[j];
j--;
st->
elem[0];
{sstable*st;
inti=1;
\n建立整数顺序表(以-1结束):
elem[i]);
elem[i]!
length=i-1;
insertsort(st);
\n排序后的顺序表为:
%d"
st->
2. 二分插入排序(BinaryInsertongSort)
(1)基本思想
直接插入算法虽然简单,但当记录数量n很大时,则比较次数将大大增加,对于有序表(限于顺序存储结构),为了减少关键字的比较次数,可采用二分插入排序。
二分插入排序的基本思想是:
用二分查找法在有序表中找到正确的插入位置,然后移动记录,空出插入位置,再进行插入。
例6.4 设有8个记录已排序,现插入新的关键字为653,请使用二分插入排序方法使之成为一个含有9个记录的新序列。
序列:
12345678
6087170275503512897908
low=1①②③high=8
m=(low+high)/2=(1+8)/2=4
第一步:
取关键字653,与序列中间位置①的关键字比较,653>
275,在后半区继续查找;
第二步:
与后半区中间位置②的关键字比较,653>
512,再继续在后半区找;
第三步:
与后半区中间位置③的关键字比较,653<
897,经三次比较找到插入位置③,然后插入653。
voidBInsSort()
=n;
{r[0]=r[i];
/*将r[i]暂存到r[0]*/
=high)/*在r[low..high]中折半查找有序插入的位置*/
{m=(low+high)/2;
/*折半*/
if(r[0].key<
r[m].key)
high=m-1;
/*插入点在低半区*/
low=m+1;
/*插入点在高半区*/
for(j=i-1;
j>
=high+1;
--j)
r[j+1]=r[j];
/*记录后移*/
r[high+1]=r[0];
}/*插入*/
二分插入排序辅助空间和直接插入相同,为O
(1)。
从时间上比较,二分插入仅减少了比较次数,而记录的移动次数不变,时间复杂度仍为O(n2)。
二分插入排序是稳定的排序方法。
编程实现对顺序表的二分插入排序:
voidBInsSort(sstable*st)
{inti,j,m,low,high;
high=i-1;
{m=(low+high)/2;
if(st->
elem[m])
high=m-1;
elem[high+1]=st->
BInsSort(st);
3. 希尔排序(Shell’sSort)
希尔排序又称“缩小增量排序”,它也是一种插入排序的方法,但在时间上较前两种排序方法有较大的改进。
先将整个待排序记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序时”,再对全体记录进行一次直接插入排序。
其特点是子序列不是简单的逐段分割,而是将相隔某个“增量”的记录组成一个子序列,所以关键字较小的记录不是一步一步地前移,而是跳跃式前移,从而使得在进行最后一趟增量为1的插入排序时,序列已基本有序,只要做少量比较和移动即可完成排序,时间复杂度较低。
例6.5 使用希尔排序方法把下面的待排序列变成一个从小到大的有序序列。
待排序列为:
40,30,60,80,70,10,20,4
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第6章 查找与排序 查找 排序
![提示](https://static.bdocx.com/images/bang_tan.gif)