【数据结构---29】简单排序方法(下)
对快速排序的优化:
快排需要设置基准值,基准值的选取会出现两种极端情况,导致排序算法的性能变差
①针对基准值的选取做出优化
②针对递归的深度做出优化
方法一:三值取中法
<1>顾名思义就是取出三个基准值,选择值为中间的那一个
<2>三个基准值分为是最左边,最右边以及中间,中间位置为left+((right-left)>>1);
<3>之前的代码基于基准值key的设定为right-1,为了不做较大改动,将mid位置的元素和right-1位置元素互换
方法一代码实现:
int GetMiddleIndex(int* array, int left, int right)
{
int mid = left + ((right - left) >> 1);
if (array[left] < array[right - 1])
{
if (array[mid] < array[left])
{
return left;
}
else if (array[mid]>array[right - 1])
{
return right - 1;
}
else
{
return mid;
}
}
else
{
if (array[mid] > array[left])
{
return left;
}
else if (array[mid] < array[right - 1])
{
return right - 1;
}
else
{
return mid;
}
}
}
方法二:调整递归深度
<1>不需要递归到只剩下一个元素才返回
<2>设置为元素个数小于16的时候,使用适合数据小,接近有序的直接插入排序
方法二代码实现:
if (right - left > 16)
{
InsertSort(array,right);
}
else
{
int div = partion3(array, left, right);
QuickSort(array, left, div);
QuickSort(array, div + 1, right);
}
快速排序的循环写法:
<1>递归的函数调用和数据结构的栈很类似,所以使用栈来辅助完成递归转变为循环
<2>首先将右边界压入栈中,在将左边界压入,这样左就在上,右就在下
<3>获取栈顶元素的顺序就是先left在right,如果栈中元素不唯一,就继续分组
<4>将新的区间压入栈中,顺序和<2>一致
代码实现:
void QucikSortNor(int* array, int size)
{
int left = 0;
int right = size;
Stack s;
StackInit(&s);
//栈的特性是先入后出,所以反过来压入元素,出栈的顺序就是对的
StackPush(&s, right);
StackPush(&s, left);
//栈若不空,就对左右部分继续分别分组
while (StackEmpty(&s) != -1)
{
left = StackTop(&s);
StackPop(&s);
right = StackTop(&s);
StackPop(&s);
if (right - left > 1)
{
int div = partion3(array, left, right);
StackPush(&s, right);
StackPush(&s, div + 1);
StackPush(&s, div);
StackPush(&s, left);
}
}
}
归并排序:
递归写法先分组,在排序,有序之后再归并,然后写入临时空间,最后内存拷贝回去
<1>类似于二叉树的后序遍历
<2>先对左侧进行递归排序,在对右边进行递归排序,然后归并
<3>memcpy拷贝的时候注意参数array和tmp都要加上left,否则只能拷到左边
<4>归并的操作就是左边分组和右边分组每个元素分别比大小,小的先放入临时空间
<5>长度不一的话单独处理,把剩下的元素直接插到后面
递归框架:
void _MergeSort(int* array, int left, int right,int* tmp)
{
if (right - left > 1)
{
int mid = left + ((right - left) >> 1);
_MergeSort(array, left, mid,tmp);
_MergeSort(array, mid, right,tmp);
MergeData(array, left, mid, right,tmp);
memcpy(array+left, tmp+left, sizeof(int)*(right - left));
}
}
为了方便调用,所以对其封装:
void MergeSort(int* array, int size)
{
int* tmp = (int*)malloc(sizeof(int)*size);
if (tmp == NULL)
{
assert(0);
}
_MergeSort(array, 0, size, tmp);
free(tmp);
tmp = NULL;
}
循环写法:
<1>循环的跳出条件是gap<size,就是一组可以表示所有数据
<2>right和mid的取值可能越界,需要单独处理
<3>不需要分组,因为开始默认为单独存在,只需要把有序的元素整合为一组
void MergeSortNor(int* array, int size)
{
int* tmp = (int*)malloc(sizeof(int)*size);
if (tmp == NULL)
{
assert(0);
}
int gap = 1;
while (gap < size)
{
for (int i = 0; i < size; i += 2 * gap)
{
int left = i;
int mid = left + gap;
int right = mid + gap;
if (right >= size)
{
right = size;
}
if (mid >= size)
{
mid = size;
}
MergeData(array, left, mid, right, tmp);
}
memcpy(array, tmp, sizeof(int)*size);
gap *= 2;
}
free(tmp);
tmp = NULL;
}