快速排序
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序相关的知识,希望对你有一定的参考价值。
快速排序是一种时间复杂度不太稳定的排序算法,也是一种可运用分治策略的排序算法。
这篇就谈一下随机化版本的快速排序的问题,所以快排就给个代码,表示学过了:
/** * 快速排序(QuickSort) */ #include <stdio.h> static int SortByParts(int* nums, int prev, int rear) { int i, j; int temp; int x = nums[rear]; j = prev - 1; for(i = prev; i < rear; i++) { if(nums[i] < x) { temp = nums[i]; nums[i] = nums[++j]; nums[j] = temp; } } temp = nums[rear]; nums[rear] = nums[j + 1]; nums[j + 1] = temp; return j + 1; } void QuickSort(int* nums, int prev, int rear) { int n; if(prev < rear) { n = SortByParts(nums, prev, rear); QuickSort(nums, prev, n - 1); QuickSort(nums, n + 1, rear); } } int main() { int arr[] = {2,8,7,1,3,5,6,4}, *nums = arr; int i; QuickSort(nums, 0, sizeof(arr)/sizeof(int) - 1); for(i = 0; i < sizeof(arr)/sizeof(int); i++) printf("%d ", arr[i]); printf("\n"); return 0; }
随机化版本的快速排序,我比较迷惑的是产生随机数代码的地方,先看看代码(注释的部分):
#include <cstdio> #include <cstdlib> #include <ctime> static int Random(); static int RandomizedControl(); static int ArrayByParts(); void QuickSort(); static int Random(int prev, int rear) { return prev + rand()%(rear + 1 - prev); // 为什么不能是 rand()%(rear + 1 - prev) or rand()%(rear + 1)? } static int RandomizedControl(int* nums, int prev, int rear) { int i = Random(prev, rear); int temp; temp = nums[i]; nums[i] = nums[rear]; nums[rear] = temp; return ArrayByParts(nums, prev, rear); } static int ArrayByParts(int* nums, int prev, int rear) { int x = nums[rear]; int temp; int i, j; j = prev - 1; for(i = prev; i < rear; i++) { if(nums[i] < x) { temp = nums[i]; nums[i] = nums[++j]; nums[j] = temp; } } temp = nums[rear]; nums[rear] = nums[j + 1]; nums[j + 1] = temp; return j + 1; } void QuickSort(int * nums, int start, int end) { int mid; if(start < end) { mid = RandomizedControl(nums, start, end); QuickSort(nums, start, mid - 1); QuickSort(nums, mid + 1, end); } } int main(void) { int arr[] = {13,-3,-25,20,-16,-23,18,-7,12,-5,-22,15,-4,7}, * nums = arr; int i; srand(time(NULL)); QuickSort(nums, 0, sizeof(arr)/sizeof(int) - 1); for(i = 0; i < sizeof(arr)/sizeof(int); i++) printf("%d ", arr[i]); printf("\n"); return 0; }
这个小问题让我郁闷了一会儿,因为不这么写,随机化快排就不正确。但想了想代码的意思难道不是每次调用随机的时候都要交换一次主元吗?快排的思想也是用了分治,所以只要动递归就没什么难理解的,也就知道随机快排主要只是因为快排的分配不均会产生O(n^2)时间复杂度而提出的解决方案,所以随机部分很重要,但我的理解应该是这样的:
#include <cstdio> #include <cstdlib> #include <ctime> static int Random(); static int RandomizedControl(); static int ArrayByParts(); void QuickSort(); /** static int Random(int prev, int rear) { return prev + rand()%(rear + 1 - prev); // 为什么不能是 rand()%(rear + 1 - prev) or rand()%(rear + 1)? 这两种写法的区别是什么 } static int RandomizedControl(int* nums, int prev, int rear) { int i = Random(prev, rear); int temp; temp = nums[i]; nums[i] = nums[rear]; nums[rear] = temp; return ArrayByParts(nums, prev, rear); } */ static int ArrayByParts(int* nums, int prev, int rear) { int x = nums[rear]; int temp; int i, j; j = prev - 1; for(i = prev; i < rear; i++) { if(nums[i] < x) { temp = nums[i]; nums[i] = nums[++j]; nums[j] = temp; } } temp = nums[rear]; nums[rear] = nums[j + 1]; nums[j + 1] = temp; return j + 1; } void QuickSort(int * nums, int start, int end) { int mid; if(start < end) { mid = ArrayByParts(nums, start, end); QuickSort(nums, start, mid - 1); QuickSort(nums, mid + 1, end); } } int main(void) { int arr[] = {13,-3,-25,20,-16,-23,18,-7,12,-5,-22,15,-4,7}, * nums = arr; int i; int temp; srand(time(NULL)); i = rand()%sizeof(arr)/sizeof(int); temp = nums[i]; nums[i] = nums[sizeof(arr)/sizeof(int) - 1]; nums[sizeof(arr)/sizeof(int) - 1] = temp; QuickSort(nums, 0, sizeof(arr)/sizeof(int) - 1); for(i = 0; i < sizeof(arr)/sizeof(int); i++) printf("%d ", arr[i]); printf("\n"); return 0; }
也就是说只随机一次+交换即可...但看代码显然人家又不是这个意思,
return prev + rand()%(rear + 1 - prev)
p + rand()%(x - p) 和rand()%x这句的区别我上网查了查,发现网上没怎么提这个问题,但想到了以前看随机洗牌算法时的一条信息,大概是说这样写就只用交换特定部分(网友原话是这样的:得到的值是没有什么区别, 但是 i+rand()%(res.size()-i) 意味着之前的iteration中swap过的元素不会再被swap, 而rand%size() 则有可能再次swap那些已经swap过的元素, 从而造成不均匀的结果。),我用特定就说明我认为程序不止交换一次,而且能够尽量平均的找到主元来交换,也就是只交换未交换过的数,但原理不明白。。。
这个问题感觉有点玄学...记录一下,仔细查一查。
快排也还有尾递归的实现:
/** * 尾递归 - 快速排序 */ #include <cstdio> #include <cstdlib> #include <ctime> static int Random(); static int RandomizedControl(); static int ArrayByParts(); void QuickSort(); static int Random(int prev, int rear) { return prev + rand()%(rear + 1 - prev); // why not rand()%(rear + 1 - prev) or rand()%(rear + 1)? } static int RandomizedControl(int* nums, int prev, int rear) { int i = Random(prev, rear); int temp; temp = nums[i]; nums[i] = nums[rear]; nums[rear] = temp; return ArrayByParts(nums, prev, rear); } static int ArrayByParts(int* nums, int prev, int rear) { int x = nums[rear]; int temp; int i, j; j = prev - 1; for(i = prev; i < rear; i++) { if(nums[i] < x) { temp = nums[i]; nums[i] = nums[++j]; nums[j] = temp; } } temp = nums[rear]; nums[rear] = nums[j + 1]; nums[j + 1] = temp; return j + 1; } void QuickSort(int * nums, int start, int end) { int mid; while(start < end) { mid = RandomizedControl(nums, start, end); QuickSort(nums, start, mid - 1); start = mid + 1; } } int main(void) { int arr[] = {13,-3,-25,20,-16,-23,18,-7,12,-5,-22,15,-4,7}, * nums = arr; int i; QuickSort(nums, 0, sizeof(arr)/sizeof(int) - 1); for(i = 0; i < sizeof(arr)/sizeof(int); i++) printf("%d ", arr[i]); printf("\n"); return 0; }
然后,我在看完快排,还没看随机排序的时候想的优化方案是求要排序的数组的平均值来作为主元...然后最后交换离主元左边最近的数和最左边的数。但这样会花费线性的时间来求平均值,也就是时间复杂度在快排的低阶上多个常数,虽然没查有没有人用这种方法,但这个想法很简单,肯定有人想过,只是貌似这个方案也并不通用,毕竟书上都没提这种方法...而且如果数据量很大,求平均值在大数据量前也会很慢...随机化的话可能更可靠一些。
最后,就说到这里了,感谢一下自己的学习和遇到的问题吧~
以上是关于快速排序的主要内容,如果未能解决你的问题,请参考以下文章