博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据结构学习笔记06排序 (冒泡、插入、希尔、堆排序、归并排序)
阅读量:5060 次
发布时间:2019-06-12

本文共 9546 字,大约阅读时间需要 31 分钟。

前提

void X_Sort ( ElementType A[], int N )
  大多数情况下,为简单起见,讨论从小大的整数排序
  N是正整数
  只讨论基于比较的排序(> = < 有定义)
  只讨论内部排序
  稳定性:任意两个相等的数据,排序前后的相对位置不发生改变

 

1.冒泡排序

(从小到大排序)

物理意义:大泡泡往下沉,小泡泡往上冒

每次比较相邻两个泡泡,符合条件,交换位置,每一轮比较完,最大的泡泡沉到最底下。

最好情况:顺序T = O( N )

最坏情况:逆序T = O( N^2 )

稳定

1 #include 
2 3 typedef int ElementType; 4 5 void BubbleSort(ElementType A[], int N) 6 { 7 for(int P = N-1; P >= 0; P--) { 8 int flag = 0; 9 for(int i = 0; i < P; i++) {10 if( A[i] > A[i+1] ) {11 ElementType temp = A[i]; //swap A[i] A[i+1]12 A[i] = A[i+1];13 A[i+1] = temp;14 flag = 1;15 }16 }17 if(flag == 0)18 break;19 }20 }21 22 int main()23 {24 int a[] = {
34,8,64,51,32,21};25 BubbleSort(a,6);26 for(int i = 0; i < 6; i++)27 printf("%d ",a[i]);28 29 return 0;30 }
BubbleSort

 

2.插入排序

 (从小到大排序) 

 和打扑克摸牌差不多,每次摸牌从最后往前依次进行比较,需插入的牌小,往前比较,找到合适位置插入。

最好情况:顺序T = O( N )

最坏情况:逆序T = O( N^2 )

 稳定

1 #include 
2 3 typedef int ElementType; 4 5 void InsertionSort(ElementType A[], int N) 6 { 7 int i; 8 for (int P = 1; P < N; P++ ) { 9 ElementType temp = A[P]; //取出未排序序列中的第一个元素10 for (i = P; i > 0 && A[i-1] > temp; i-- )11 A[i] = A[i-1]; //依次与已排序序列中元素比较并右移12 A[i] = temp; 13 }14 }15 16 int main()17 {18 int a[] = {
34,8,64,51,32,21};19 InsertionSort(a,6);20 for(int i = 0; i < 6; i++)21 printf("%d ",a[i]);22 return 0;23 }
InsertionSort

 

3.希尔排序

每5 3 1间隔数进行插入排序。

5 3 1成为增量序列。

  定义增量序列DM > DM-1 > … > D1 = 1

  对每个Dk 进行“Dk-间隔”排序( k = M, M-1, … 1 )
“Dk-间隔”有序的序列,在执行“Dk-1-间隔”排序后,仍然是“Dk-间隔”有序的

 

原始希尔排序DM = [N / 2]向下取整 , Dk = [D(k+1) / 2]向下取整

最坏情况: T =Ο( N^2 )   不稳定

增量元素不互质,则小增量可能根本不起作用。

更多增量序列

  Hibbard 增量序列
    Dk = 2^k – 1 — 相邻元素互质
    最坏情况: T = O ( N^ 3/2 )
    猜想:Tavg = O ( N^ 5/4 )
  Sedgewick增量序列
    {1, 5, 19, 41, 109, … }
      9*4^i – 9*2^i + 1 或4^i – 3*2^i + 1
    猜想:Tavg = O ( N^ 7/6 ),Tworst = O ( N^ 4/3 )

1 #include 
2 3 typedef int ElementType; 4 5 /* 原始希尔排序 */ 6 void Shell_Sort(ElementType A[], int N) 7 { 8 int i; 9 for(int D = N/2; D > 0; D/=2) {10 for(int P = D; P < N; P++) {11 ElementType temp = A[P];12 for(i = P; i >= D && A[i-D]>temp; i-=D)13 A[i] = A[i-D];14 A[i] = temp;15 }16 }17 }18 19 /* 希尔排序 - 用Sedgewick增量序列 */20 void ShellSort(ElementType A[], int N)21 { 22 int Si, i;23 /* 这里只列出一小部分增量 */24 int Sedgewick[] = {
929, 505, 209, 109, 41, 19, 5, 1, 0};25 26 for ( Si = 0; Sedgewick[Si] >= N; Si++ ) 27 ; /* 初始的增量Sedgewick[Si]不能超过待排序列长度 */28 29 for (int D = Sedgewick[Si]; D > 0; D = Sedgewick[++Si])30 for (int P = D; P < N; P++ ) { //插入排序31 ElementType temp = A[P];32 for(i = P; i >= D && A[i-D]>temp; i-=D)33 A[i] = A[i-D];34 A[i] = temp;35 }36 }37 38 int main()39 {40 int a[] = {
34,8,64,51,32,21};41 Shell_Sort(a,6);42 ShellSort(a,6);43 for(int i = 0; i < 6; i++)44 printf("%d ",a[i]);45 return 0;46 }
ShellSort

 

4.堆排序

(从小到大排序) 

首先将堆调整为最大堆,堆顶元素为数组最大元素在A[0]位置,与A[N]交换位置。找到最大元素,将其放在最后。

再调整为最大堆,且堆的大小-1,为N-1。依次执行。

  定理:堆排序处理N个不同元素的随机排列的平均比较次数是2N logN - O(Nlog logN) 。  不稳定

  虽然堆排序给出最佳平均时间复杂度,但实际效果不如用Sedgewick增量序列的希尔排序。

1 #include 
2 3 typedef int ElementType; 4 5 void Swap( ElementType *a, ElementType *b ) 6 { 7 ElementType t = *a; 8 *a = *b; 9 *b = t;10 }11 /* 改编PercDown( MaxHeap H, int p )*/12 void PercDown( ElementType A[], int p, int N )13 { 14 /* 将N个元素的数组中以A[p]为根的子堆调整为最大堆 */15 int Parent, Child;16 17 ElementType X = A[p]; /* 取出根结点存放的值 */18 for( Parent=p; (Parent*2+1) < N; Parent=Child ) {19 Child = Parent * 2 + 1;20 if( (Child != N-1) && (A[Child] < A[Child+1]) )21 Child++; /* Child指向左右子结点的较大者 */22 if( X >= A[Child] ) break; /* 找到了合适位置 */23 else /* 下滤X */24 A[Parent] = A[Child];25 }26 A[Parent] = X;27 }28 29 void HeapSort(ElementType A[], int N) 30 { 31 for(int i = N/2-1; i >= 0; i--)/* 建立最大堆 */32 PercDown( A, i, N );33 34 for(int i = N-1; i > 0; i--) {35 /* 删除最大堆顶 */36 Swap(&A[0], &A[i] ); 37 PercDown(A, 0, i);38 }39 }40 41 int main()42 {43 int a[] = {
34,8,64,51,32,21};44 HeapSort(a,6); 45 for(int i = 0; i < 6; i++)46 printf("%d ",a[i]);47 return 0;48 }
HeapSort

 

5.归并排序

归并排序一般不作内排序,在外排序中用

核心:有序子列的归并

1 /* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/ 2 void Merge( ElementType A[], ElementType TmpA[], int L, int R, int RightEnd ) 3 { /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */ 4  5      int LeftEnd = R - 1; /* 左边终点位置 */ 6      int temp = L;         /* 有序序列的起始位置 */ 7      int NumElements = RightEnd - L + 1; 8        9      while( L <= LeftEnd && R <= RightEnd ) {10          if ( A[L] <= A[R] )11              TmpA[temp++] = A[L++]; /* 将左边元素复制到TmpA */12          else13              TmpA[temp++] = A[R++]; /* 将右边元素复制到TmpA */14      }15  16      while( L <= LeftEnd )17          TmpA[temp++] = A[L++]; /* 直接复制左边剩下的 */18      while( R <= RightEnd )19          TmpA[temp++] = A[R++]; /* 直接复制右边剩下的 */20           21      for(int i = 0; i < NumElements; i++, RightEnd -- )22          A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */23 }
Merge

 

递归算法

 

 分而治之 

 T( N ) = O( N logN )

 稳定

1 #include 
2 #include
3 typedef int ElementType; 4 5 /* 归并排序 - 递归实现 */ 6 7 /* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/ 8 void Merge( ElementType A[], ElementType TmpA[], int L, int R, int RightEnd ) 9 { /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */10 11 int LeftEnd = R - 1; /* 左边终点位置 */12 int temp = L; /* 有序序列的起始位置 */13 int NumElements = RightEnd - L + 1;14 15 while( L <= LeftEnd && R <= RightEnd ) {16 if ( A[L] <= A[R] )17 TmpA[temp++] = A[L++]; /* 将左边元素复制到TmpA */18 else19 TmpA[temp++] = A[R++]; /* 将右边元素复制到TmpA */20 }21 22 while( L <= LeftEnd )23 TmpA[temp++] = A[L++]; /* 直接复制左边剩下的 */24 while( R <= RightEnd )25 TmpA[temp++] = A[R++]; /* 直接复制右边剩下的 */26 27 for(int i = 0; i < NumElements; i++, RightEnd -- )28 A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */29 }30 /* 核心递归排序函数 */ 31 void Msort( ElementType A[], ElementType TmpA[], int L, int RightEnd )32 { 33 int Center;34 35 if ( L < RightEnd ) {36 Center = (L+RightEnd) / 2;37 Msort( A, TmpA, L, Center ); /* 递归解决左边 */ 38 Msort( A, TmpA, Center+1, RightEnd ); /* 递归解决右边 */ 39 Merge( A, TmpA, L, Center+1, RightEnd ); /* 合并两段有序序列 */ 40 }41 }42 /* 归并排序接口函数 */43 void MergeSort( ElementType A[], int N )44 { 45 ElementType *TmpA;46 TmpA = (ElementType *)malloc(N*sizeof(ElementType));47 48 if ( TmpA != NULL ) {49 Msort( A, TmpA, 0, N-1 );50 free( TmpA );51 }52 else printf( "空间不足" );53 }54 55 int main()56 {57 int a[] = {
34,8,64,51,32,21};58 MergeSort(a,6);59 for(int i = 0; i < 6; i++)60 printf("%d ",a[i]);61 return 0;62 }
归并排序 - 递归实现

 

