Presentation is loading. Please wait.

Presentation is loading. Please wait.

9.1 概述 9.2 插入排序 9.3 交换排序 9.4 选择排序 9.5 归并排序 9.6 基数排序 9.7 各种内部排序的比较

Similar presentations


Presentation on theme: "9.1 概述 9.2 插入排序 9.3 交换排序 9.4 选择排序 9.5 归并排序 9.6 基数排序 9.7 各种内部排序的比较"— Presentation transcript:

1 9.1 概述 9.2 插入排序 9.3 交换排序 9.4 选择排序 9.5 归并排序 9.6 基数排序 9.7 各种内部排序的比较
第九章 排序 9.1 概述 9.2 插入排序 9.3 交换排序 9.4 选择排序 9.5 归并排序 9.6 基数排序 9.7 各种内部排序的比较

2 9.1 概述 一、排序 定义——将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列叫~ 二、排序分类
9.1 概述 一、排序 定义——将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列叫~ 二、排序分类 1.按待排序记录所在位置 内部排序:待排序记录存放在内存 外部排序:排序过程中需对外存进行访问的排序 2.按排序依据原则 插入排序:直接插入排序、折半插入排序、希尔排序 交换排序:冒泡排序、快速排序 选择排序:简单选择排序、堆排序 归并排序:2-路归并排序 基数排序

3 3、按排序所需工作量 简单的排序方法:T(n)=O(n²) 先进的排序方法:T(n)=O(logn) 基数排序:T(n)=O(d.n) 4、排序基本操作 比较两个关键字大小 将记录从一个位置移动到另一个位置 三、排序的稳定性 如果两个相同的关键字ki与关键字kj在排序之前其序号为i<j,在排完序后,这两个关键字的关后位置关系依然为i<j, 则认为排序是稳定的,反之认为是不稳定的。

