1 / 57

第十章 内部排序

第十章 内部排序. 信息查找是信息系统的最主要的操作。如何在大量信息中快速找到所需要的信息一直是信息查找所追求的目标。如果数据是按照某种合理的顺序进行存储,则查找将是最有效的。 排序就是使数据有序的一种基本操作,是组织数据的最基本的运算,采用更有效的排序方法,能很好地提高计算机的效率。. 基本 概念. 排序 : 设 {R 1 ,R 2 ,...,R n } 是 n 个记录的序列 , 其相应的关键字为 {K 1 ,K 2 ,…,K n } 。需确定 1,2,…,n 的一种排列 p 1 ,p 2 ,…,p n ,使其相应满足下面的非递减 ( 或非递增 ) 关系:

Download Presentation

第十章 内部排序

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第十章 内部排序 信息查找是信息系统的最主要的操作。如何在大量信息中快速找到所需要的信息一直是信息查找所追求的目标。如果数据是按照某种合理的顺序进行存储,则查找将是最有效的。 排序就是使数据有序的一种基本操作,是组织数据的最基本的运算,采用更有效的排序方法,能很好地提高计算机的效率。

  2. 基本概念 排序:设{R1,R2,...,Rn}是n个记录的序列,其相应的关键字为{K1,K2,…,Kn}。需确定1,2,…,n的一种排列p1,p2,…,pn,使其相应满足下面的非递减(或非递增)关系: Kp1≤Kp2≤...≤Kpn 使上面n个记录的序列成为一个按关键字有序的序列 { Rp1,Rp2,...,Rpn} 这样一种操作称为排序。 即排序是一个将记录关键字无序的序列调整为一个按关键字有序(递增(非递减)、递减(非递增))的记录序列。 用于排序的关键字Ki:可以是记录的主关键字,也可以是次关键字,甚至还可以是若干数据项的集合。 若是主关键字,则排序的结果是唯一的。 若是次关键字,排序结果不唯一。

  3. 基本概念 排序的稳定性:假设Ki=Kj,i≠j,且在排序前的序列中Ri领先Rj(即i<j),若在排序后的序列中,Ri仍领先Rj,则称所用的排序方法是稳定的。 若在排序后的序列中, Rj领先Ri,则称所用的排序方法是不稳定的。 排序算法的稳定性是指对所有输入实例而言,只要能找出一个实例证明其是不稳定的,就说明它是不稳定的。 内排序:排序期间,全部记录都存放在内存。 外排序:排序期间,只有部分记录在内存,随时存在记录的内外存交换。

  4. 排序过程进行的操作 ①比较操作:比较两个关键字的大小--必不可少的操作。 ②改变记录的逻辑联系或将一个记录从一个位置移动到另一个位置。 是否需要移动,与待排序记录的存储方式有关: ①以顺序表作为存储结构:当两个记录关键字不满足给定关系时,必须移动记录。 ②以链表作为存储结构,通过修改指针来改变记录的逻辑关系,不需移动记录。通常这种排序为链表排序。 ③若存储在一组地址连续的存储单元中,另设一地址向量来指示各记录的存储位置。排序过程中通过移动地址向量中的记录的“地址”来改变记录的次序关系。(排序完成后再调整记录的存储位置,这种存储方式的排序称为地址排序。)

  5. 评价排序算法的标准 ①执行时间; ②所需辅助空间。 若所需辅助空间不依赖于问题的规模n,即辅助空间为O(1),则称为就地排序。非就地排序所要求的辅助空间为O(n)。 排序算法的时间开销主要是关键字的比较次数和记录的移动次数。算法执行时间不仅取决于问题的规模,而且取决于输入实例中的数据状态,分析中一般给出最好、最坏和平均的三种时间性能评价。 本章所讨论的排序,除基数排序外,都是采用顺序表作为存储结构。

  6. 基本假设 为了讨论方便,假设记录关键字均为整数。 待排序的记录的数据类型为: #define n 100 typedef int KeyType; typedef struct { KeyType key; InfoType otherinfo; }RecType; typedef RecType SeqList[n+1]; 若关键字类型没有比较算符,可事先定义宏或函数来表示比较算符。如字符串比较。

  7. 插入排序 一、插入排序算法的基本思想:先将第一个记录看作一个有序子表,然后依次从第二个记录开始逐个将记录插入到前面的有序子表中。 二、直接插入排序----线性插入排序 假设:待排序记录放在数组R[1..n]中。把数组中记录分成两个区R[1..i-1]和R[i..n],其中R[1..i-1]为有序区,R[i..n]为无序区。然后把无序区中记录依次插入到有序区中形成新的有序区。这种方法通常称为增量法,因为每次在有序区中增加一个新记录。 初始时,1个记录自然有序,然后从i=2开始,直到i=n为止,依次将R[i]插入到有序区中。

  8. 直接插入排序 寻找R[i]的插入位置的方法:从j=i-1的记录位置开始依次向前比较: 若R[i].key<R[j].key(j=i-1,i-2,...,1),则将R[j]后移一个位置; 若R[i].key≥R[j].key,则插入位置找到,将R[i]插入到第j+1个位置。 在查找插入位置的过程中,为了避免测试j>=1,在R[0]处设置监视哨。即令: R[0]=R[i] 这样,在向前搜索过程中,总有一个记录有R[i].key≥R[j].key。因此,引入哨兵元素简化了边界条件的判断,使得测试查找循环条件的时间减少一半。

  9. 直接插入排序 设有一组待排序记录的关键自为: {49,38,65,97,76,13,27,49} 【初始关键字序列为】 (49),38,65,97,76,13,27,49 i=2 (38)(38,49),65,97,76,13,27,49 i=3 ( )(38,49,65),97,76,13,27,49 i=4 ( )(38,49,65,97),76,13,27,49 i=5 (76)(38,49,65,76,97),13,27,49 i=6 (13) (13,38,49,65,76,97),27,49 i=7 (27) (13,27,38,49,65,76,97),49 i=8 (49) (13,27,38,49,49,65,76,97) 监视哨

  10. 直接插入排序 算法: void InsertSort(SeqLiat &L) { int i,j; for (i=2;i<=L.length;++i) //对每一个Ri∈R if LT(L.r[i].key, L.r[i-1].key) { L.r[0]=L.r[i]; for (j=i-1;LT (L.r[0].key, L.r[j].key);--j) //寻找插入位置j L.r[j+1]=L.r[j]; //将j..i-1 的记录后移一格 L.r[j+1]=L.r[0]; //将Ri插入到位值j } }

  11. 直接插入排序 空间分析:需要一个记录的辅助空间。 时间分析: 1、若记录关键字已按非递减排列,每趟排序插入,只需进行一次关键字比较,则总的比较次数为n-1。算法时间复杂度为O(n)。 2、若关键字已按非递增排列,则对第i个记录进行查找插入时,要比较i次,移动i+1个记录。则总的比较次数为 ∑i=(n+2)(n-1)/2=(n2+n-2)/2 (i=2..n) 移动记录数为∑(i+1)=(n+4)(n-1)/2=(n2+3n-4)/2 平均比较次数=((n2+n-2)/2+n-1)/2=n2/4+3n/4-1≈n2/4 平均移动次数=((n2+3n-4)/2+2(n-1))/2 =n2/4+7n/4-2≈n2/4 则直接插入排序的时间复杂度为O(n2) 直接插入排序为稳定的排序方法。

  12. 折半插入排序 方法:先利用折半查找方法来确定插入位置,然后向后移动元素进行插入。 优点:比较次数少,但移动元素个数相同。 void BinaryInsertSort(SeqLiat &L) { for(i=2; i<= L.length; i++) //共进行n-1次插入 { L.r[0]=L.r[i]; left=1;right=i-1; while(left<=right) { mid=(left+right)/2; if(L.r[0].key<L.r[mid].key) right=mid-1; else left=mid+1; } for( j=i-1;j>=left;j--) L.r[j+1]=L.r[j]; //元素后移 L.r[left]=L.r[0]; } }

  13. 希尔插入排序 希尔排序(shell’s mothod)也称“缩小增量排序”。 直接插入排序:若待排记录为“正序”时,时间复杂度为O(n),即: 待排记录基本有序时,效率高; 当n很小时,效率也很高。 希尔排序正是针对这两个特点对直接插入排序进行改造的一种插入排序方法。 基本思想:先将整个待排记录分割成若干个子序列分别进行直接插入排序。待整个序列基本有序时,再对全部记录进行一次直接插入排序。 分割方法:选定一个记录的间隔值d,将所有间隔d的记录作为一组。在组内进行直接插入排序。然后缩小间隔,重新分组,再进行组内重新排序,直到间隔为1为止。

  14. 希尔插入排序 例如:设待排序列为 { 49 38 65 97 76 13 27 49 55 04} 按如下方式分组: dh=5 { 49 38 65 97 76 13 27 49 55 04} 对各组直接排序后得到: {13 27 49 55 04 49 38 65 97 76} 共移动5对记录

  15. 希尔插入排序 dh=3 {13 27 49 55 04 49 38 65 97 76} 对各组直接排序后得到: (移动2对记录) {13 04 49 38 27 49 55 65 97 76} 最后dh=1,全序列进行一次直接插入排序得到: {04 13 27 38 49 49 55 65 76 97} 最后移动4对记录。共移动10对记录。直接插入排序共移动27次。

  16. 希尔插入排序 di的取值: 希尔提出的取法:d1=n DIV 2 di+1=(di+1) DIV 2 克努特的取法:di+1=di DIV 3 其他:dk=2t-k+1-1 t为排序躺数=(int)(log2n+1) n=10,t=3,di=7,3,1 算法中,用数组存放每趟分组的间隔。 即:d[1],d[2],.......d[t] 称为增量序列。

  17. 希尔插入排序 viod Shellsort(Sqlist &L,int d[ ], int t) //d[0..t-1]=....7,3,1 { for (k=0;k<t;++k) fz_insert_sort(L,d[k]) ; //分成 d[k]组直接排序 } void fz_insert_sort(Sqlist &L ,int dk); { for (i=dk+1;i<=L.length;++i) if LT(L.r[i].key, L.r[i-dk].key) { L.r[0]=L.r[i]; for (j=i-dk;j>0&&LT (L.r[0].key, L.r[j].key);j-=dk) L.r[j+dk]=L.r[j]; L.r[j+dk]=L.r[0]; } }

  18. 希尔插入排序 算法分析: 当增量序列为dk=2t-k+1-1时,时间复杂都为O(n3/2)。 根据经验统计,shell排序所需比较和移动次数约为n1.3。 当n→∞时,可减少到n(log2n)2。 shell排序是不稳定的排序。

  19. 38 49 65 76 13 27 49 97 38 49 65 13 27 49 76 97 38 49 13 27 49 65 76 97 38 13 27 49 49 65 76 97 13 27 38 49 49 65 76 97 交换排序--冒泡排序 基本思想:每次比较相邻元素关键字,如不符合次序就立即进行交换。 若把元素按纵向排列,在比较和交换的过程中,关键字值小的记录就象气泡一样逐步往上冒,而关键字大的记录象石头一样往下沉。每一次有一块大的石头沉到下面。所以称为冒泡排序。 49 38 65 97 76 13 27 49 13 27 38 49 49 65 76 97

  20. 交换排序--冒泡排序 结束标志:若某遍处理无数据交换,说明已排序好,可提前结束。 若为正序,则只需进行一趟排序,只进行n-1次关键字比较,无记录移动。 若为逆序,则需进行n-1趟排序,需进行n(n-1)/2次比较,并做等数量级的记录移动,算法时间复杂度为O(n2). 算法中可设置一开关变量switch,每遍处理开始时,令其为FALSE,该遍若有数据交换,则置成TRUE。

  21. 交换排序--冒泡排序 算法描述: viod Bubble_sort(Sqlist &L) { for (i= L.length-1;i>0; --i) { switch=FALSE; for (j=0;j<i;++j) if GT(L.r[j].key, L.r[j+1].key) { L.r[j]<==>L.r[j+1]; switch=TRUE; } if (!switch) break; } }

  22. 交换排序--快速排序 基本思想:通过一趟排序,把待排序记录分成独立的两部分。其中一部分记录的关键字比另一部分记录关键字小。然后再按上面方式对每一部分进行同样的分割排序,以达到整个序列有序(直到每一部分的记录数为1为止)。即快速排序是采用二分法的思想进行排序的方法。 实现方法:在待排序记录中先选一记录(通常选第一个记录)作为枢轴,然后将所有关键字小于它的关键字的记录放在它的前面,将所有关键字较它大的记录放在它的位置之后。

  23. 交换排序--快速排序 具体做法:设待排序记录范围为r[low‥high], 设两个指针i和j,其初值分别为low和high,设枢轴记录pivot=r[low],pivotkey=pivot.key。先从j所指记录向前搜索,找到第一个关键字小于pivotkey的记录与pivot互相交换。然后从i所指位置起向后搜索,找到第一个记录关键字比pivotkey大的记录与pivot互换。重复上面两步,直到i=j为止。 这样,以枢轴记录作为分界线,将待排记录分成两个子序列,且r[low‥i-1]<r[i+1‥high],这一过程称为一趟排序.

  24. 例: 49 38 65 97 76 13 27 49 pivot 49 38 65 97 76 13 27 49 i j 27 38 65 97 76 13 4949 i j 27 38 49 97 76 13 6549 i j 27 38 13 97 76 49 65 49 i j 27 38 13 49 76 97 65 49 i j 交换排序--快速排序 [ 27 38 13] 49 [76 97 65 49] ij

  25. 交换排序--快速排序 实现技巧:搜索过程中,在枢轴记录未最终确定之前,可以直接把小于(或大于)枢轴记录的数据记录放到逻辑上所在的位置,而不是使用交换的方法。

  26. 交换排序--快速排序 任意子系列L.r[low..high]的一趟划分算法: int partition(Sqlist &L,int low,int high) //以L.r[low]为枢轴记录,对子系列L.r[low..high]的一趟划分 { L.r[0]= L.r[low]; // 把枢轴记录L.r[low]放在L.[0]中,使L.r[low]空闲 pivotkey=L.r[low].key; while (low<high) //用枢轴记录进行一趟划分 {//在high端,寻找一个比pivotkey小的记录放入low,使L.r[high]空闲 while (low<high &&L.r[high].key>=pivotkey) - -high; L.r[low]=L.r[high]; //在low端,寻找一个比pivotkey大的记录放入high,使L.r[low]空闲 while (low<high &&L.r[low].key<=pivotkey) ++low; L.r[high]=L.r[low]; } L.r[low]=L.r[0]; return low; //找到枢轴记录的位置low }

  27. 交换排序--快速排序 快速排序的递归算法实现 void Quicksort(Sqlist &L,int low,int high) { if (low<high) { loc=Partition(L,low,high); Quicksort(L,low,loc-1); Quicksort(L,loc+1,high); } }

  28. 交换排序--快速排序 算法分析: 1、辅助存储空间:由递归调用时栈的最大深度确定。最坏情况(待排记录为有序)为O(n)。最好情况为O(㏒2n)。 2、时间复杂度:平均时间为kn㏒2n,k为某个常数因子。经验证明,在所有同数量级的此类排序中,快速排序的常数因子k最小。即 快速排序是速度最快的排序。 时间复杂度:O(n㏒2n) 3、有序或基本有序:最坏情况:O(n2) 。若枢轴记录取r[low]、r[(low+high)/2]和r[high]中关键字的中值的记录,并与r[low]互换,可以大大改善最坏情况的快速排序。 4、快速排序是非稳定排序。如 23,22,21,20,23,12,65

  29. 选择排序 基本思想:每一趟从n-i+1(i=1,2,...,n)个记录中选取关键字最小的记录作为有序序列中第i个记录。

  30. 选择排序--简单选择排序 基本思想:第i趟排序开始时,有序区和无序区分别为R[1..i-1]和R[i..n](i=1,2,..n-1),该趟排序则是从n-i+1个记录选取关键字最小的记录R[k],并与第i(i=1,2,...,n)个记录交换,形成新的有序区和无序区。 即从i=1开始,每次从剩余元素中选出最小(最大)元素。这就是直接选择排序。 Void SelectSort(SeqList &L); { int i,j,k; for(i=1;i<L.lenght;i++) { k=i; for(j=i+1;j<=L.lenght;j++) if(R[j].key<R[k].key) k=j; if(k!=i) { R[0]=R[i];R[i]=R[k];R[k]=R[0];} } }

  31. 选择排序--简单选择排序 算法分析: 第i趟排序需比较n-i次,共n-1趟 移动记录最少为0,最多为3(n-1) 关键字比较为n(n-1)/2 因此: 算法时间复杂度为O(n2) 算法空间复杂度: 只需交换数据的一个辅助空间。为就地排序。 排序是非稳定的(交换后可能改变了相对位置)。

  32. 选择排序--堆排序 堆:设有n个元素的序列: {k1, k2, ..., kn} 对所有的i=1,2,...,(int)(n/2),当满足下面关系: ki≤k2i,ki≤k2i+1 或 ki≥k2i,ki≥k2i+1 这样的序列称为堆。 堆的两种类型: 根结点最小的堆----小根堆。 根结点最大的堆----大根堆。 根结点称为堆顶 即:在一棵完全二叉树中,所有非叶结点的值均小于(或均大于)左、右孩子的值。

  33. 96 12 83 27 36 24 53 38 11 09 85 47 30 大根堆 小根堆 91 选择排序—堆排序 例:{ 96,83,27,38,11,09} {12,36,24,85,47,30,53,91} 对应的二叉树结构为:

  34. 选择排序—堆排序 堆排序:是一种树型选择排序,特点是,在排序过程中,把R[1..n]看成是一个完全二叉树的存储结构,利用完全二叉树双亲结点和孩子结点的内在关系,在当前无序区中选择关键字最大(最小)的记录。 堆排序步骤: 1、从k-1层的最右非叶结点开始,使关键字值大(或小)的记录逐步向二叉树的上层移动,最大(或小)关键字记录成为树的根结点,使其成为堆。 2、逐步输出根结点,令r[1]=r[i](i=n,,n-1,...,2),在将剩余结点调整成堆。直到输出所有结点。

  35. 49 49 38 65 38 65 97 76 97 76 13 27 13 27 49 49 49 49 38 65 97 65 97 76 49 76 13 27 13 27 49 38 选择排序—堆排序 例:设由关键字集合:{ 49,38,65,97,76,13,27,49)

  36. 38 第1个与第7个交换 前7个调整成堆 第1个与第8个交换 76 65 49 49 13 27 97 27 76 97 49 65 49 65 76 65 38 49 38 49 13 76 13 27 49 49 13 27 97 97 38 选择排序—堆排序

  37. 65 13 前6个调整成堆 前5个调整成堆 第1个与第5个交换 第1个与第6个交换 49 27 49 27 38 49 13 76 38 49 65 76 97 97 49 13 49 27 49 27 38 13 38 49 65 27 65 27 97 97 选择排序—堆排序

  38. 49 13 前4个调整成堆 前3个调整成堆 第1个与第3个交换 第1个与第4个交换 38 27 38 27 13 49 49 49 65 27 65 76 97 97 38 27 13 27 13 38 49 49 49 49 65 27 65 27 97 97 选择排序—堆排序

  39. 13 27 第1个与第2个交换 前2个调整成堆 27 38 13 38 49 49 49 49 65 76 65 27 97 97 选择排序—堆排序 排序完成:13,27,38,49,49,65,76,97

  40. 选择排序—堆排序 这里要解决两个问题: 1、如何由一个无序序列建成一个堆; 2、输出一个根结点后,如何将剩余元素调整成一个堆。 大根堆:把一棵完全二叉树调整成堆的过程实际上是一个筛选过程,小的关键字筛到筛底,大的关键字浮到筛上面。调整时,取左右孩子的大者进行调整。 1、将一个无序序列建成一个堆的方法: 筛选过程的算法为: 从最后一个非叶结点开始,逐步向根结点,反复进行“筛选”过程,使其结点元素的关键字大于左右孩子结点的关键字。

  41. 选择排序—堆排序 将指定结点为根的子树调整成堆的算法: void Heapadjust(Sqlist &H,int s,int m) // 除结点H.r[s]外,H.r[s+1..m]为堆,将s 结点调整到适当位置 { temp=H.r[s]; for (j=2*s;j<=m;j*=2) { if ( j<m && LT(H.r[j].key,H.r[j+1]) ++j; //直接子树根结点中较大者为 j if ( LT(temp.key,H.r[j].key) { H.r[s]=H.r[j];s=j;} //若 s 结点 小于 j 结点,则交换. else break; } H.r[s]=temp; }

  42. 选择排序—堆排序 堆排序的算法: void Heapsort(Sqlist &H) { // 建立初始堆 for (i=H.length/2;i>0;--i) Heapadjust(H,i,H.length); //将堆顶记录与未排序子序列末记录交换,并调整成堆 for (i=H.length;i>1;--i) { H.r[0]=H.r[1]; H.r[1]=H.r[i]; H.r[i]=H.r[0]; Heapadjust(H,1,i-1) //剩余 1..i-1 个元素调整为堆 } }

  43. 选择排序—堆排序 算法分析: 对深度为k的堆,筛选算法中进行关键字的比较次数至多为2(k-1)。建堆时,n个元素所需的比较次数不超过4n。堆排序时,<2n㏒2n. 堆排序在最坏的情况下,时间复杂度为O(n㏒2n) 仅需一个记录大小的辅助存储空间。 堆排序方法当记录较少时,不值得提倡。当n很大时,效率高。 堆排序是非稳定的。

  44. 归并排序 “归并”的含义:将两个或两个以上有序表组合成一个新的有序表。 归并排序的基本思想:将待排序的n个记录,看作n个有序的子序列,每个子序列的长度为1。然后两两归并,得到n/2个长度为2或为1的子序列;再两两归并,...,如此重复,直到得到长度为n的子序列为止。这种排序的方法称为2_路归并排序。 2_路归并排序的核心操作:将一维数组中前后两个有序序列归并为一个有序序列。 例: [49],[38],[65],[97],[76],[13],[27],[49] [38,49],[65,97],[13,76],[27,49] [38,49,65,97],[13,27,49,76] [13,27,38,49,49,65,76,97] 在归并过程中,需要一个与原来长度相同的表存放归并结果。

  45. 归并排序 将两个有序序列归并为一个有序序列的算法: void merge (Sqlist SR,Sqlist &TR,int i,int m, int n) //将有序表 SR.r[i..m]以及SR.r[m+1..n]有序归并到 TR.r[i..m]中 {la=i;lb=m+1;lc=i; //序列 la,lb,lc 的始点 while(la<=m &&lb<=n) { if LT(SR.r[la].key, SR.r[lb].key) TR.r[lc++]=SR.r[la++] //有序合并 else TR.r[lc++]=SR.r[lb++] } if ( la<=m) TR.r[lc..n]=SR.r[la..m]; //剩余复制 if ( lb<=n) TR.r[lc..n]=SR.r[lb..n]; }

  46. 归并排序 一趟归并排序操作:需调用n/(2h)次算法merge,将SR[1..n]前后相邻且长度为h的有序段两两归并,得到前后相邻、长度为2h的有序段,并放在TR[1..n]中。整个归并排序需要[log2n]趟。 递归算法:排序区间:R[s..t] 设:m=(int)((low+high)/2) 可递归地对两个子区间R[s..m]和R[m+1..t]进行归并排序。然后将两个已排序子区间合并为一个有序区间。 void MSort(SeqList SR,SeqList TR, ints,int t) //将有序表 SR.r[s..t]有序归并排序到 TR.r[s..t]中 { if (s==t) TR.r[s]=SR.r[s]; else { m=(s+t)/2; MSort( SR,MR,s,m); MSort( SR,MR,m+1,t); merge(MR,TR,s,m,t) } }

  47. 归并排序 算法分析: 每趟归并的时间复杂度为O(n), 整个算法需㏒2n趟。时间复杂度为O(n㏒2n)。 归并排序算法虽简单,但占用辅助空间大,实用性差。 归并排序是稳定的排序。

  48. 基数排序--多关键字排序 基数:不同关键字的数目。 例如:已知扑克牌52张牌面次序为: 梅花2->梅花3->...->梅花尖-> 方块2->方块3->...->方块尖-> 黑桃2->黑桃3->...->黑桃尖-> 红桃2->红桃3->...->红桃尖 每一张有两个关键字:花色(梅花<方块<黑桃<红桃) 面值(2<3<...<A) 规定:“花色”的地位高于面值。 要将扑克牌整理成上面次序,通常有两种方法: 1、先按花色分成有次序的4堆。每一堆具有相同花色,再对每一堆按面值进行整理。 2、先按面值分成13堆,然后按面值从小到大重叠起来,再把整个牌颠倒过来再按不同花色分成4堆,然后按花色从小到大合起来,就得到所需要的次序。即分配和收集。 这种整理扑克牌的方法便是多关键字排序的方法。

  49. 基数排序--多关键字排序 设有n个记录的序列 {R1,R2,...,Rn} 且每个记录Ri含有d个关键字(Ki1,Ki2,...,Kid),则称序列{R1,R2,…,Rn}对关键字(K1,K2,…,Kd)有序指:对任意两个记录Ri和Rj(1<=i<j<=n) 都满足下列有序关系: (Ki1,Ki2,...,Kid)<(Kj1,Kj2,...,Kjd) 其中K1为最高位关键字,Kd为最低位关键字。

  50. 基数排序 它是一种借助多关键字(多个排序码)排序的思想来实现单关键字排序的排序方法。 对记录关键字进行定义时,总是要规定其长度和组成的字符。这样,可以把d位关键字看作d个关键字组成的一个多关键字。 例如,若关键字是3位数字组成,可以看作(k1,k2,k3)三个关键字组成。有5个字母组成的关键字可看成(k1,k2,k3,k4,k5)五个关键字组成。由数字组成的关键字有10个不同的取值,其“基”为10;字母组成的关键字有26个不同取值,其“基”为26。“基”指关键字取值的范围。 基数排序:设关键字的基为rd,从关键字低位起,按关键字的不同值将序列中记录“分配”到rd个队列中,再从小到大顺序“收集”起来,如此重复d次。

More Related