收藏 分享(赏)

《数据结构》第10章:排序.ppt

上传人:fmgc7290 文档编号:8494272 上传时间:2019-06-29 格式:PPT 页数:40 大小:298.50KB
下载 相关 举报
《数据结构》第10章:排序.ppt_第1页
第1页 / 共40页
《数据结构》第10章:排序.ppt_第2页
第2页 / 共40页
《数据结构》第10章:排序.ppt_第3页
第3页 / 共40页
《数据结构》第10章:排序.ppt_第4页
第4页 / 共40页
《数据结构》第10章:排序.ppt_第5页
第5页 / 共40页
点击查看更多>>
资源描述

1、王钢 主编 清华大学出版社,数据结构,第10章 排 序,排序的基本概念 常用内部排序 外部排序,直接插入排序,直接插入排序是插入排序中最简单的排序方法,假设排序表中有n个记录,存储在一维数组Rn中。直接插入排序的基本思想为:把待排记录按排序码大小插入到已排好序的有序表中。,算法10.1 直接插入排序算法 void D_InsertSort ( Elemtype R , int n) / 对记录序列R1n作直接插入排序int i; for ( i=2; i=n; +i ) if (Ri.keyRi-1.key) /小于时,需要Ri-1插入有序表适当位置 R0 = Ri; / 复制为监视哨 for

2、 ( j=i-1; R0.key Rj.key; -j ) Rj+1 = Rj; / 记录后移 Rj+1 = R0; / 插入到正确位置 ,例10.1 设一组关键码为8,3,2,5,9,1,6,按递增的顺序进行排序,则基本过程如图10.1所示:,图10.1 直接插入排序的过程,直接插入排序的算法分析,实现排序的基本操作有两个: (1)比较。(2) 移动记录。,折半插入排序,折半插入排序的基本方法是:用二分查找法将待插入记录在有序表中找到正确的插入位置,然后移动记录,空出插入位置,再进行插入。,算法10.2 折半查找算法描述 void B_InsertionSort (Elemtype R ,

3、int n) / 对记录序列R1n作折半插入排序int i, low, high , mid;for ( i=2; i=high+1; -j ) / high+1为插入位置 Rj+1 = Rj; / 记录后移 Rhigh+1 = R0; / 插入记录 ,希尔排序(又称缩小增量排序),希尔排序(Shells sort)又称缩小增量排序,是1959年由D.L.Shell提出来的。希尔排序可以理解为对直接插入排序的一种改进。直接插入排序是从第二个元素开始比较的,循环起始条件为i=2,可以将i=2写成i=1+1,将第一个1用dk代替,即循环初始条件为i= dk +1,dk是一个希尔增量序列,一般可取做

