
༺ 个人主页 · 纪念229 ༻
༒专栏目录:《算法》༒
༒专栏目录:《MySQL数据库》༒
༒专栏目录:《前端开发》༒
༺世上本没有路,走的人多了自然就有了༻
本文承接上文,继续讲述快速排序找基准值的方法,另外会补充其它排序方法
文章目录
1.找基准值(lomuto前后指针法)
给个prev,cur刚开始prev为left(为下标0基准值也是)
通过遍历如果cur上的值小于基准值那么++prev然后将下标上的值prev与下标cur的值交换(前提++prev后的值不能与cur相等因为相等的话就是自己换自己)否则直接cur++接着比较后面下标上的值与基准值比较直到不符合cur <= right循环结束将下标keyi上的值与下标prev上的值交换最后返回下标prev(原因上篇文章有这里不赘述了)
然后利用递归实现排序
代码展示
c
//lomuto前后指针法
int _QuickSort(int* arr, int left, int right)
{
int keyi = left;
int prev = left, cur = prev + 1;
while (cur <= right)
{
//判断基准值与arr[cur]的大小如果比基准值小就将prev++并且将prev里的值与cur里面的值交换
//为啥++prev != cur,因为这样会自己交换自己
//这样写就可以保证prev的值都是小于基准值的值
if (arr[cur] < arr[keyi] && ++prev != cur)
//为啥cur不遍历下标0因为基准值不要和自己比较
{
Swap(&arr[prev], &arr[cur]);
}
cur++;
}
//遍历完后将基准值与prve里的值交换此时基准值在下标prev上即返回prev的下标即可
Swap(&arr[keyi], &arr[prev]);
return prev;
}
这里注释已经较为详尽就不加以赘述了
2.快速排序(非递归法)------栈
这里的快速排序利用栈来实现非递归
这里栈的代码我都会展示但是就不讲解了想了解栈的构成以及实现栈的一些功能请看我前面对栈文章的讲述
代码展示
Stack.h
c
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top; //指向栈顶的位置
int capacity;//栈的容量
}ST;
//初始化
void StackInit(ST* ps);
//销毁
void StackDestroy(ST* ps);
//入栈--栈顶
void StackPush(ST* ps, STDataType x);
//出栈--栈顶
void StackPop(ST* ps);
//取栈顶元素
STDataType StackTop(ST* ps);
//获取栈中有效元素个数
int StackSize(ST* ps);
//栈是否为空
bool StackEmpty(ST* ps);//定义的函数一般都要声明
Stack.c
c
#include"Stack.h"
void StackInit(ST* ps)
{
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//销毁
void StackDestroy(ST* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
//最后要将ps->arr为NULL
ps->capacity = ps->top = 0;
}
//入栈--栈顶
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
//ps->top == ps->capacity
//有两种情况
//刚开始top和capacity都为0
//第二种top的数等于capacity(capacity不为0)
int newCapacity = ps->top == 0 ? 4 : ps->capacity * 2;//这里用2 * ps->capacity不用newCapacity * 2
//因为是要扩充内容的所以用realloc
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
//perror与exit的头文件是#include<stdio.h>
perror("realloc fail");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
//这里的top是ps->top不是top,top只存在栈中
ps->arr[ps->top++] = x;//这里是先将下标中的数据放入数组中top再加加说明top指的是栈元素的个数即求栈顶元素时top要减一
}
//判断栈为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈--栈顶
void StackPop(ST* ps)
{
//判断栈是否为空
assert(!StackEmpty(ps));
--ps->top;
}
//取栈顶元素
STDataType StackTop(ST* ps)//没写函数名(StcakEmpty)编译器乱报 ST不是类型名
{
//判断栈是否为空
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];//top为数据个数数组里指的是下标所以要减一
}
//获取栈中有效元素个数
int StackSize(ST* ps)
{
return ps->top;
}
c
void QuickSortNoR(int* arr, int left, int right);//不递归
c
//非递归版本的快速排序------栈
void QuickSortNoR(int* arr, int left, int right)
{
ST st;//类型不用取栈指针取的话为初始化不能用只能创造空间后才能用
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
//刚开始先给个栈变量再初始化再给左右两下标入栈
while (!StackEmpty(&st))
//用while循环条件是栈不为空
//利用while循环找基准值给数组排序
{
//排序与lomuto法类似
//取栈顶两次(得栈顶元素要出栈) (目的是取得找基准值的范围)
//因为是right最后入栈即先取end
int end = StackTop(&st);
StackPop(&st);
int begin = StackTop(&st);
StackPop(&st);
//[begin,end]找基准值
//把keyi、prev、cur位置找好
int keyi = begin;
int prev = begin, cur = begin + 1;
while (cur <= end)//遍历条件为cur <= end 遍历完结束
{
if (arr[cur] < arr[keyi] && ++prev != cur)
{
Swap(&arr[prev], &arr[cur]);
}
cur++;
//比完后cur要加加
}
Swap(&arr[keyi], &arr[prev]);
keyi = prev;//之后将prev赋值给keyi
//之后根据范围继续入栈找基准值给数组排序
//begin keyi cur
//左序列:[begin,keyi - 1] 右序列:[keyi + 1, end]
//先判断右序列再判断左序列
if (keyi + 1 < end)
{
StackPush(&st, keyi + 1);
StackPush(&st, end);
}
if (begin < keyi - 1)
{
StackPush(&st, begin);
StackPush(&st, keyi - 1);
}
}
//结束后销毁栈
StackDestroy(&st);
}
具体讲解
c
ST st;//类型不用取栈指针取的话为初始化不能用只能创造空间后才能用
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
//刚开始先给个栈变量再初始化再给左右两下标入栈
刚开始给个栈类型的变量(如果用栈指针类型的变量要创造空间之后才能用)
然后给栈初始化将左右两下标依次入栈
c
while (!StackEmpty(&st))
//用while循环条件是栈不为空
//利用while循环找基准值给数组排序
{
//排序与lomuto法类似
//取栈顶两次(得栈顶元素要出栈) (目的是取得找基准值的范围)
//因为是right最后入栈即先取end
int end = StackTop(&st);
StackPop(&st);
int begin = StackTop(&st);
StackPop(&st);
//[begin,end]找基准值
//把keyi、prev、cur位置找好
int keyi = begin;
int prev = begin, cur = begin + 1;
while (cur <= end)//遍历条件为cur <= end 遍历完结束
{
if (arr[cur] < arr[keyi] && ++prev != cur)
{
Swap(&arr[prev], &arr[cur]);
}
cur++;
//比完后cur要加加
}
Swap(&arr[keyi], &arr[prev]);
keyi = prev;//之后将prev赋值给keyi
//之后根据范围继续入栈找基准值给数组排序
//begin keyi cur
//左序列:[begin,keyi - 1] 右序列:[keyi + 1, end]
//先判断右序列再判断左序列
if (keyi + 1 < end)
{
StackPush(&st, keyi + 1);
StackPush(&st, end);
}
if (begin < keyi - 1)
{
StackPush(&st, begin);
StackPush(&st, keyi - 1);
}
}
c
while (!StackEmpty(&st))
{
.....
}
里面的找基准值方法用的就是Lomuto前后指针法(找基准值我推荐用这个方法)
这里我就讲一些与栈有关的东西,和一些我认为重要的地方
c
int end = StackTop(&st);
StackPop(&st);
int begin = StackTop(&st);
StackPop(&st);
获得栈顶元素元素在取栈顶元素两次得到序列范围然后根据lomuto找基准值
这里为什么要先取end在取begin因为栈是后进先出(入栈是先入left再入right)所以用end(end配right)
keyi = prev;
将下标prev值赋值到下标keyi中原因是基准值最后在下标prev中
c
if (keyi + 1 < end)
{
StackPush(&st, keyi + 1);
StackPush(&st, end);
}
if (begin < keyi - 1)
{
StackPush(&st, begin);
StackPush(&st, keyi - 1);
}
然后根据基准值判断左右两边的序列是否范围大于一
keyi + 1 < end
要是keyi + 1 < end说明右序列数据数目大于一
begin < keyi - 1
要是begin < keyi - 1说明左序列数据数目大于一
将序列中的左右两个下标继续入栈然后根据循环取栈顶元素出栈直到栈元素为空为止(注意这里只要是入栈就会入两个下标)
栈为空说明找不到基准值了及排序完成
c
StackDestroy(&st);
因为入栈时栈中的arr需要创建空间甚至扩大及要用realloc
所以最后我们要释放空间
一般来说只要是创造了空间我们最后就要free
文章到这就告一段落,希望对你有所帮助,感谢观看!