1、算法基本思想
找一个哨兵数,让小的到左边,大的到右边(不断执行),直到区间只剩下0个或者1个元素,排序完成。
2、源代码
交换数据
/*
* 函数名称:Swap
*
* 函数功能:交换数据
*
* 入口参数:a, b
*
* 出口参数:void
*
* 返回类型:void
*/
void Swap(int * a, int * b)
{
int tmp = 0;
assert(NULL != a);
assert(NULL != b);
tmp = *a;
*a = *b;
*b = tmp;
return;
}
a.左右下标寻找法
设置最右边的数字是哨兵数,如果最左边的数字小于等于哨兵数,那么左边的指针向右走,注意:是左边的数先走。左边的指针走完后,右边的指针开始向左边走,(假如左边的数字大于哨兵数,那么左边的指针不动。小于等于哨兵数后,向右走)。右边的数如果大于等于哨兵数,那么就向左走,直到找到一个小于哨兵数的数字,然后交换此时右边指向的数字和左边的数字,此时的情况应该是指针指向的左边的数字大于哨兵数,指针指向的右边的数字小于哨兵数。意思就是要保证左右两边指向的数字指向相同(同时大于或者同时小于或者同时等于),不能出现异号的情况,一旦出现就交换数字。当最后区间内只剩下一个数字时,交换哨兵数和该位置的数字,排序完成。
/*
* 函数名称:CutPart
*
* 函数功能:快排之切分(左右下标寻找法)
*
* 入口参数:array, left, right
*
* 出口参数:begin
*
* 返回类型:int
*/
int CutPart(int array[], int left, int right)
{
int key = array[right];
int begin = left; // 都是下标
int end = right; // 为什么不是right - 1? 1 2 3 4 5出现问题 begin走到4的时候停下来,因为begin=end,交换4和5
//从两头往中间扫描
while (begin < end)
{
// 为什么不是小于? 5 5 5 5 5 时begin需要往右边走,最后走到begin=end时就停下来
while ((begin < end) && (array[begin] <= key))
{
begin++;
}
while ((begin < end) && (array[end] >= key))
{
end--;
}
//begin指向的左边的数字大于哨兵数,end指向的右边的数字小于哨兵数
if (begin < end)
{
Swap(array + begin, array + end);
}
}
// begin只会有两个位置停下来 可能1:array[begin] > key 可能2:begin就是right
// 错误1 Swap(array + begin, key); key是局部变量 错误2 Swap(array + begin, array + end); end会发生变化
// 最后交换哨兵数和 begin等于end时指向的数字即可
Swap(array + begin, array + right);
return begin; // 返回的begin就是之前设置的哨兵数的下标
}
b.挖坑法
设置最右边的数字是哨兵数,然后从左边开始,如果左边的数字大于等于哨兵数,那么就覆盖掉哨兵数(或者说填上哨兵的坑)。接着end向左走,如果此时end指向的数字小于哨兵数,继续覆盖掉begin指向数字,直到begin和end指向同一个数字的时候,之前保存的哨兵数覆盖掉这个位置,排序完成。
/*
* 函数名称:CutPart
*
* 函数功能:快排之切分(挖坑法)
*
* 入口参数:array, left, right
*
* 出口参数:begin
*
* 返回类型:int
*/
int CutPart(int array[], int left, int right)
{
int key = array[right];
int begin = left;
int end = right;
while (begin < end)
{
while (begin < end && array[begin] <= key)
{
begin++;
}
array[end] = array[begin];
while (begin < end && array[end] >= key)
{
end--;
}
array[begin] = array[end];
}
array[begin] = key;
return begin;
}
c.前后下标寻找法
设置最右边的数字是哨兵数,一开始cur和firstBigger(第一个比哨兵数大的数,这意味着它前面的数都比哨兵数小,cur和firstBigger之间的数字都比哨兵数大)都指向左边,cur指向的数字如果比哨兵数大,那么cur向右走,当cur指向的数字小于哨兵数时,这时候交换cur指向的数字和firstBigger指向的数字,交换后,firstBigger向右走,cur继续向右走,重复上述过程,最后当cur走到哨兵数时,交换cur指向的数字(哨兵数)和firstBigger指向的数字。
/*
* 函数名称:CutPart
*
* 函数功能:快排之切分(前后下标法)
*
* 入口参数:array, left, right
*
* 出口参数:begin
*
* 返回类型:int
*/
int CutPart(int array[], int left, int right)
{
int key = array[right];
int cur = left;
int firstBigger = left;
while (cur < right)
{
if (array[cur] < key)
{
Swap(array + firstBigger, array + cur);
firstBigger++;
}
else
{
;
}
cur++;
}
Swap(array + firstBigger, array + right);
return firstBigger;
}
优化哨兵数
三选一法:按照左中右下标找到对应的数字,然后比较大小找到大小在中间的数,这时候把这个数字和最右边的数字交换即可。
/*
* 函数名称:GetMiddle
*
* 函数功能:求中间大小的数
*
* 入口参数:array, left, right
*
* 出口参数:left or mid or right
*
* 返回类型:int
*/
int GetMiddle(int array[], int left, int right)
{
int mid = (left & right) + ((left ^ right) >> 1);
if (array[left] < array[mid])
{
if (array[mid] < array[right])
{
// left < mid < right
return mid;
}
else if (array[left] > array[right])
{
// right < left < mid
return left;
}
else
{
// left < right < mid;
return right;
}
}
else
{
if (array[mid] < array[right])
{
// mid < right < left
return right;
}
else if (array[left] < array[right])
{
// mid < left < right
return left;
}
else
{
// right < mid < left
return mid;
}
}
}
快排之递归
/*
* 函数名称:QuickSortRecursion
*
* 函数功能:分组(递归)
*
* 入口参数:array, left, right
*
* 出口参数:void
*
* 返回类型:void
*/
void QuickSortRecursion(int array[], int left, int right)
{
int div = 0;
int pivot = GetMiddle(array, left, right);
Swap(array + pivot, array + right);
//left == right区间[left, right]剩下1个,left > right 区间剩下0个(1 2 3 4 5)
if (left >= right)
{
return;
}
else
{
div = CutPart(array, left, right);
QuickSortRecursion(array, left, div - 1);
QuickSortRecursion(array, div + 1, right);
}
return;
}
/*
* 函数名称:QuickSort
*
* 函数功能:快速排序
*
* 入口参数:array, size
*
* 出口参数:void
*
* 返回类型:void
*/
void QuickSort(int array[], int size)
{
//0代表最小下标值,size - 1代表最大下标值
QuickSortRecursion(array, 0, size - 1);
return;
}
快排之迭代
/*
* 函数名称:QuickSortLoop
*
* 函数功能:分组(迭代) 使用栈来实现
*
* 入口参数:array, size
*
* 出口参数:void
*
* 返回类型:void
*/
void QuickSortLoop(int array[], int size)
{
int stack[100];
int top = 0;
int left = 0;
int right = 0;
int div = 0;
// 先入左边界,再入右边界
stack[top++] = 0;
stack[top++] = size - 1;
while (top > 0)
{
// 先取右,再取左
right = stack[--top];
left = stack[--top];
if (left >= right)
{
continue;
}
else
{
;
}
div = CutPart(array, left, right);
// 先入左边区间
stack[top++] = left;
stack[top++] = div - 1;
// 再入右边区间
stack[top++] = div + 1;
stack[top++] = right;
}
return;
}
main
#define _CRT_SECURE_NO_WARNINGS 1
/*
* Copyright (c) 2018, code farmer from sust
* All rights reserved.
*
* 文件名称:QuickSort.c
* 功能:快速排序
*
* 当前版本:V1.0
* 作者:sustzc
* 完成日期:2018年7月10日16:32:46
*/
# include <stdio.h>
# include <assert.h>
/*
* 函数名称:main
*
* 函数功能:测试主程序
*
* 入口参数:void
*
* 出口参数:0
*
* 返回类型:int
*/
int main(void)
{
int i = 0;
int numbers[] = {3, 1, 6, 4, 5, 2};
int len = sizeof(numbers) / sizeof(int);
printf("排序之前:\n");
for (i = 0; i < len; i++)
{
printf("%d ", numbers[i]);
}
QuickSort(numbers, len);
//QuickSortLoop(numbers, len);
printf("\n排序之后:\n");
for (i = 0; i < len; i++)
{
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
3、输出结果