4、dk =5,3,1(当然也可以取其他素数序列,但最后一个必为1),从第i个记录开始,间隔dk 的记录为一组,每次用第i个关键字与第Idk个关键字进行比较后做插入排序。,例10.2 已知排序表关键字序列为:49,38,65,97,76,13,27,49,55,04,则希尔排序的排序过程如图10.2所示。,算法10.3 希尔排序算法 void Shell Insert(Elemtype R , int dk) / 对R1n进行一趟插入排序,dk为步长因子 int i , j ; for (i=dk+1; i0 一趟增量为dk的插入记录 ,冒泡排序,冒泡排序的基本思想:从第一个元素开始,通过每两个相

5、邻元素的比较,大的记录下沉(后移),同时较小的记录就像“气泡”一样浮到序列前面的位置。一趟冒泡排序后,值最大的记录就排在了序列的最后,二趟排序后,第二大的记录就排在了倒数第二个位置,依此类推,直到把整个记录排完为止,序列就变成有序的了。 需要把每一趟冒泡排序最后一个下沉记录的位置记下,下一遍只检查比较到此为止,到所有记录都不发生下沉时,整个过程结束。所以n个元素实施冒泡排序,共需要n1趟排序,每趟需要ni次比较。,算法10.4 冒泡排序的基本算法 void Bubble_Sort(Elemtype R , int n) int i , j , flag; for(i=1;iRj+1.key)

6、R0=Rj; / R0作为交换的中间变量Rj=Rj+1;Rj+1=R0;flag=1;if (flag= =0) return; / 若第一趟过后,没有发生交换,则结束循环 ,快速排序,快速排序是对冒泡排序的一种改进。找一个记录,以它的关键字作为“枢轴”,凡其关键字小于枢轴的记录均移动至该记录之前,反之,凡关键字大于枢轴的记录均移动至该记录之后。致使一趟排序之后,记录的无序序列以该记录关键字为枢轴分割成两部分:枢轴元素左边的所有的关键字值均小于枢轴元素,枢轴元素右边的所有元素值均大于枢轴元素。第二趟再分别对分割成两部分的子序列进行快速排序,依此类推,直到每个待排序列的子序列中只有一个记录时为止

7、,整个排序过程结束。,例10.3 对关键码序列43,60,54,17,73,3,1,55实施一趟快速排序,其排序过程如图10.3所示。,算法10.5 快速排序一次划分算法的描述 int Partition (Elemtype R , int low , int high) / 交换记录子序列Rlowhigh中的记录,使枢轴记录到位,并返回其所 / 在位置,此时,在它之前(后)的记录均不大(小)于它 R0.key= Rlow.key;/ 用子表的第一个记录作枢轴记录 privotkey=Rlow.key; while (low=pivotkey) -high; if (lowhigh) / 将比

8、枢轴记录小的记录交换到低端 Rlow=Rhigh;low+; while (lowhigh / 返回枢轴所在位置 / Partition,算法10.6 快速排序的算法描述 void QSort (Elemtype R , int s, int t) / 对记录序列Rst进行快速排序 if (s t) / 长度大于1 i = Partition(R,s,t); / 将Rst一分为二 QSort(R, s, i-1);/ 对低子表递归排序,i是枢轴位置 QSort(R, i+1,t);/ 对高子表递归排序 void QuickSort(Elemtype R , int n) / 对记录序列进行快速

9、排序 QSort(R, 1, n); ,简单选择排序,简单选择排序是最简单的一种选择排序。其基本思想是:对有n条记录的关键字序列,第i趟简单选择排序是从关键字序列中第i个记录开始到第n 个记录结束的ni+1个记录中选出关键字最小的记录与第i个记录进行交换。(其中i从1到n1共进行n1趟选择排序),例10.4 对数据序列54,32,48,32,60,7,18,40进行简单选择排序,其过程如图10.4所示。,图10.4 简单选择排序的过程,算法10.7 简单选择排序算法 void Select_Sort( Elemtype R , int n) int i , j , k ;for (i=1;in

10、;i+) / 做n-1趟选取for (k=i,j=i+1;j=n;j+) / 在i开始的n-i+1个记录中选关键字最小的记录if (Rj.keyRk.key) k=j; / k中存放关键字最小的记录的下标if (i!=k) R0=Rk; Rk=Ri; Ri=R0; /交换记录,树形选择排序,树形选择排序又称为锦标赛排序,其基本思想是:首先对n个记录的关键字进行两两比较,选出最大的作为子树的根结点。然后在其中n/2个较大的根结点之间再进行两两比较,直到选出最大关键字的记录为止,可以用一棵有n个叶子结点的完全二叉树表示。此时根结点就是排序关键字最大的结点,把选走后的叶设为最小,重新进行比较选择,依

11、此选择下去,直到排序完成。,堆排序,设有n个元素的序列k1,k2,kn,当且仅当待排序列关键字满足下述关系之一时,称之为堆。如图10.7所示分别为小顶堆和大顶堆的表示和存储结构。,(a)堆顶元素取最大值 (b)堆顶元素取最小值 图10.7 堆的示意图,例10.5 对关键码序列16,24,53,47,36,85,30,91构造的堆树如图10.8所示。,(a) 初始序列完全二叉树 (b) 从第4个结点开始调整 (c) 从第3个结点开始调整,(d) 从第2个结点开始调整 (e) 整棵树调整为堆,图10.8 堆调整示意图,算法10.8 筛选的算法 void HeapAdjust (Elemtype R

12、 , int s, int m) / 已知Rst中记录的关键字除Rs之外均满足堆的定义, / 本函数调整Rs 的关键字,使Rsm成为一个大顶堆(对其中记录的关键字而言) int i , j ; Elemtype rc; rc = Rs; for ( j=2*i; j= Rj.key ) break; / 不用调到叶子就到位了 Ri = Rj; i= j; / 准备继续向下调整 Ri = rc; / 插入 ,算法10.9 堆排序的基本算法 void HeapSort ( Elem R , int n ) / 对记录序列R1n进行堆排序 int i; for ( i=n/2; i0; -i )/

13、把R1n建成大顶堆 HeapAdjust ( R, i, n ); for ( i=n; i1; -i ) R0=Ri; / 将堆顶记录和当前未经排序子序列 R1i中最后一个记录相互交换 R1=Ri; Ri=R0; HeapAdjust(R, 1, i-1); / 将R1i-1 重新调整为大顶堆 / HeapSort,归 并 排 序,所谓归并排序就是两个或者两个以上的有序数据序列合并成一个有序序列的过程。 归并排序的基本思想为: (1) 将n个记录的待排序列看成为有n个长度都为1的有序表; (2) 将两两相邻的子表归并为一个有序子表; (3) 重复上述步骤,直至归并为一个长度为n的有序表。,例

14、10.6 对下述关键字15,13,21,25,24,10,12,11实施归并排序,其基本过程如图10.9 所示。,图10.9 归并排序的排序过程,算法10.10 一趟归并排序具体算法 void MergePass(Elemtype R ,Elemtype R1 ,int len,int n) / len是本趟归并中有序表的长度,从R1n归并到R11n中int i;for(i=1;i+2*len-1=n;i=i+2*len) / 对两个长度为len的有序表的合并Merge(R,R1,i, i+len-1,n); / 归并长度为len的两个相邻子文件 if(i+len-1n) Merge(R,i,

15、i+len-1,n); / 一组半的情况 Elseif (i=n) While (i=n) / 最后一组没有合并者R1i+=Ri+;,算法10.11 2-路归并排序迭代算法void MergeSort(Elemtype R ,int n) / 采用自底向上的方法,对R1n进行二路归并排序int len=1;while (lenn) MergePass(R,R1,len); len*=2; MergePass(R1,R,len); ,算法10.12 2-路归并的递归算法 void Msort (Elemtype R ,Elemtype R1 ,int s,int t) / 将Rst归并排序为R1

16、st int m; if (s=t)R1s=Rs;Else m=(s+t)/2; /平分*p表 Msort(R,R1,s,m); / 递归的将Rsm归并为有序的R1sm Msort(R,R1,m+1,t); / 递归的将Rm+1t归并为有序的R1m+1t Merge(R1,R,s,m,t); void MergeSort(Elemtype R , R1 int n) / 对顺序表R作归并排序 Msort(R, R1 , 1 , n); ,基 数 排 序,多关键字的排序 链式基数排序,链式基数排序,例10.7 以静态链表存储的排序表的基数排序过程如图10.10所示。排序表记录关键字为:178,1

17、09,63,593,284,55,269,8,830,如图10.10为分配和收集的过程。,相关的数据结构: #define KEY_NUM 8 /关键字项数 #define RADIX 10 /关键字基数,此时为十进制整数的基数 #define MAX_SPACE 1000 / 分配的最大可利用存储空间 typedef struct KeyType keysKEY_NUM; /关键字字段InfoType otheritems; /其他字段int next; /指针字段NodeType; /静态链表结点类型 typedef struct int f; int e; Q_Node; typedef

18、 Q_Node QueueRADIX; /各队列的头尾指针,基数排序算法 void Distribute(NodeType R,int i, Queue q) /分配算法:静态链表中R中的记录已按key0,key1,keyi-1有序 /本算法按第i个关键字keyi建立RADIX个子表,使同一子表中的记录keyi相同, /qi.f和qi.e分别指向第i个子表的第一个和最后一个记录 int j,p; for (j=0;jRADIX;j+) /各子表初始化为空表qj.f=qj.e=0; for (p=R0.next;p;p=Rp.next) j=ord(Rp.keys i ); /ord将记录中第i

19、个关键字影射到0RADIX-1 if (!fj)qj.f=p; elseRqj.e.next=p; qj.e=p; /将p所指的结点插入到第j个队列中 ,void Collect(NodeType R ,int i,Queue q) /收集算法:本算法按q0qRADIX-1所指各子表依次链接成一个链表 int j; for (j=0;!qj.f;j=succ(j); /找第一个非空子表,succ为求后继函数 R0.next=qj.f;t=qj.e; /R0.next指向第一个非空子表中第一个结点 While (jRADIX) for (j=succ(j);jRADIX-1 /t指向最后一个非空

20、子表中的最后一个结点 ,void RadixSort(NodeType R ,int n) /对R作基数排序,使其成为按关键字升序的静态链表,R0为头结点 int i;Queue q; /定义队列 for (i=0;in;i+)Ri.next=i+1; Rn.next=0; /将R改为静态链表 for (i=0;iKEY_NUM;i+) /按最低位优先依次对各关键字进行分配和收集 Distribute(R,i,q); /第i趟分配Collect(R,i,q); /第i趟收集 ,外 部 排 序,外部排序指的是大文件的排序,即待排的记录存储在外存储器上,在排序过程中需要进行多次的内、外存之间的交换

21、,外部排序基本上由两个相对独立的阶段组成。首先,按可用内存的大小,将外存上含有n个记录的文件分成若干个长度为k的子文件或段,依次读入内存并利用有效的内部排序方法对它们进行排序,并将排序后得到的有序子文件重新写入内存。通常称这些有序子文件为归并段或顺串;然后,对这些归并段进行逐趟归并,使归并段(有序子文件)逐渐由小到大,直至得到整个有序文件为止。,算法10.13 k-路归并算法 typedef int LoserTree(k); / 败者树是完全二叉树且不含叶子,可采用顺序存储结构typedef struct KeyType key; ExNOde,Externalk; / 外结点,只存放待归并

22、记录的关键字 viod K_Merge(LoserTree *ls,Externa *b) / k-路归并处理程序,利用败者树ls将编号从0到k-1的k个输入归并段中的记录归并到输 出归并段,b0到bk-1为败者树上的k个叶子结点,分别存放k个输入归并段中当前记录的关 键字 int i ; LoseTree q; for(i=0;ik;i+)input(bi.key); / 分别从k个输入归并段读入该段当前第一个记录的关键字到外结点 CreateLoserTree(ls); / 建败者树ls,选择最小关键字为b0.key while (bls0.key!=MAXKEY), q=ls0; / q

23、指示当前最小关键字所在归并段output(q); / 将编号为q的归并段中当前(关键字为bq.key的记录写入输 / 出归并段input(bq.key); / 将编号为q的输入归并段中读入下一个记录的关键字Adjust(ls , q); / 调整败者树,选择新的关键字 , output(ls0); / 将含最大关键字MAXKEY的记录写入输出归并段 void Adjust(LoserTree *ls,int s) / 选择最小关键字记录后,从叶到根调整败者树选下一个最小关键字 / 沿从叶子结点bs到根结点ls0的路径调整败者树 int t; t=(s+k)/2; /lst是bs的双亲结点,while (t0)if (bs.keyblst.key slst; / s指示新的胜者 t=t/2; ls0=s; void CreateLoserTree(LoseTree *ls) / 建立败者树,已知b0到bk-1为完全二叉树ls的叶子结点存有k个关键字 / 沿从叶子到根的k条路径将ls调整为败者树 int i; bk.key=MINKEY; / 设MINKEY为关键字可能的最小值 for (i=0;i0;i-)Adjust(ls,i); / 依次从bk-1,bk-2,b0出发调整败者 ,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报