快速排序,找中位
Posted 编程牛人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序,找中位相关的知识,希望对你有一定的参考价值。
陆小凤原创
小白:快速排序算法,这么上古的东西,而且每一种语言都有相应的实现,还需要学吗?
陆小凤:如果目标是解决常规的业务开发,那就没有理由去做这样的事情,有时间还不如去理解业务。但是,如果你的工作对性能的要求很高(比如大量的数据运算),或者你需要看懂一些经常与算法打交道的项目,或者你想感受一下“算法如何把问题变小”的设计思想,或者…
小白:好吧,陆大侠,你总能找到理由来开讲!
快速排序是工程中常见的非稳定的排序算法,最快时为O(nlogn),最差时为O(n^2)。
快排的设计,体现了分而治之的思想,一步步地把问题变小,最终解决。
本文介绍快排的实现设计,试图从中感受分而治之的思想。
快排的关键点,一是确定中位值,二是确定中位。
中位值,坐在中位,左边的数比中位值小,右边的数比中位值大。于是,这个中位值就是最终排序后的一个元素,它的位置不需要再调整。然后,就中位两边的两部分,再重复定值定位的行为,即可解决问题。
小白:也就是每次确定一个值,再对左右两部分递归操作,不断减小问题规模…,你之前讲的递归思想又可以上场了。
陆小凤:设计思想是不断地把问题变小,但不一定用递归来实现,迭代也可以。
(一)定中位值
这个很随意。
一般,可以选择数列最左边,或最右边的值作为中位值。
也可随机地选择一个值(包括中间的值),然后跟最左或最右的值互换位置,于是又回到了一般的思路。
对于中位值,需要用变量记录下来,因为数列中的中位值很快就会被其它值覆盖。
中位值,最终要坐到中位。
(二)定中位
定中位,是实现快排的最复杂的流程。
确定出来的中位,用来放置中位值。
最终,要保证“左小右大”,即中位左边的数小于(或等于)中位值,而右边的数大于(或等于)中位值。
这里介绍两个设计思路。
(1)“小左大右”
设计两个索引i跟j,分列数列的两端。不断地把小值扔到i的位置,然后把大值扔到j的位置,一直保证[start,i]是小值,[j,end]是大值。
最终,i==j,设置好中位值,再返回i。
在拿到中位后,数列分为[start,i-1]跟[i+1,end]两部分,用次“快排”的算法解决它们。
小白:这就是空手套白狼啊,先假设这个函数已经能排序,再在函数里面调用自己。
这是“小左大右”的一个演示图:
可以看到,每一轮的定位,都是不断地把红框区域变小,最终就是中位。
根据这种设计思想,可以这样编码实现:
#include <stdio.h>
int position(int* arr, int i, int j) {
int v = arr[i];
while (i < j) {
while (i < j && arr[j] >= v) {
j--;
}
if (i<j) {
arr[i++] = arr[j];
}
while (i<j && arr[i]<v) {
i++;
}
if (i<j) {
arr[j--]=arr[i];
}
}
arr[i]=v;
return i;
}
void _quicksort(int* arr, int i, int j) {
if (i<j) {
int pos = position(arr, i, j);
_quicksort(arr, i, pos-1);
_quicksort(arr, pos+1, j);
}
}
void quicksort(int* arr, int size) {
_quicksort(arr, 0, size-1);
}
int main(int argc, char *argv[])
{
int arr[] = {4, 2, 5, 1, 6, 6, 8, 9, 8, 3};
int size=sizeof arr/sizeof *arr;
for (int i = 0; i < size; i ++) {
printf("%d, ", arr[i]);
}
quicksort(arr, size);
printf("\nafter_sort:\n");
for (int i = 0; i < size; i ++) {
printf("%d, ", arr[i]);
}
printf("\n");
return 0;
}
(2)“小左”
把所有小值扔到左边,最后,中位就是小值堆的右边的第一个位置。
可以取list[end]即最右的数值为中位值(或最左边也可以)。
设计i、j索引。保证[start,i]为小值,而j用来遍历所有数值(从start到end),如果发现list[j]小于中位值,则扩大[start,i]并把小值扔到里面(list[j]与list[i]互换即可)。
最终,i+1为中位。
示意图是这样的:
按这种设计,可以这样写代码:
#include <iostream>
using namespace std;
template< typename T >
void Exchange(
T & leftElement,
T & rightElement
)
{
T temp = leftElement;
leftElement = rightElement;
rightElement = temp;
}
template< typename T >
int Partition(
T List[],
int nStartPos,
int nStopPos
)
{
if ( nStartPos < 0 || nStopPos < 0 )
return -1;
int nLessEndPos = nStartPos - 1;
int nCurrentDealPos = nStartPos;
while ( nCurrentDealPos < nStopPos )
{
if ( List[nCurrentDealPos] <= List[nStopPos] )
{
nLessEndPos ++;
Exchange( List[nLessEndPos], List[nCurrentDealPos] );
}
nCurrentDealPos ++;
}
Exchange( List[nLessEndPos+1], List[nStopPos] );
return nLessEndPos + 1;
}
template< typename T >
void QuickSort(
T List[],
int nStartPos,
int nStopPos
)
{
if ( nStartPos < nStopPos )
{
int nMidPos = Partition<T>( List, nStartPos, nStopPos );
QuickSort( List, nStartPos, nMidPos - 1 );
QuickSort( List, nMidPos + 1, nStopPos );
}
}
int main(int argc, const char *argv[])
{
int arr[] = {4, 2, 5, 1, 6, 6, 8, 9, 8, 3};
int size=sizeof arr/sizeof *arr;
for (int i = 0; i < size; i ++) {
printf("%d, ", arr[i]);
}
QuickSort<int>(arr, 0, size-1);
printf("\nafter_sort:\n");
for (int i = 0; i < size; i ++) {
printf("%d, ", arr[i]);
}
printf("\n");
return 0;
}
小白:困死了。
猜猜你喜欢
以上是关于快速排序,找中位的主要内容,如果未能解决你的问题,请参考以下文章