您的位置:首页 > 其它

关于排序的一点思考

2017-03-10 15:21 288 查看
排序有很多种方法,最原始的方法莫过于插入排序。

插入排序

算法实现:

void InsertionSort(ElementType A[],int N){

int i,j;

ElementType Tmp;

for ( i = 1;i < N ; i++){

Tmp = A[i];

for(j = i; j > 0 && Tmp < A[j-1]; j++)

A[j] = A[j-1];

A[j] = Tmp;

}

}

时间复杂度分析:

插入排序是最简单粗暴的。插入排序的实现过程就像,假设有一篮球队选拔队员,让队员们从矮到高站好,插入排序的过程就像。让这一队先乱序站好,然后从第二个人开始(数组中下标从1开始),先让这个人出列(赋值给Tmp,这样做的目的是为了避免频繁的交换操作),让这个人和他前面的人比较,如果高过他,则把高的往后挪,直到碰到比他矮的(前面的以排好序,这里不考虑数值相同的情况)才结束,然后把这个人补到空位里面去(A[j]=Tmp)。很明显,这种排序很麻烦。

插入排序的时间复杂度显然是O(N2)。

希尔排序

算法实现:

void ShellSort(ElementType A[],int N){

int i,j,Increment;

ElementType Tmp;

for(Increment = N/2;Increment > 0;Increment /=2)

for(i = Increment; i < N;i++){

Tmp = A[i];

for(j = i;j > Increment && Tmp < A[j - Increment];j++)

A[j] = A[j - Increment];

A[j] = Increment;

}

}

希尔排序的评价:

个人感觉,希尔排序是一个很复杂的排序方法。

希尔排序的效率和增量序列(Increment)的选取有关。

堆排序:

堆排序是稳定的排序。(具体后面总结的时候和其他几种排序比较之后再提)

首先我们说的堆当然默认是二叉堆(binary heaps)。

为什么要使用堆排序?

因为够快和够稳定。理论上,堆排序的时间复杂度是O(NlogN),比希尔排序快(虽然实际的运用中希尔排序要更好一些)。这个时间是怎么来的?我们可以建一个堆,然后再将堆顶元素删除,将得到的元素记录在另一个数组中(当然这样会增加时间和空间的开销,不过这些开销并没有太多关系)。最后当堆中的元素都记录到另一个数组之后,整个排序就完成了,我们得到了一个递增的序列。另外的一个策略是(如果你不想使用一个增加开销的数组),我们把delete出来的数放在数组的最后一个位置,不断重复就可以得到一个有序序列。虽然对于小顶堆来说,这个序列是递减的。如果我们想要得到一个递增的序列的话,只需要调整一下heap order(变成大顶堆)再deleteMax就可以得到递增的序列了。

算法实现:

#define LeftChild(i) (2*(i)+1)

void PercDown(ElementType A[],ElementType i,int N){

int Child;

ElementType Tmp;

for(Tmp = A[i];LeftChild(i) < N;i=Child){

}

}

void HeapSort(ElementType A[],int N){

/*build heap*/

int i,j

for(i=N/2;i>=0;i++)

percdown(A,i,N);

for(j=N-1;j>0;j--){

swap(&A[0],&A[j]);

perdown(A,0,i);

}

}

快速排序

快速排序正如他的名字所示的那样,理论上是最快。

算法实现:

快速排序很重要的一点是pivot的选取。这里采用的方法是三数中值法,去头尾和中间三个元素,然后取三个数的中值作为pivot的值。(下面对快排做具体分析的时候会解释pivot的选取和时间复杂度的问题)

#define Cutoff 3

/*Median3用来选取pivot 并且返回pivot*/

ElementType Median3(ElementType A[], int Left, int Right){

int Center;

Center = (Left + Right)/2;

if(A[Left] > A[Right])

Swap(&A[Left],&A[Right]);

if(A[Left] > A[Center])

Swap(&A[Left],&A[Center]);

if(A[Center] > A[Right])

Swap(&A[Center],&A[Right]);

Swap(&A[Center],&A[Right-1]);

return A[Right-1];

}

void QSort(ElementType A[], int Left, int Right){

//对于元素较少的子数组来说,不能用三数中值法来选取枢纽元素,不能用快速排序来实现,可选用插入排序。

if(Left + Cutoff <= Right){

ElementType pivot;

int i,j;

i = Left;

j = Right;

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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: