【数据结构与算法基础】几种典型的排序算法 / Typical sort algorithms
2011-04-19 19:51
555 查看
// 所有原创文章转载请注明作者及链接
// blackboycpp(AT)gmail.com
// QQ群: 135202158
几种典型的排序算法:冒泡排序、插入排序、桶排序(基数排序前面的文章有,这里不实现了)、希尔排序、堆排序、归并排序,快速排序。
所有算法均使用ANSI C实现。
// blackboycpp(AT)gmail.com
// QQ群: 135202158
几种典型的排序算法:冒泡排序、插入排序、桶排序(基数排序前面的文章有,这里不实现了)、希尔排序、堆排序、归并排序,快速排序。
所有算法均使用ANSI C实现。
/******************************************************************** File: sort.h Author: blackboy blackboycpp@gmail.com Purpose: 常见的各种排序算法,C实现 Created: 2011-04-15 Modified: 2011-04-19 19:43 *********************************************************************/ #ifndef __SORT_H__ #define __SORT_H__ void BubbleSort(int A[], int N); void InsertionSort(int A[], int N); void BucketSort(int A[], int N); void ShellSort(int A[], int N); void HeapSort(int A[], int N); void MergeSort(int A[], int N); void QuickSort(int A[], int N); #endif
/* 关于各种排序算法的参考资料,见 http://en.wikipedia.org/wiki/Sorting_algorithms */ #include <stdlib.h> #include <stdio.h> #include "sort.h" /* 冒泡排序 http://en.wikipedia.org/wiki/Bubble_sort 最差时间复杂度 O(N^2) 最优时间复杂度 O(N) 平均时间复杂度 O(N^2) 冒泡排序由N-1趟排序组成。在第i趟排序后,将前N-i+1个元素中的最大值放到N-i位置,比如,第1趟 排序把N个元素中的最大值放到最后一个位置N-1上。在第i趟排序时,依次将第0到第N-i-1个元素和它的下一 个元素进行比较,把更大的元素放在后面。 可以在第2个循环中加入标记来检测是否发生了元素交换。如果没有发生交换,则证明所有元素已序。 */ void BubbleSort(int A[], int N) { int i, j, tmp; for(i=0; i<N-1; ++i) { for(j=0; j<N-i-1; ++j) { if(A[j] > A[j+1]) { tmp = A[j]; A[j] = A[j+1]; A[j+1] = tmp; } } } } /* 插入排序 http://en.wikipedia.org/wiki/Insertion_sort 最差时间复杂度 O(N^2) 最优时间复杂度 O(N) 平均时间复杂度 O(N^2) 插入排序由N-1趟排序组成,对于第i趟排序,把第i个元素移动到前i+1个元素中正确的位置上,从而 保证0到i位置上的i+1个元素有序。 有意思的是当所有元素已序时,第2个循环将自动停止。 */ void InsertionSort(int A[], int N) { int i, j, tmp; for(i=1; i<N; ++i) { tmp = A[i]; for(j=i; j>0 && A[j-1] > tmp; --j) A[j] = A[j-1]; A[j] = tmp; } } /* 桶排序 http://en.wikipedia.org/wiki/Bucket_sort 最差时间复杂度 O(N^2) 最优时间复杂度 O(N + K) 平均时间复杂度 O(N * K) 设待排序的序列A中的元素大小为0-M。先构造一个大小为(M+1)的桶B,并将桶中所有元素置为0。依次 扫描A中的元素,对于A[i],将B[A[i]]增1。最后,将不为空桶依次输出。 */ #define K 10 void BucketSort(int A[], int N) { int Backet[K] = {0}; int i, j; for(i=0; i<N; ++i) Backet[A[i]]++; for(i=0; i<K; ++i) for(j=0; j<Backet[i]; ++j) printf("%d ", i); printf("/n"); } /* 希尔排序 http://en.wikipedia.org/wiki/Shell_sort 最差时间复杂度 根据增量序列的不同而不同,O(N log^2 N) 最优时间复杂度 O(N) 平均时间复杂度 根据增量序列的不同而不同 希尔排序使用一个增量序列(increment sequence): h1, h2,...,ht. 其中h1 = 1。在使用增量hk的 一趟排序后,保证所有的 A[i] <= A[i+hk],即所有相隔hk的元素是有序的。 以下代码中使用Shell建议的增量序列ht = N/2, hk = [h(k+1)/2],但这个序列没有Hibbard增量或 Sedgewick增量好。 */ void ShellSort(int A[], int N) { int i, j, k, tmp; // k为增量, 对于N=10, 为5,2,1 for(k = N/2; k>0; k /= 2) for(i=k; i<N; ++i) { tmp = A[i]; for(j=i; j>=k; j -= k) { if(tmp < A[j-k]) A[j] = A[j-k]; else break; } A[j] = tmp; } } /* 堆排序 http://en.wikipedia.org/wiki/Heapsort 最差时间复杂度 O(N logN) 最优时间复杂度 Ω(N), O(N logN) 平均时间复杂度 O(N logN) 使用优先队列,确切地说是一个(max)堆。首先对要排序的数组建立堆(BuildHeap),然后执行N次 DeleteMax(),并将删除的元素放在堆的最后一个元素的下一位置。 */ // 这里和二叉堆实现不太一样,实际元素从下标0开始,而不是1 #define LeftChild(i) (2*(i) + 1) // 向下过滤,使大元素向上浮动 static void PercDown(int A[], int i, int N) { int Child, tmp; for(tmp=A[i]; LeftChild(i)<N; i=Child) { Child = LeftChild(i); if(Child != N-1 && A[Child+1] > A[Child]) Child++; if(tmp < A[Child]) A[i] = A[Child]; else break; } A[i] = tmp; } void HeapSort(int A[], int N) { int i, tmp; // BuildHeap, 这是一个(max) Heap for(i=N/2; i>=0; --i) PercDown(A, i, N); // N次DeleteMax for(i=N-1; i>0; --i) { tmp = A[0]; A[0] = A[i]; A[i] = tmp; PercDown(A, 0, i); } } /* 归并排序 http://en.wikipedia.org/wiki/Merge_sort 最差时间复杂度 O(N logN) 最优时间复杂度 O(N), natural variant 平均时间复杂度 O(N logN) 归并排序的基本操作是合并两个已排序的表。它采取了典型的分治(divide-and-conquer)策略,递归地 将要排序的表分成两部分,各自排序,然后再将这两个已序的表合并。 */ static void Merge(int A[], int Temp[], int LPos, int RPos, int RightEnd) { int i, LeftEnd, Counts, TempPos; LeftEnd = RPos - 1; TempPos = LPos; Counts = RightEnd - LPos + 1; // 依次比较两个表中的元素,将较小的元素放入临时表 while(LPos <= LeftEnd && RPos <= RightEnd) { if(A[LPos] <= A[RPos]) Temp[TempPos++] = A[LPos++]; else Temp[TempPos++] = A[RPos++]; } // 至此,左、右两表必有一个有剩余元素,将这些剩余元素复制到临时表 while(LPos <= LeftEnd) Temp[TempPos++] = A[LPos++]; while(RPos <= RightEnd) Temp[TempPos++] = A[RPos++]; // 将已序的临时表拷贝回原表 for(i=0; i<Counts; i++, RightEnd--) A[RightEnd] = Temp[RightEnd]; } static void MSort(int A[], int Temp[], int Left, int Right) { int Center; if(Left < Right) { Center = (Left + Right) / 2; MSort(A, Temp, Left, Center); MSort(A, Temp, Center+1, Right); Merge(A, Temp, Left, Center+1, Right); } } void MergeSort(int A[], int N) { int* Temp; Temp = (int*)malloc(sizeof(int) * N); MSort(A, Temp, 0, N-1); free(Temp); } /* 快速排序 http://en.wikipedia.org/wiki/Quicksort 最差时间复杂度 O(N^2) 最优时间复杂度 O(N logN) 平均时间复杂度 O(N logN) 快速排序主要由以下几步构成(设A为待排序的数组): 1. 如果A中的元素个数为0或1,返回; 2. 从A中选取一个枢纽元Pivot,比较好的办法是“三数中值分割法”,即从A的左、中、右端各取 一个元素,将3个数的中值做为Pivot; 3. 将A的其余元素根据Pivot分成两部分A1和A2,A1的所有元素都小于Pivot,A2则大于等于Pivot; 4. 递归地对A1和A2分别进行排序。 */ static void Swap(int* pa, int* pb) { int tmp = *pa; *pa = *pb; *pb = tmp; } // 三数中值分割法, 取左、中、右3个元素将对其排序,然后将中间的元素放入右元素的前一个 // 位置(Right-1) static int Median3(int A[], int Left, int Right) { int Center = (Left + Right) / 2; if(A[Left] > A[Center]) Swap(&A[Left], &A[Center]); if(A[Left] > A[Right]) Swap(&A[Left], &A[Right]); if(A[Center] > A[Right]) Swap(&A[Center], &A[Right]); Swap(&A[Center], &A[Right-1]); return A[Right-1]; } #define Cufoff 3 static void QSort(int A[], int Left, int Right) { int i, j, Pivot; if(Left+Cufoff <= Right) { Pivot = Median3(A, Left, Right); i = Left; j = Right-1; for(;;) { while(A[++i] < Pivot) {} while(A[--j] > Pivot) {} if(i < j) Swap(&A[i], &A[j]); else break; } Swap(&A[i], &A[Right-1]); QSort(A, Left, i-1); QSort(A, i+1, Right); } else InsertionSort(A+Left, Right-Left+1); // 对于小数组,使用插入排序 } void QuickSort(int A[], int N) { QSort(A, 0, N-1); }
#include <stdlib.h> #include <memory.h> #include <stdio.h> #include "sort.h" #define N 12 const int array = {34, 8, 64, 85, 32, 21, 52, 9, 111, 6, 40, 23}; const int array2 = {3, 1, 9, 0, 7, 8, 2, 5, 4, 6, 3, 2}; // 用于桶排序的小数字数组 void RestoreArray(int A[]) { memcpy(A, array, N * sizeof(int)); } void PrintArray(int A[]) { int i; for(i=0; i<N; ++i) printf("%d ", A[i]); printf("/n"); } int main() { int A ; RestoreArray(A); BubbleSort(A, N); PrintArray(A); RestoreArray(A); InsertionSort(A, N); PrintArray(A); RestoreArray(A); ShellSort(A, N); PrintArray(A); RestoreArray(A); HeapSort(A, N); PrintArray(A); RestoreArray(A); MergeSort(A, N); PrintArray(A); RestoreArray(A); QuickSort(A, N); PrintArray(A); BucketSort(array2, N); system("PAUSE"); return 0; }
相关文章推荐
- 几种典型的立体匹配算法
- 几种sort算法的Java实现
- 算法(algorithms)基础之:选择排序
- STL 排序算法之全排列sort和next_permutation
- 【算法导论】2-1 插入排序insertion-sort.cpp
- 【C#】洗牌算法、利用delegate自定义list的sort排序方法、summary注释
- Algorithms: Merge Sort -- 归并排序
- 算法分析中最常用的几种排序算法(插入排序、希尔排序、冒泡排序、选择排序、快速排序,归并排序)C 语言版
- 几种常见的排序算法之比较
- 几种sort算法的Java实现
- 关于利用STL中的sort算法对向量等容器进行排序的问题
- 算法学习-基数排序(radix sort)卡片排序(card sort) C++数组实现
- 数据结构和算法分析之排序篇--归并排序(Merge Sort)和常用排序算法时间复杂度比较(附赠记忆方法)
- 利用STL中的sort算法对向量等容器进行排序
- Sort 排序的几种用法
- 算法不会,尚能饭否之排序——折半插入排序(Binary Insert Sort)
- sort和qsort的几种排序方法
- 教你几种排序的基本算法
- 算法 之 位图排序 bit-map sort
- STL_算法_局部排序(partial_sort、partial_sort_copy)