非递归算法

 

 用循环

 O(N)

 稳定

 

1 #include 
2 #include
3 typedef int ElementType; 4 5 /* 归并排序 - 循环实现 */ 6 7 /* L = 左边起始位置, R = 右边起始位置, RightEnd = 右边终点位置*/ 8 void Merge( ElementType A[], ElementType TmpA[], int L, int R, int RightEnd ) 9 { /* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */10 11 int LeftEnd = R - 1; /* 左边终点位置 */12 int temp = L; /* 有序序列的起始位置 */13 int NumElements = RightEnd - L + 1;14 15 while( L <= LeftEnd && R <= RightEnd ) {16 if ( A[L] <= A[R] )17 TmpA[temp++] = A[L++]; /* 将左边元素复制到TmpA */18 else19 TmpA[temp++] = A[R++]; /* 将右边元素复制到TmpA */20 }21 22 while( L <= LeftEnd )23 TmpA[temp++] = A[L++]; /* 直接复制左边剩下的 */24 while( R <= RightEnd )25 TmpA[temp++] = A[R++]; /* 直接复制右边剩下的 */26 27 for(int i = 0; i < NumElements; i++, RightEnd -- )28 A[RightEnd] = TmpA[RightEnd]; /* 将有序的TmpA[]复制回A[] */29 }30 31 /* length = 当前有序子列的长度*/32 void Merge_pass( ElementType A[], ElementType TmpA[], int N, int length )33 { /* 两两归并相邻有序子列 */34 int i, j;35 36 for ( i = 0; i <= N-2*length; i += 2*length )37 Merge( A, TmpA, i, i+length, i+2*length-1 );38 if ( i+length < N ) /* 归并最后2个子列*/39 Merge( A, TmpA, i, i+length, N-1);40 else /* 最后只剩1个子列*/41 for ( j = i; j < N; j++ ) TmpA[j] = A[j];42 }43 44 void Merge_Sort( ElementType A[], int N )45 { 46 int length; 47 ElementType *TmpA;48 49 length = 1; /* 初始化子序列长度*/50 TmpA = (ElementType *)malloc( N * sizeof( ElementType ) );51 if ( TmpA != NULL ) {52 while( length < N ) {53 Merge_pass( A, TmpA, N, length );54 length *= 2;55 Merge_pass( TmpA, A, N, length );56 length *= 2;57 }58 free( TmpA );59 }60 else printf( "空间不足" );61 }62 63 int main()64 {65 int a[] = {
34,8,64,51,32,21};66 Merge_Sort(a,6);67 for(int i = 0; i < 6; i++)68 printf("%d ",a[i]);69 return 0;70 }
归并排序 - 循环实现

 

