C语言中快速排序和插入排序优化的实现
快速排序 1962年,由C.A.R.Hoare创造出来。该算法核心思想就一句话:“排序数组时,将数组分成两个小部分,然后对它们递归排序”。然而采取什么样的策略将数组分成两个部分是关键,想想看,如果随便将数组A分成A1和A2两个小部分,即便分别将A1和A2排好序,那么A1和A2重新组合成A时仍然是无序的。所以,我们可以在数组中找一个值,称为key值,我们在把数组A分解为A1和A2前先对A做一些处理,让小于key值的元素都移到其左边,所有大于key值的元素都移到其右边。这样递归排序A1和A2,数组A就排好了。 举例 我们要排序的数组如下: 55 41 59 26 53 58 97 93 我们选取第一个元素作为key值,即55.(一般都是选取第一个元素)。假如我们有一种办法可以将数组做一步预处理,让小于key值的元素都位于其左边,大于key值的元素都位于其右边,预处理完数组如下: 41 26 53 55 59 58 97 93 这样数组就被key值划分成了两段,A[0...2]小于key,A[4...7]大于key,可见key值本身已排好序,接下来对A[0...2]和A[4...7]分别进行递归排序,那么整个数组就排好序了。预处理做的工作再次澄清下:找一个key值,把key位放到某位置A[p],使小于key值的元素都位于A[p]左边,大于key值的元素都位于A[p]的右边。到此,我们的快排模型就成型了。 /*l,u 代表待排序部分的下界和上界*/ void qsort(l,u) { /*递归结束条件是待排序部分的元素个数小于2*/ if(l >= u) { return; } /*此处进行预处理,预处理后key值位于位置p*/ qsort(l,p-1); qsort(p+1,u); } 接下来看如何做预处理。我们选取A[0]做为key值,p作为key值的位置。我们从A[1]开始遍历后面的数组,用变量i指示目前的位置,每次找到小于key的值都与A[++p]交换。开始时p=0. 55 41 59 26 53 58 97 93 i = 1,A[i]位置为41,即A[i] < key,swap(++p,i),即p = 1: 完整的程序1 /*l,u 代表待排序部分的下界和上界*/ void qsort(int l,int u) { /*递归结束条件是待排序部分的元素个数小于2*/ if(l >= u) { return; } int p = l; for(int i = l+1; i <= u; i++) { if(A[i] < A[l]) { swap(++p,i); } } swap(l,p); qsort(l,u); } 这就是第一代快速排序算法,正常情况下其复杂度为nlogn,但在考虑一种极端情况:n个相同元素组成的数组。在n-1次划分中每次划分都需要O(n)的时间,所以总的时间为O(n^2)。使用双向划分就可以避免这个问题。 双向划分快速排序 /*l,u 代表待排序部分的下界和上界*/ void qsort(int l,int u) { /*递归结束条件是待排序部分的元素个数小于2*/ if(l >= u) { return; } key = A[l] for(int i = l,j = u+1; i <= j;) { do i++ while(i <= u && A[i] < key)); do j-- while(A[j] > key); if(i > j) { break; } swap(i,j); } swap(l,j); qsort(l,j-1); qsort(j+1,u); } 插入排序优化 简单的实现(sort1) void insertSort(int *array,size_t size) { for(size_t i = 1; i < size; i++) { for(int j = i; j > 0 && array[j - 1] > array[j]; j--) { swap(array[j - 1],array[j]); } } } 优化思路 优化代码(sort2) void insertSort(int *array,size_t size) { for(size_t i = 1; i < size; i++) { for(int j = i; j > 0 && array[j - 1] > array[j]; j--) { int t = array[j]; array[j] = array[j - 1]; array[j - 1] = t; } } } 优化思路 优化代码(sort3) void insertSort(int *array,size_t size) { for(size_t i = 1; i < size; i++) { int j = i; int t = array[j]; for(; j > 0 && array[j - 1] > array[j]; j--) { array[j] = array[j - 1]; } array[j] = t; } } 《编程珠玑》书中给出了三种排序的运行时间: 插入排序的效率总是O(n2),效率差在比较的次数以及交换的频率,如果交换的频率减少的话就可以大大提高插入排序的效率,这也是为什么元素基本有序时插入排序效率高的原因。 个人观点 代码调优以及性能优化都可能带来一系列的副作用,比如程序的正确性,可读性,可维护性等。是否需要调优要看问题性质,调优既是华而不实的“花活”,也是一把利刃,区别就在于使用的场合。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |