快速排序,找中位

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;

}



小白:困死了。


猜猜你喜欢






以上是关于快速排序,找中位的主要内容,如果未能解决你的问题,请参考以下文章

中位数 3 快速排序实现

求中位数,O(n)的java实现利用快速排序折半查找中位数

快速排序——中位数

不用排序怎样快速找到中位数,最好是一遍下来得到结果,求算法或者思路 谢谢!

6.比较排序之快速排序

轻松学习快速排序(二 )-- 快速排序优化