转载于:https://www.cnblogs.com/kuotian/p/5458963.html

你可能感兴趣的文章
存储开头结尾使用begin tran,rollback tran作用?
查看>>
Activity启动过程中获取组件宽高的五种方式
查看>>
java导出Excel表格简单的方法
查看>>
SQLite数据库简介
查看>>
利用堆实现堆排序&amp;优先队列
查看>>
Mono源码学习笔记:Console类(四)
查看>>
Android学习路线(十二)Activity生命周期——启动一个Activity
查看>>
《Genesis-3D开源游戏引擎完整实例教程-跑酷游戏篇03:暂停游戏》
查看>>
CPU,寄存器,一缓二缓.... RAM ROM 外部存储器等简介
查看>>
windows下编译FreeSwitch
查看>>
git .gitignore 文件不起作用
查看>>
Alan Turing的纪录片观后感
查看>>
c#自定义控件中的事件处理
查看>>
App.config自定义节点读取
查看>>
unity3d根据手机串号和二维码做正版验证
查看>>
二十六、Android WebView缓存
查看>>
django Models 常用的字段和参数
查看>>
linux -- 嵌入式linux下wifi无线网卡驱动
查看>>
SVN使用教程总结
查看>>
SQL中varchar和nvarchar有什么区别?
查看>>