数据结构教案(苏3)PPT资料.ppt
《数据结构教案(苏3)PPT资料.ppt》由会员分享,可在线阅读,更多相关《数据结构教案(苏3)PPT资料.ppt(297页珍藏版)》请在冰豆网上搜索。
Ki=Kj(ij,i,j=1,2,n),且在排序前Ri先于Rj(ij),排序后的记录序列仍然是Ri先于Rj,称排序方法是稳定的,否则是不稳定的。
排序算法有许多,但就全面性能而言,还没有一种公认为最好的。
每种算法都有其优点和缺点,分别适合不同的数据量和硬件配置。
评价排序算法的标准有:
执行时间和所需的辅助空间,其次是算法的稳定性。
若排序算法所需的辅助空间不依赖问题的规模n,即空间复杂度是O
(1),则称排序方法是就地排序,否则是非就地排序。
排序的分类待排序的记录数量不同,排序过程中涉及的存储器的不同,有不同的排序分类。
待排序的记录数不太多:
所有的记录都能存放在内存中进行排序,称为内部排序;
待排序的记录数太多:
所有的记录不可能存放在内存中,排序过程中必须在内、外存之间进行数据交换,这样的排序称为外部排序。
内部排序的基本操作对内部排序地而言,其基本操作有两种:
比较两个关键字的大小;
存储位置的移动:
从一个位置移到另一个位置。
第一种操作是必不可少的;
而第二种操作却不是必须的,取决于记录的存储方式,具体情况是:
记录存储在一组连续地址的存储空间:
记录之间的逻辑顺序关系是通过其物理存储位置的相邻来体现,记录的移动是必不可少的;
记录采用链式存储方式:
记录之间的逻辑顺序关系是通过结点中的指针来体现,排序过程仅需修改结点的指针,而不需要移动记录;
记录存储在一组连续地址的存储空间:
构造另一个辅助表来保存各个记录的存放地址(指针):
排序过程不需要移动记录,而仅需修改辅助表中的指针,排序后视具体情况决定是否调整记录的存储位置。
比较适合记录数较少的情况;
而、则适合记录数较少的情况。
为讨论方便,假设待排序的记录是以的情况存储,且设排序是按升序排列的;
关键字是一些可直接用比较运算符进行比较的类型。
待排序的记录类型的定义如下:
#defineMAX_SIZE100typedefintKeyType;
typedefstructRecTypeKeyTypekey;
/*关键字码*/infoTypeotherinfo;
/*其他域*/RecType;
typedefRecTypeSeqListn+1;
8.2插入排序,采用的是以“玩桥牌者”的方法为基础的。
即在考察记录Ri之前,设以前的所有记录R1,R2,.,Ri-1已排好序,然后将Ri插入到已排好序的诸记录的适当位置。
最基本的插入排序是直接插入排序(StraightInsertionSort)。
8.2.1直接插入排序,1.排序思想将待排序的记录Ri,插入到已排好序的记录表R1,R2,.,Ri-1中,得到一个新的、记录数增加1的有序表。
直到所有的记录都插入完为止。
设待排序的记录顺序存放在数组R1n中,在排序的某一时刻,将记录序列分成两部分:
R1i-1:
已排好序的有序部分;
Rin:
未排好序的无序部分。
显然,在刚开始排序时,R1是已经排好序的。
例:
设有关键字序列为:
7,4,-2,19,13,6,直接插入排序的过程如下图10-1所示:
2.算法实现voidstraight_insert_sort(SqlistR)inti,j;
for(i=2;
i=n;
i+)R0=Ri;
j=i-1;
/*设置哨兵*/while(LT(R0.key,Rj.key)Rj+1=Rj;
j-;
/*查找插入位置*/Rj+1=R0;
/*插入到相应位置*/,3.算法说明算法中的R0开始时并不存放任何待排序的记录,引入的作用主要有两个:
不需要增加辅助空间:
保存当前待插入的记录Ri,Ri会因为记录的后移而被占用;
保证查找插入位置的内循环总可以在超出循环边界之前找到一个等于当前记录的记录,起“哨兵监视”作用,避免在内循环中每次都要判断j是否越界。
4.算法分析最好情况:
若待排序记录按关键字从小到大排列(正序),算法中的内循环无须执行,则一趟排序时:
关键字比较次数1次,记录移动次数2次(RiR0,R0Rj+1)。
则整个排序的关键字比较次数和记录移动次数分别是:
最坏情况:
若待排序记录按关键字从大到小排列(逆序),则一趟排序时:
算法中的内循环体执行i-1,关键字比较次数i次,记录移动次数i+1。
则就整个排序而言:
一般地,认为待排序的记录可能出现的各种排列的概率相同,则取以上两种情况的平均值,作为排序的关键字比较次数和记录移动次数,约为n2/4,则复杂度为O(n2)。
8.2.2其它插入排序,1.折半插入排序当将待排序的记录Ri插入到已排好序的记录子表R1i-1中时,由于R1,R2,Ri-1已排好序,则查找插入位置可以用“折半查找”实现,则直接插入排序就变成为折半插入排序。
算法实现voidBinary_insert_sort(SqlistR)inti,j,low,high,mid;
/*设置哨兵*/,low=1;
high=i-1;
while(low=high+1;
j-)Rj+1=Rj;
Rhigh+1=R0;
/*插入到相应位置*/,从时间上比较,折半插入排序仅仅减少了关键字的比较次数,却没有减少记录的移动次数,故时间复杂度仍然为O(n2)。
排序示例设有一组关键字30,13,70,85,39,42,6,20,采用折半插入排序方法排序的过程如图10-2所示:
2.2-路插入排序是对折半插入排序的改进,以减少排序过程中移动记录的次数。
附加n个记录的辅助空间,方法是:
另设一个和L-R同类型的数组d,L-R1赋给d1,将d1看成是排好序的序列中中间位置的记录;
分别将L-R中的第i个记录依次插入到d1之前或之后的有序序列中,具体方法:
L-Ri.keyRi插入到d1之前的有序表中;
L-Ri.keyd1.key:
L-Ri插入到d1之后的有序表中;
关键点:
实现时将向量d看成是循环向量,并设两个指针first和final分别指示排序过程中得到的有序序列中的第一个和最后一个记录。
排序示例设有初始关键字集合49,38,65,13,97,27,76,采用2-路插入排序的过程如右图10-3所示。
在2-路插入排序中,移动记录的次数约为n2/8。
但当L-R1是待排序记录中关键字最大或最小的记录时,2-路插入排序就完全失去了优越性。
3.表插入排序前面的插入排序不可避免地要移动记录,若不移动记录就需要改变数据结构。
附加n个记录的辅助空间,记录类型修改为:
typedefstructRecNodeKeyTypekey;
infotypeotherinfo;
int*next;
RecNode;
初始化:
下标值为0的分量作为表头结点,关键字取为最大值,各分量的指针值为空;
将静态链表中数组下标值为1的分量(结点)与表头结点构成一个循环链表;
i=2,将分量Ri按关键字递减插入到循环链表;
增加i,重复,直到全部分量插入到循环链表。
设有关键字集合49,38,65,97,76,13,27,49,采用表插入排序的过程如下图10-4所示。
和直接插入排序相比,不同的是修改2n次指针值以代替移动记录,而关键字的比较次数相同,故时间复杂度为O(n2)。
表插入排序得到一个有序链表,对其可以方便地进行顺序查找,但不能实现随即查找。
根据需要,可以对记录进行重排,记录重排详见P268。
8.2.3希尔排序,希尔排序(ShellSort,又称缩小增量法)是一种分组插入排序方法。
1.排序思想先取一个正整数d1(d1n)作为第一个增量,将全部n个记录分成d1组,把所有相隔d1的记录放在一组中,即对于每个k(k=1,2,d1),Rk,Rd1+k,R2d1+k,分在同一组中,在各组内进行直接插入排序。
这样一次分组和排序过程称为一趟希尔排序;
取新的增量d2d1,重复的分组和排序操作;
直至所取的增量di=1为止,即所有记录放进一个组中排序为止。
2.排序示例设有10个待排序的记录,关键字分别为9,13,8,2,5,13,7,1,15,11,增量序列是5,3,1,希尔排序的过程如图10-5所示。
3.算法实现先给出一趟希尔排序的算法,类似直接插入排序。
voidshell_pass(SqlistR,intd)/*对顺序表L进行一趟希尔排序,增量为d*/intj,k;
for(j=d+1;
j0,然后在根据增量数组dk进行希尔排序。
voidshell_sort(SqlistR,intdk,intt)/*按增量序列dk0t-1,对顺序表L进行希尔排序*/intm;
for(m=0;
m=t;
m+)shll_pass(R,dkm);
希尔排序的分析比较复杂,涉及一些数学上的问题,其时间是所取的“增量”序列的函数。
希尔排序特点子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列。
希尔排序可提高排序速度,原因是:
分组后n值减小,n更小,而T(n)=O(n),所以T(n)从总体上看是减小了;
关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有序。
增量序列取法无除1以外的公因子;
最后一个增量值必须为1。
8.3交换排序,是一类基于交换的排序,系统地交换反序的记录的偶对,直到不再有这样一来的偶对为止。
其中最基本的是冒泡排序(BubbleSort)。
8.3.1冒泡排序,1.排序思想依次比较相邻的两个记录的关键字,若两个记录是反序的(即前一个记录的关键字大于后前一个记录的关键字),则进行交换,直到没有反序的记录为止。
首先将R1与R2的关键字进行比较,若为反序(R1的关键字大于R2的关键字),则交换两个记录;
然后比较R2与R3的关键字,依此类推,直到Rn-1与Rn的关键字比较后为止,称为一趟冒泡排序,Rn为关键字最大的记录。
然后进行第二趟冒泡排序,对前n-1个记录进行同样的操作。
一般地,第i趟冒泡排序是对R1n-i+1中的记录进行的,因此,若待排序的记录有n个,则要经过n-1趟冒泡排序才能使所有的记录有序。
2.算法实现#defineFALSE0#defineTRUE1,voidBubble_Sort(SqlistR)intj,k,flag;
for(j=0;
jn;
j+)/*共有n-1趟排序*/flag=TRUE;
for(k=1;
k=n-j;
k+)/*一趟排序*/if(LT(Rk+1.key,Rk.key)flag=FALSE;
R0=Rk;
Rk=Rk+1;
Rk+1=R0;
if(flag=TRUE)break;
故时间复杂度:
T(n)=O(n)空间复杂度:
S(n)=O
(1),4.算法分析时间复杂度最好情况(正序):
比较次数:
n-1;
移动次数:
0;
最坏情况(逆序):
8.3.2快速排序,1.排序思想通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,再分别对这两部分记录进行下一趟排序,以达到整个序列有序。
2.排序过程设待排序的记录序列是Rst,在记录序列中任取一个记录(一般取Rs)作为参照(又称为基准或枢轴),以Rs.ke