快速排序
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将 待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
我以为,此函数需要主体函数和分隔函数。
1.主体函数
这个函数要做的事是,先得到一个数组,然后通过分隔函数得到分隔下标,将数组分为两份,然后继续分隔左半部分和右半部分,直到数组剩下一个数,此时数组已经有序。
void QuickSort(int* array,int left,int right)
{
int div;
if(left<right)
{
div = partion_3(array,left,right); //进入分隔函数,返回分隔下标
QuickSort(array,left,div);
QuickSort(array,div+1,right);
}
}
上面的是递归版本的,还可以利用栈将递归改为循环:
void QuickSort(int* array,int left,int right)
{
int div;
int _left,_right;
DATA_TYPE region;
Stack ps; //创建栈
StackInit(&ps); //初始化栈
div = partion_3(array,left,right); //得到第一次分隔的下标
region.left = div+1;
region.right = right;
StackPush(&ps,region); //将分隔之后的右边数组首坐标和末坐标压入栈
region.left = left;
region.right = div;
StackPush(&ps,region); //将分隔之后的左边数组首坐标和末坐标压入栈
while(!IsNull(&ps))
{
_left = StackTop(&ps).left;
_right = StackTop(&ps).right;
div = partion_3(array,StackTop(&ps).left,StackTop(&ps).right);
//栈顶是左边的数组,先分隔左边的,记录分隔下标
StackPop(&ps); //将之前已经分隔过的左边下标出栈
if(div+2<_right) //如果新分隔后右边的数组大于2,就继续压入
{
region.left = div+1;
region.right = _right;
StackPush(&ps,region);
}
if(_left+1<div) //如果新分隔后左边的数组大于2,就继续压入
{
region.left = _left;
region.right = div;
StackPush(&ps,region);
}
}
}
2.分隔函数
分隔函数就是将传入的数组,以数组中某个数作为基准值,将小于此基准值的放在基准值左边,大于基准值的放在基准值右边。这样数组就被分为两份,然后这个函数返回基准值的下标位置。分割函数有三种实现方法:
Ⅰ、交换法
这个方法就是将数组中最后一个数作为基准值,然后将数组的0下标处标记为begin,将数组中倒数第二个数的下标位置标记为end,从begin开始往后找,找如果找到一个大于基准值的数,就停下。从end开始往前找,找如果找到一个小于等于基准值的数,就停下。然后将两个数位置进交换,begin往后走,end往前走。重复上述操作,直到end和begin相等,说明已经交换完成。最后将end位置处的数和基准值比较,如果大于基准值,就和基准值交换,如果小于等于基准值,就将end+1处的值和基准值交换。
代码实现如下:
int partion_3(int* array,int left,int right) //交换法
{
int div;
int end = right-2;
int begin = left;
div = array[right-1];
while(begin < end)
{
while(array[begin] <= div && begin < end)
begin++;
while(array[end] > div && begin < end)
end--;
if(begin < end)
Swap(&array[end],&array[begin]);
}
if(array[end] >= div)
{
Swap(&array[end],&array[right-1]);
return end;
}
else
{
Swap(&array[end+1],&array[right-1]);
return end+1;
}
}
Ⅱ、挖坑法
将数组中最后一个数最为基准值,然后保存起来,设置begin=0下标,end是倒数第一个数的下标,然后从左边开始找比基准值大的数,如果找到就把它直接放在end的位置,因为end的数已经保存起来。如果begin处的数放在了end处那么,begin这个位置就是一个坑,因为它已经保存到end处了。然后end往前找,找比基准值小的数,如果找到直接放到begin的那个坑的地方,end又成为了新坑,就这样重复操作,直到end和begin相等,把最开始的基准值赋值给end。
代码实现如下:
int partion_2(int* array,int left,int right) //挖坑法
{
int div = array[right-1];
int end = right-1;
int begin = left;
while(begin < end)
{
while(array[begin] <= div && begin < end)
begin++;
if(begin >= end)
break;
array[end--] = array[begin];
while(array[end] > div && begin < end )
end--;
if(begin >= end)
break;
array[begin++] = array[end];
}
array[begin] = div;
return begin;
}
Ⅲ、前后指针法
设置基准值为数组最后一个数,然后定义一个当前下标cur,和cur的前一个下标pre。然后让cur位置的数去判断是否大于基准值,如果大于cur就往后走,pre不动,如果小于等于,再判断pre+1位置处的数是否大于基准值,如果大于,就让pre+1位置的数和cur位置的数交换,pre往后走一下,cur往后走一下,如果不大于,pre往后走一下,cur往后走一下,不交换。就只让重复操作,直到cur走到最后一个数的位置,最后将pre+1位置的数和基准值交换一下。
接上图
代码实现如下:
int partion_1(int* array,int left,int right) //前后指针法
{
int div = right-1;
int pre = left-1;
int cur = left;
while(cur<right-1)
{
if(array[cur]>array[div])
cur++;
else
{
if(array[++pre]>array[div])
Swap(&array[++pre],&array[cur++]);
else
{
pre++;
cur++;
}
}
}
Swap(&array[++pre],&array[div]);
return pre;
}