个人主页:日刷百题
系列专栏
:〖C/C++小游戏〗〖Linux〗〖数据结构〗 〖C语言〗
🌎欢迎各位
→点赞
👍+收藏
⭐️+留言
📝
前言:
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。
基本思想:
递归实现方式常见有三种,区别于单趟思想,性能差别不大,下面我们看下快排递归实现。
一、快速排序的递归实现
1.1 Hoare排序
1.1.1 单趟目的
1.1.2 动图解析
单趟思路:
该排序有一个需要注意的点是:必须左边先走找小
因为左边先走,必定相遇时位置对应的值小于keyi位置值,保证最后这俩个位置交换,相遇位置即是keyi位置对应值最终位置。
解析:
1.1.3 代码实现
解析:
该代码将单趟写在子函数中,这样使得整个代码层次更加清晰,也便于理解。可以发现我们对单趟中keyi做了优化,因为keyi的位置,是影响快速排序效率的重大因素。因此我们采用了三数取中的方法解决选keyi不合适的问题。即知道这组无序数列的首和尾后,我们只需要在首,中,尾这三个数据中,选择一个排在中间的数据作为基准值(keyi),进行快速排序,即可进一步提高快速排序的效率。
后面2种单趟也做这样的优化,后面就不过多介绍。
//Hoare快排
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
{
return end;
}
else
{
if (a[begin] > a[mid])
{
return mid;
}
else
{
return begin;
}
}
}
else//(a[begin]<= a[end])
{
if (a[begin] > a[mid])
{
return begin;
}
else
{
if (a[end] > a[mid])
{
return mid;
}
else
{
return end;
}
}
}
}
void swap(int* x, int* y)
{
int z = *x;
*x = *y;
*y = z;
}
int _QuickSort_Hoare(int* a, int begin, int end)
{
int mid = GetMid(a,begin, end);
swap(&a[begin],
int keyi = begin;
int left = begin;
int right = end;
while (left < right)
{
//右边找小
while (left < right && a[right] >= a[keyi])
{
right--;
}
//左边找大
while (left < right && a[left] <= a[keyi])
{
left++;
}
swap(&a[left],
}
swap(&a[keyi],
return left;
}
void QuickSort_Hoare(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi= _QuickSort_Hoare(a, begin, end);//单趟
//递归 [begin,keyi-1] keyi,[keyi+1,end]
QuickSort_Hoare(a, begin, keyi - 1);
QuickSort_Hoare(a, keyi+1, end);
}
1.2 挖坑法
1.2.1 单趟目的
1.2.2 动图解析
单趟思路:
1.2.3 代码实现
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
{
return end;
}
else
{
if (a[begin] > a[mid])
{
return mid;
}
else
{
return begin;
}
}
}
else//(a[begin]<= a[end])
{
if (a[begin] > a[mid])
{
return begin;
}
else
{
if (a[end] > a[mid])
{
return mid;
}
else
{
return end;
}
}
}
}
void swap(int* x, int* y)
{
int z = *x;
*x = *y;
*y = z;
}
int _QuickSort_Pit(int* a, int begin, int end)
{
int mid = GetMid(a, begin, end);
swap(&a[begin],
int pit = begin;
int key = a[begin];
int left = begin;
int right = end;
while (left < right)
{
while (left < right && a[right] >= key)
{
right--;
}
a[pit] = a[right];
pit = right;
while(left < right&& a[left] <= key)
{
left++;
}
a[pit] = a[left];
pit = left;
}
a[left] = key;
return left;
}
void QuickSort_Pit(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi = _QuickSort_Pit(a, begin, end);
//[begin,keyi-1],keyi,[keyi+1,end]
QuickSort_Pit(a, begin, keyi - 1);
QuickSort_Pit(a, keyi + 1, end);
}
1.3 双指针法
1.3.1 单趟目的
1.3.2 动图解析
单趟思路:
1.3.3 代码实现
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
{
return end;
}
else
{
if (a[begin] > a[mid])
{
return mid;
}
else
{
return begin;
}
}
}
else//(a[begin]<= a[end])
{
if (a[begin] > a[mid])
{
return begin;
}
else
{
if (a[end] > a[mid])
{
return mid;
}
else
{
return end;
}
}
}
}
void swap(int* x, int* y)
{
int z = *x;
*x = *y;
*y = z;
}
int _QuickSort_Pointer(int* a, int begin, int end)
{
int mid = GetMid(a, begin, end);
swap(&a[begin],
int key = begin;
int prev= begin;
int cur = prev + 1;
while (cur <= end)
{
if (a[cur] > a[key])
{
cur++;
}
else
{
prev++;
swap(&a[prev],
cur++;
}
}
swap(&a[key],
return prev;
}
void QuickSort_Pointer(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi = _QuickSort_Pointer(a, begin, end);
//[begin,keyi-1],keyi,[keyi+1,end]
QuickSort_Pointer(a, begin, keyi - 1);
QuickSort_Pointer(a, keyi + 1, end);
}
二、快速排序的优化
2.1 三数取中法选key
这个方法提升效率比较显著,上面已经排序均用该方法优化。
2.2 递归到小的子区间,使用插入排序
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
{
return end;
}
else
{
if (a[begin] > a[mid])
{
return mid;
}
else
{
return begin;
}
}
}
else//(a[begin]<= a[end])
{
if (a[begin] > a[mid])
{
return begin;
}
else
{
if (a[end] > a[mid])
{
return mid;
}
else
{
return end;
}
}
}
}
void swap(int* x, int* y)
{
int z = *x;
*x = *y;
*y = z;
}
int _QuickSort_Pointer(int* a, int begin, int end)
{
int mid = GetMid(a, begin, end);
swap(&a[begin],
int key = begin;
int prev= begin;
int cur = prev + 1;
while (cur <= end)
{
if (a[cur] > a[key])
{
cur++;
}
else
{
prev++;
swap(&a[prev],
cur++;
}
}
swap(&a[key],
return prev;
}
void QuickSort_Pointer(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
if(end-begin+1>10)
{
int keyi = _QuickSort_Pointer(a, begin, end);
//[begin,keyi-1],keyi,[keyi+1,end]
QuickSort_Pointer(a, begin, keyi - 1);
QuickSort_Pointer(a, keyi + 1, end);
}
else
{
InsertSort(a + begin, end - begin + 1);
}
}
三、快速排序的非递归实现
递归改为非递归,一般2种方法:
递归使用的空间是栈空间,所以容易出现栈溢出的情况,我们将快速排序改为非递归版本,这样空间的开辟就在堆上了,这样也就解决了这个问题。
快速排序的非递归与递归思想相同,非递归使用栈来模拟递归的实现,思路如下:
代码实现:
int GetMid(int* a, int begin, int end)
{
int mid = (begin + end) / 2;
if (a[begin] > a[end])
{
if (a[end] > a[mid])
{
return end;
}
else
{
if (a[begin] > a[mid])
{
return mid;
}
else
{
return begin;
}
}
}
else//(a[begin]<= a[end])
{
if (a[begin] > a[mid])
{
return begin;
}
else
{
if (a[end] > a[mid])
{
return mid;
}
else
{
return end;
}
}
}
}
void swap(int* x, int* y)
{
int z = *x;
*x = *y;
*y = z;
}
int _QuickSort_Pointer(int* a, int begin, int end)
{
int mid = GetMid(a, begin, end);
swap(&a[begin],
int key = begin;
int prev= begin;
int cur = prev + 1;
while (cur <= end)
{
if (a[cur] > a[key])
{
cur++;
}
else
{
prev++;
swap(&a[prev],
cur++;
}
}
swap(&a[key],
return prev;
}
typedef int DateType;
typedef struct Stack
{
DateType* a;
int top;
int capacity;
}Stack;
//初始化和销毁栈
void InitStack(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void DestoryStack(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
//出栈和入栈
void StackPush(Stack* ps, DateType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
DateType* tmp = (DateType*)realloc(ps->a, sizeof(DateType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail:");
return;
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
//栈的有效个数和栈顶元素
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
DateType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
//判空
bool IsEmptyStack(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
void QuickSort_Non_r(int* a, int begin, int end)
{
Stack tmp;
InitStack(
StackPush(
StackPush(&tmp, begin);
while (!IsEmptyStack(&tmp))
{
int left = StackTop(
StackPop(
int right = StackTop(
StackPop(
int keyi = _QuickSort_Pointer(a, left, right);
if (keyi+1 <right)
{
StackPush(
StackPush(&tmp,keyi+1);
}
if (left < keyi - 1)
{
StackPush(&tmp, keyi-1);
StackPush(
}
}
DestoryStack(
}
总结:本篇文章总结了快速排序的递归及非递归俩大种方式。
希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,百题一定会认真阅读!