4 9.2 插入排序 9.2.1 直接插入排序 1.排序的思想: 排序过程:整个排序过程为n-1趟插入,即先将序列中第1个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序 2.算法描述: void InsertSort(SqList &L) { for(i=2;i<=L.length;++i) if(LT(L.r[i].key, L.r[i-1].key){ L.r[0]=L.r[i]; //监视哨 L.r[i]=L.r[i-1]; j=i-2; while(LT(L.r[0].key,L.r[j].key){j--;L.r[j+1]=L.r[j];} L.r[j+1]=L.r[0]; }

5 3.算法演示: i= ( ) i= ( ) i= ( ) i= ( ) i= ( ) i= ( ) 27 i= ( ) 27 27 27 38 49 65 76 97 j j j j j j ( ) 排序结果:

6 .若待排序记录按关键字从小到大排列(正序) 关键字比较次数:
4.算法评价: (1)时间复杂度 .若待排序记录按关键字从小到大排列(正序) 关键字比较次数: 记录移动次数: .若待排序记录按关键字从大到小排列(逆序) 关键字比较次数: 记录移动次数: .若待排序记录是随机的,取平均值 关键字比较次数: T(n)=O(n²) 记录移动次数: (2)空间复杂度:S(n)=O(1)

7 9.2.2 折半插入排序 排序过程:用折半查找方法确定插入位置的排序叫~ 例 i=1 (30) 13 70 85 39 42 6 20
…... i= ( ) 20 i= ( ) 20 s j m i= ( ) 20 s j m i= ( ) 20 s j m i= ( ) 20 s j i= ( )

8 1、算法描述 2、算法评价 void BInsertSort(SqList &L) {for(i=2;i<=L.length;++i)
{ L.r[0]=L.r[i]; low=1; high=i-1; //已排序的序列 while(low<=high){ //找位置 m=(low+high)/2; if(LT(L.r[m].key,L.r[m].key) high=m-1; else low=m+1; }//while for(j=i-1;j>=high+1;--j) L.r[j+1]=L.r[j]; //移动 L.r[high+1]=L.r[0]; //插入 } //for }//BInsertSort 2、算法评价 时间复杂度:T(n)=O(n²) 空间复杂度:S(n)=O(1)

9 9.2.3 希尔排序(缩小增量法) 1.排序思想: 先取一个正整数d1<n,把所有相隔d1的记录放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止 2. 算法描述: void ShellInsert(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]; //插入 }// if,for }//ShellInsert void ShellSort(SqList &L,int delta[],int t) { for(k=0;k<t;k++) ShellInsert(L,dlta[k] } //如dlta={5,3,1}

10 例 初始: 取d1=5 一趟分组: 一趟排序: 取d2=3 二趟分组: 二趟排序: 取d3=1 三趟分组: 三趟排序:

11 #define T 3 int d[]={5,3,1}; 13 27 48 55 4 49 38 65 97 76 j i j i j i j i j i 一趟排序: 4 38 27 55 j i j i j i j i j i j i j i j i 二趟排序:

12 3、希尔排序特点 (1) 子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列 (2)希尔排序可提高排序速度,因为 ○分组后n值减小,n²更小,而T(n)=O(n²),所以T(n)从总体上看是减小了 ○关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有序,所以移动次数并不多。 (3) 增量序列取法 ○无除1以外的公因子 ○最后一个增量值必须为1

13 9.3 交换排序 9.3.1 冒泡排序 1、排序思想 (1)将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序r[1].key>r[2].key,则交换;然后比较第二个记录与第三个记录;依次类推,直至第n-1个记录和第n个记录比较为止——第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上 (2)对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录位置 (3)重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止

14 2、冒泡排序算法演示 排序前 10 21 22 33 15 7 8 1 4 找第1个最大数 第1步 10 21 22 33 15 7 8 1 4 15 7 33 33 8 1 33 33 15 4 33 33 找第 i 个最大数 第i步 10 15 7 8 1 4 21 22 33 7 8 15 15 1 15 4 15 从第 1个元素开始到第 i 个元素,把相邻两个元素进行比较,如果前面的比后面的大,则交换两个元素,这样可以得使第 i 个元素是最大的。算法如下: 让 j 从 1 到 i 变化,重复: 如果L.r[j].key>L.r[j+1].key ,则交换L.r[j]与L.r[j+1]

15 3.算法描述和实现 void BubbleSort(SqList &L) { n=L.length; for(i=1;i<=n;i++) {flag=FALSE; //交换标志 for(j=1;j<=n-i;j++) if(GT(L.r[j].key,L.r[j+1].key) //如果前面元素大于后面元素,则交换 { temp=L.r[j];L.r[j]=L.r[j+1];L.r[j+1]=temp; flag=TRUE; //有交换 }//if if(!flag) break; //如果无交换,则结束 }//for }//BubbleSort

16 4、算法评价 (1)时间复杂度 最好情况(正序) ◎比较次数:n-1 ◎移动次数:0 最坏情况(逆序) ◎比较次数: ◎移动次数:
T(n)=O(n²) (2)空间复杂度:S(n)=O(1)

17 1、基本思想:通过一趟排序,将待排序记录分割成独立的三部分
9.3.2 快速排序 1、基本思想:通过一趟排序,将待排序记录分割成独立的三部分 (x1,x2,x3,...,xk-1) xk (xk+1,xk+2,....,xn) 其中,xk比左边的所有元素都大,但比右边所有元素都小。 2、排序过程:对r[s……t]中记录进行一趟快速排序,附设两个指针i和j,设数组记录rp=r[s],x=rp.key (1)初始时令i=s,j=t (2)首先从j所指位置向前搜索第一个关键字小于x的记录,并和rp交换 (3)再从i所指位置起向后搜索,找到第一个关键字大于x的记录,和rp交换 (4)重复上述(2)、(3)两步,直至i==j为止 (5)再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止

18 x 初始关键字: 27 49 49 97 13 49 49 65 i j j i i j i j i j j i i j i j 完成一趟排序: ( ) ( ) 分别进行快速排序: ( 13) (38) ( ) (97) 快速排序结束:

19 3、算法描述及实现: void QuickSort(SqList &L,int low,int high)
int Patition(SqList &L,int low,int high) { x=L.r[low].key; //用子表第一个元素作分类 while(low<high) { //从后往前走,如果x大于r[high]元素,则结束并交换 while(low<high&&L.r[high].key>=x) --high; swap(L.r[low],L.r[high]); //交换 //从前往后走,如果x小于r[low]元素,则结束并交换 while(low<high&&L.r[low].key<=x) ++low; swap(L.r[low],L.r[high]); //交换 } return low; //分类元素所在下标 void QuickSort(SqList &L,int low,int high) { if(low<high) {m=Patition(L,low,high); QuickSort(SqList,low,m-1); QuickSort(SqList,m+1,high); }

20 4、算法评价 (1)时间复杂度 最好情况(每次总是选到中间值作分类元素) T(n)=O(nlog2n)
(2)空间复杂度:需栈空间以实现递归 最坏情况:S(n)=O(n) 一般情况:S(n)=O(log2n)

21 9.4 选择排序 9.4.1 简单选择排序 1、排序思想 (1)首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换 (2)再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换 (3)重复上述操作,共进行n-1趟排序后,排序结束

22 k k k 例 i=1 初始: [ 49 38 65 97 76 13 27 ] 13 49 j j j j j j k k i=2
2、算法演示 k k k i=1 初始: [ ] 13 49 j j j j j j k k i=2 一趟: [ ] 27 38 j j j j j 二趟: [ ] 三趟: [ ] 四趟: [ ] 五趟: [ ] 六趟: [97 ] 排序结束:

23 3、算法描述与实现 void SelectSort(SqList &L) {n=L.length; for(i=1;i<=n;i++) { p=SelectMinKey(L,i); //在i..n之间选择最小数下标 if(p!=i) swap(L.r[i],L.r[p]); }//for }//selectsort int SelectMinKey(SqList &L,int i) {min=i; for(k=i;k<=L.length;k++) if(LT(L.r[k].key,L.r[min].key) min=k; //找最小下标min return min; }

24 4、算法评价 (1)时间复杂度 记录移动次数 最好情况:0 最坏情况:3(n-1) 比较次数: T(n)=O(n²)
(2)空间复杂度:S(n)=O(1)

25 n个元素的序列(k1,k2,……kn),当且仅当满足下列关系时,称之为堆
9.4.2 堆排序 1、堆的定义: n个元素的序列(k1,k2,……kn),当且仅当满足下列关系时,称之为堆 (i=1,2,…...n/2) kik2i kik2i+1 kik2i kik2i+1 例 (96,83,27,38,11,9) 例 (13,38,27,50,76,65,49,97) 13 27 38 49 65 76 50 97 96 27 9 11 38 83 可将堆序列看成完全二叉树,则堆顶 元素(完全二叉树的根)必为序列中 n个元素的最小值或最大值

26 2、堆排序思想:将无序序列建成一个堆,得到关键字最小(或最大)的记录;输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,则可得到n个元素的次小值;重复执行,得到一个有序序列,这个过程叫~
(1)堆排序需解决的两个问题: 如何由一个无序序列建成一个堆?(建堆) 如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆?(调整堆) (2)第二个问题解决方法——筛选 方法:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”

27 97 27 38 49 65 76 50 13 输出:13 27 49 38 97 65 76 50 13 输出:13 13 27 38 49 65 76 50 97 97 49 38 27 65 76 50 13 输出:13 27 38 49 50 27 65 76 97 13 输出:13 27 65 49 50 27 38 76 97 13 输出:

28 49 65 50 27 38 76 97 13 输出: 76 65 50 27 38 49 97 13 输出: 50 65 76 27 38 49 97 13 输出: 97 65 76 27 38 49 50 13 输出: 65 97 76 27 38 49 50 13 输出: 97 65 76 27 38 49 50 13 输出:

29 76 65 97 27 38 49 50 13 输出: 97 65 76 27 38 49 50 13 输出: 97 65 76 27 38 49 50 13 输出:

30 3、堆排序算法描述 (1)第一个问题解决方法—建堆
void HeapSort(HeapType &H) {for(i=H.length/2;i>0;--i) //建堆 HeapAdjust(H,i,H.length); for(i=H.length;i>1;--i) {swap(H.r[1],H.r[i] HeapAdjust(H,1,i-1); //调整H.r[1..i-1]为最大堆 } }//HeapSort (1)第一个问题解决方法—建堆 方法:从无序序列的第n/2个元素(即此无序序列对应的完全二叉树的最后一个非终端结点)起,至第一个元素止,进行反复筛选

31 例 含8个元素的无序序列(49,38,65,97,76,13,27,50) 49 65 38 27 13 76 97 50 49 65 38 27 13 76 50 97 49 13 38 27 65 76 50 97 49 13 38 27 65 76 50 97 13 27 38 49 65 76 50 97

32 4、调整堆算法描述 5、算法评价 (1)时间复杂度:最坏情况下T(n)=O(nlogn) (2)空间复杂度:S(n)=O(1)
void HeapAdjust(HeapType &H,int s,int m) {//设H.r[s..m]之间的元素除了H.r[s]外,其余的H.r[s+1..m]满足最大堆性质 rc=H.r[s]; j=2*s; while(j<=m) {if(j<m&&LT(H.r[j].key,H.r[j+1].key) ++j; //找s的两个孩子的最大孩子 if(!LT(rc.key,H.r[j].key) break; //如果双亲不大于最大孩子,则已经是堆,结束 H.r[s]=H.r[j]; //最大元素调到双亲 s=j; //以最大孩子作为新的子树,继续向下调整 j=2*s; //新子树s的孩子 }//while }//HeapAdjust 5、算法评价 (1)时间复杂度:最坏情况下T(n)=O(nlogn) (2)空间复杂度:S(n)=O(1)

33 将两个或两个以上的有序表组合成一个新的有序表,叫~ 2、2路归并排序 (1)排序思想
9.5 归并排序 1、归并 将两个或两个以上的有序表组合成一个新的有序表,叫~ 2、2路归并排序 (1)排序思想 设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1 两两合并,得到n/2个长度为2或1的有序子序列 再两两合并,……如此重复,直至得到一个长度为n的有序序列为止

34 初始关键字: [49] [38] [65] [97] [76] [13] [27] 一趟归并后: [ ] [ ] [ ] [27] 二趟归并后: [ ] [ ] 三趟归并后: [ ]

35 3、算法描述 4、算法评价 (1)时间复杂度:T(n)=O(nlog2n) (2)空间复杂度:S(n)=O(n)
void Merge(RcdType SR[],RcdType &TR,int i,int m,int n) { j=m+1; k=i; //SR[i..m]与SR[m+1,n]合并,合并后放入TR[i..n]中 while(i<=m&&j<=n) if(LQ(SR[i].key,SR[j].key)TR[k++]=SR[i++]; else TR[k++]=SR[j++]; if(i<=m)TR[k..n]=SR[i..m]; //如果第一个数组未完,其余放入后面 if(j<=n) TR[k..n]=SR[j..n]; //如果第二个数组未完,其余放入后面 }//Merge //将SR[s..t]归并为TR1[s..t] void MSort(RcdType SR[],RcdType &TR1[],int s,int t) {if(s==t) TR1[s]=SR[s]; else { m=(s+t)/2; MSort(SR,TR2,s,m); //将SR[s..m]归并到TR2[s..m] MSort(SR,TR2,m+1,t);//将SR[m+1..t]归并到TR[m+1..t]中 Merge(TR2,TR1,s,m,t);//将TR2[s..t]归并入TR1中 } 4、算法评价 (1)时间复杂度:T(n)=O(nlog2n) (2)空间复杂度:S(n)=O(n)

36 2<3<……<A<2<3<……<A<
9.6 基数排序 9.6.1 多关键字排序 1.定义 例 对52张扑克牌按以下次序排序: 2<3<……<A<2<3<……<A< 2<3<……<A<2<3<……<A 两个关键字:花色( <<< ) 面值(2<3<……<A) 并且“花色”地位高于“面值” 2、多关键字排序方法 (1)最高位优先法(MSD):先对最高位关键字k1(如花色)排序,将序列分成若干子序列,每个子序列有相同的k1值;然后让每个子序列对次关键字k2(如面值)排序,又分成若干更小的子序列;依次重复,直至就每个子序列对最低位关键字kd排序;最后将所有子序列依次连接在一起成为一个有序序列

37 (2)最低位优先法(LSD):从最低位关键字kd起进行排序,然后再对高一位的关键字排序,……依次重复,直至对最高位关键字k1排序后,便成为一个有序序列
(3)MSD与LSD不同特点 按MSD排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序 按LSD排序,不必分成子序列,对每个关键字都是整个序列参加排序;并且可不通过关键字比较,而通过若干次分配与收集实现排序 9.6.2 链式基数排序 基数排序:借助“分配”和“收集”对单逻辑关键字进行排序的一种方法 链式基数排序:用链表作存储结构的基数排序

38 1、链式基数排序步骤 (1)设置10个队列,f[i]和e[i]分别为第i个队列的头指针和尾指针
(2)第一趟分配对最低位关键字(个位)进行,改变记录的指针值,将链表中记录分配至10个链队列中,每个队列记录的关键字的个位相同 (3)第一趟收集是改变所有非空队列的队尾记录的指针域,令其指向下一个非空队列的队头记录,重新将10个队列链成一个链表 (3)重复上述两步,进行第二趟、第三趟分配和收集,分别对十位、百位进行,最后得到一个有序序列

39 初始状态: 278 109 063 930 589 184 505 269 008 083 e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9] f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9] 一趟分配 930 083 184 505 008 269 589 063 278 109 930 063 083 184 505 278 008 109 589 269 一趟收集:

40 930 063 083 184 505 278 008 109 589 269 一趟收集: e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9] f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9] 二趟分配 109 930 269 589 278 008 184 505 063 083 505 008 109 930 063 269 278 083 184 589 二趟收集:

41 505 008 109 930 063 269 278 083 184 589 二趟收集: e[0] e[1] e[2] e[3] e[4] e[5] e[6] e[7] e[8] e[9] f[0] f[1] f[2] f[3] f[4] f[5] f[6] f[7] f[8] f[9] 三趟分配 083 184 278 589 930 063 008 109 269 505 008 063 083 109 184 269 278 505 589 930 三趟收集:

42 2、算法描述与实现 //分配算法,对第i个关键字进行分配 void Distribute(SLCell &r,int i,ArrType &f,ArrType &r) { for(j=0;j<Radix;++j) f[j]=0; p=r[0].next; while(p!=0) {j=ord(r[p].keys[i]); //将记录中第i个关键字映射到[0..RADIX-1],即第i个关键字对应队列j if(!f[j]) f[j]=p; //如果是空队,直接赋值 else r[e[j]].next=p; //放在队尾元素的后面,r[e[j]]表示队列j的队尾元素一 e[j]=p; //修改队列j的队尾指针 p=r[p].next; //取p元素的下一个元素 }//while }//Distribute void Collect(SLCell &r,int i,ArrType f,ArrType e) { for(j=0;f[j]!=0;j=succ(j)); //找第一非空子表 r[0].next=f[j];t=e[j]; while(j<RADIX){ for(j=succ(j);j<RADIX-1&&f[j]==0;j=succ(j)); if(f(j)!=0) {r[t].next=f[j];t=e[j];} }//while r[t].next=0; }//Collect

43 3、算法评价 (1)时间复杂度: (2)空间复杂度:S(n)=2rd个队列指针+n个指针域空间 分配:T(n)=O(n)
收集:T(n)=O(rd) T(n)=O(d(n+rd)) 其中:n——记录数 d——关键字数 rd——关键字取值范围 (2)空间复杂度:S(n)=2rd个队列指针+n个指针域空间

44 f[0]=0 e[0]=0 f[1]=0 e[1]=0 f[2]=0 e[2]=0 f[3]=0 e[3]=0 f[4]=0 e[4]=0
初始状态: 1 278 109 063 930 589 184 505 269 008 083 2 3 4 5 6 7 8 9 10 f[0]= e[0]=0 f[1]= e[1]=0 f[2]= e[2]=0 f[3]= e[3]=0 f[4]= e[4]=0 f[5]= e[5]=0 f[6]= e[6]=0 f[7]= e[7]=0 f[8]= e[8]=0 f[9]= e[9]=0 4 4 3 10 3 6 6 7 7 1 9 1 2 5 8 2 4 930 063 083 184 505 278 008 109 589 269 3 10 6 7 1 9 2 5 8

45 f[0]=0 e[0]=0 f[1]=0 e[1]=0 f[2]=0 e[2]=0 f[3]=0 e[3]=0 f[4]=0 e[4]=0
930 063 083 184 505 278 008 109 589 269 3 10 6 7 1 9 2 5 8 一趟收集: f[0]= e[0]=0 f[1]= e[1]=0 f[2]= e[2]=0 f[3]= e[3]=0 f[4]= e[4]=0 f[5]= e[5]=0 f[6]= e[6]=0 f[7]= e[7]=0 f[8]= e[8]=0 f[9]= e[9]=0 7 2 9 7 4 4 3 8 3 1 1 10 10 5 6 7 505 008 109 930 063 269 278 083 184 589 9 2 4 3 8 1 10 6 5 二趟收集:

46 f[0]=0 e[0]=0 f[1]=0 e[1]=0 f[2]=0 e[2]=0 f[3]=0 e[3]=0 f[4]=0 e[4]=0
7 505 008 109 930 063 269 278 083 184 589 9 2 4 3 8 1 10 6 5 二趟收集: f[0]= e[0]=0 f[1]= e[1]=0 f[2]= e[2]=0 f[3]= e[3]=0 f[4]= e[4]=0 f[5]= e[5]=0 f[6]= e[6]=0 f[7]= e[7]=0 f[8]= e[8]=0 f[9]= e[9]=0 9 10 3 9 2 6 2 8 8 1 7 5 7 4 9 008 063 083 109 184 269 278 505 589 930 3 10 2 6 8 1 7 5 4 三趟收集: 4

47 总 结 要求: 掌 握:插入算法、选择算法、冒泡算法 基本掌握:快速排序,堆排序,归并排序 希尔排序 理解:基数排序,折半排序
总 结 要求: 掌 握:插入算法、选择算法、冒泡算法 基本掌握:快速排序,堆排序,归并排序 希尔排序 理解:基数排序,折半排序 要素:排序思想,时间复杂度,算法稳定性


Download ppt "9.1 概述 9.2 插入排序 9.3 交换排序 9.4 选择排序 9.5 归并排序 9.6 基数排序 9.7 各种内部排序的比较"

Similar presentations


Ads by Google