数据结构 第10章_内排序

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
0 1 234 56 7 (a)初始存储状态
key
312 126 272 226 28 165 123
link 1 0
下标 0 1
23 456 7
(b)由第1个记录构成的有序表
key
312 126 272 226 28 165 123
link 2
0
1
下标 0 ቤተ መጻሕፍቲ ባይዱ 2
3 45 6 7
(c)插入第2个记录构成的有序表
printf("\n"); }
/*顺序表文件输入函数*/
void input(table *L,char *f)
{int i;
FILE *fp;
fp=fopen(f,"r");
if (fp)
{
fscanf(fp,"%d",&L->length);
for (i=1;i<=L->length;i++)
while (left<=right)
/*查找第i个元素的插入位置*/
{ mid=(left+right)/2; /*取中点位置*/
if (L->r[i].key<L->r[mid].key) right=mid-1;
else
left=mid+1;
}
/*插入位置为left*/
for(j=i-1;j>=left;j--) /*后移留空*/
➢ 归并排序
➢ 选择排序
直接选择排序 堆排序
➢交换排序 冒泡排序 快速排序
排序是数据处理过程中经常使用的一种重要的运 算,排序的方法有很多种,本章主要讨论内排序的各 种算法,并对每个排序算法的时间和空间复杂性以及 算法的稳定性等进行了讨论。
10.1 排序的基本概念
假设一个文件是由n个记录R1,R2,…,Rn组成, 所谓排序就是以记录中某个(或几个)字段值不减(或 不增)的次序将这n个记录重新排列,称该字段为排 序码。能唯一标识一个记录的字段称为关键码,关 键码可以作为排序码,但排序码不一定要是关键码。
srand(time(NULL)); L->length=10; for (i=1;i<=10;i++) L->r[i].key= rand(i)%100;
} void print(table L) /*顺序表输出函数*/ { int i,k=0;
for (i=1;i<=L.length;i++) {printf("%4d",L.r[i].key); if (++k%10==0) printf("\n"); }
10.2.1 直接插入排序
直接插入排序算法的思路是:初始可认为文件中的 第1个记录己排好序,然后将第2个到第n个记录依次 插入已排序的记录组成的文件中。在对第i个记录Ri进 行插入时,R1,R2,…,Ri-1已排序,将记录Ri的排序 码keyi与已经排好序的排序码从右向左依次比较,找 到Ri应插入的位置,将该位置以后直到Ri-1各记录顺序 后移,空出该位置让Ri插入。
while(p!=0&&tab->r[i].key>=tab->r[p].key) /*找插入 位置*/
{ q=p;
p=tab->r[p].link;
/*继续查找*/
}
tab->r[i].link=p;tab->r[q].link=i;
}}
算法10.3 表插入排序算法
10.2.4 Shell插入排序
直接插入排序算法的时间复杂度为O(n2)。
10.2.2 二分法插入排序
二分法插入排序的思想:
根据插入排序的基本思想,在找第i个记录的插 入位置时,前i-l个记录已排序,将第i个记录的排序码 key[i]和已排序的前i-1个的中间位置记录的排序码进 行比较,如果key[i]小于中间位置记录排序码,则可以 在前半部继续使用二分法查找,否则在后半部继续使 用二分法查找,直到查找范围为空,即可确定key[i]的 插入位置。
按排序过程中使用到的存储介质来分,可以将排 序分成两大类:内排序和外排序。
内排序是指在排序过程中所有数据均放在内存中 处理,不需要使用外存的排序方法。而对于数据量很 大的文件,在内存不足的情况下,则还需要使用外存, 这种排序方法称为外排序。
排序码相同的记录,若经过排序后,这些记录 仍保持原来的相对次序不变,称这个排序算法是稳 定的。否则,称为不稳定的排序算法。
/*记录类型的定义*/
typedef struct{
recordtype r[MAXSIZE+1];
int length;
/*待排序文件中记录的个数*/
}table2;
/*待排序文件类型*/
表插入排序算法的示意如图10.4所示
key link 下标
312 126 272 226 28 165 123
fscanf(fp,"%d",&L->r[i].key);
}
else L->length=0; }
10 50 30 20 90 10 70 80 40 60 100
一个输入文件的例子
10.2 插入排序
插入排序的基本方法是:
将待排序文件中的记录, 逐个地按其排序码值的 大小插入到目前已经排好序的若干个记录组成的文件中 的适当位置,并保持新文件有序。
评价排序算法优劣的标准 :
首先考虑算法执行所需的时间,这主要是用执行 过程中的比较次数和移动次数来度量;
其次考虑算法执行所需要的附加空间。
当然,保证算法的正确性是不言而喻的,可读性 等也是要考虑的因素。
/****************************************/
/*常见排序算法的头文件,文件名table.h
16 21 25 25* 49 16 08 0123456
i=6
08 16 21 25 25* 49 08 012345 6
完成
08 16 21 25 25* 49
习题:给出初始数列{47,28,32,15,94,33, 14,16}在直接插入排序下的状态变化过程。
初态: 47 28 32 15 94 33 14 16
void tableinsertsort(table2 *tab)
{ int i,p,q; tab->r[0].link=1;
tab->r[1].link=0;
/*第1个元素为有序静态表*/
for(i=2;i<=tab->length;i++) /*依次插入从第2个开始的所有 元素*/
{ q=0;p=tab->r[0].link; /*p指向表中第1个元素,q指向 p的前驱元素位置*/
/* 表插入排序定义的头文件,文件名为:table2.h */
#define MAXSIZE 100
/*文件中记录个数的最大值*/
typedef int keytype;
/*定义排序码类型为整数类型*/
typedef struct{
keytype key;
int link;
}recordtype;
i=2 28 47 32 15 94 33 14 16 i=3 28 32 47 15 94 33 14 16 i=4 15 28 32 47 94 33 14 16 i=5 15 28 32 47 94 33 14 16 i=6 15 28 32 33 47 94 14 16 i=7 14 15 28 32 33 47 97 16 i=8 14 15 16 28 32 33 47 97
10.2.3 表插入排序
二分法插入排序比较次数通常比直接插入排序 的比较次数少,但移动次数相等。表插入排序将在不 进行记录移动的情况下,利用存储结构有关信息的改 变来达到排序的目的。
给每个记录附设一个所谓的指针域link,它的类 型为整型,表插入排序的思路:在插入第i个记录Ri时, R1,R2,…,Ri-1已经通过各自的指针域link按排序码 不减的次序连接成一个(静态链)表,将记录Ri的排 序码keyi与表中已经排好序的排序码从表头向右、或 称向后依次比较,找到Ri应插入的位置,将其插入在 表中,使表中各记录的排序码仍然有序。
L->r[j+1]=L->r[j];
L->r[ left ]=L->r[0];
/*插入第i个元素的副本*/
}
}
算法10.2 二分法插入排序算法
二分法插入排序算法,在查找第i个记录的插入 位置时,每执行一次while循环体,查找范围缩小一 半,和直接插入排序的比较次数对比,二分法插入的 比较次数少于直接插入排序的最多比较次数,而一般 要多于直接插入排序的最少比较次数。总体上讲,当 n较大时,二分法插入排序的比较次数远少于直接插 入排序的平均比较次数,但二者所要进行的移动次数 相等,故二分法插入排序的时间复杂度也是O(n2),所 需的附加存储空间为一个记录空间。
j--;
}
L->r[j+1]=L->r[0];
}
}
算法10.1 直接插入排序算法
直接插入排序算法执行时间的分析:
最好的情况 :
即初始排序码开始就是有序的情况下,直接插入 排序算法的比较次数为(n-1)次,移动次数为2*(n-1) 次。
最坏情况 :
即初始排序码开始是逆序的情况下,因为当插入 第i个排序码时,该算法内循环while要执行i次条件判 断,循环体要执行i-l次,每次要移动1个记录,外循 环共执行n-1次,其循环体内不含内循环每次循环要 进行2次移动操作,所以在最坏情况下,比较次数为 (1+2+…+n)。
*/
/***************************************/
#define MAXSIZE 100 /*文件中记录个数的最大值*/
typedef int keytype; /*定义排序码类型为整数类型*/
typedef struct{
keytype key;
int other;
void insertsort(table *L) /*直接插入排序*/
{int i,j;
for (i=2;i<=L->length;i++)
{ j=i-1;
L->r[0]=L->r[i]; /*放置哨兵*/
while ( L->r[j].key>L->r[0].key )
{
L->r[j+1]=L->r[j];
/*此处还可以定义记录中除排序码外
的其他域*/
}recordtype;
/*记录类型的定义*/
typedef struct{
recordtype r[MAXSIZE+1];
int length;
/*待排序文件中记录的个数*/
}table;
/*待排序文件类型*/
void init(table *L) /*顺序表初始化函数*/ { int i;
void binarysort(table *L) /*折半插入排序*/
{ int left,right,mid,i,j;
for(i=2;i<=L->length;i++)
{ L->r[0]=L->r[i];
/*保存待插入的元素*/
left=1; right=i-1;
/*设置查找范围的左、右
位置值*/
基本思想:对待排记录序列先作“宏观”调整, 再作“微观”调整,它是由D.L.shell在1959年提出 来的。
所谓“宏观”调整,指的是,“跳跃式”的插 入排序。即:将记录序列分成若干子序列,每个子序 列分别进行插入排序。关键是,这种子序列不是由相 邻的记录构成的。假设将n个记录分成d个子序列, 则这d个子序列分别为:
初态
21 25 49 25* 16 08
直 接
01 2 3 4 56

入 排 i=2 序
25 21
25
49
25*
16
08

01 2 3 4 5 6

i =3 49 21 25 49 25* 16 08
01 2 3 4 5 6
i=4 i=5
25* 21 25 49 25* 16 08 0123456
key
312 126 272 226 28 165 123
link 5 0 6 1 3 7 4 2
下标 0 1 2 3 4 5 6
7
(d)所有记录都插入后的结束状态(下标为5的记录的key值最小)
图10.4 表插入排序算法示意图
表插入排序算法:初始时,r[0].Link用于存放表 中第1个记录的下标,r[0].Link的值为1,排序结束时, r[0].Link中存放的是所有排序码中值最小的对应记录 的下标,其它的排序码通过各自的指针域link按不减的 次序连接成一个(静态链)表,最大的排序码对应的 link为0。
高等学校精品课程(省级) 国家十二五规划教材
数据结构
李云清 杨庆红 揭安全
人民邮电出版社
高等学校精品课程(省级) 国家十二五规划教材
第10章 内排序
揭安全
jieanquan@163.com 江西师范大学计算机信息工程学院
第十章 内排序
➢插入排序 直接插入排序 二分插入排序 希尔排序
➢ 基数排序
相关文档
最新文档