快速排序
1.排序规则
划分的规则:
第一次排序以第一个数据作为基数
从右向左找比基准值小的(相等)
从左向右找比基准值大的
向下划分:至少这个组里有两个数据,才有必要去划分;0个或者1个都没有必要划分
原始数据: 84 9 18 19 48 12 90 84 8 12
具体过程如下图:
2.代码实现
第一种方法:递归法
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//一次划分函数 核心函数 //返回基准值最终所在下标
int Partition(int* arr, int left, int right)
{
//先讲arr数组里的[left, right]的第一个值 作为基准值
int tmp = arr[left];
while (left < right)
{
while (left<right && arr[right] > tmp)//左右边界没有相遇且当前右边的值大于基准值tmp
right--;
if (left < right)//如果此时,左右边界没有相遇,那就只能证明右边right找到了一个小于等于基准值tmp的值
{
arr[left] = arr[right];
}
else
{
break;
}
while (left < right && arr[left] <= tmp)//左右边界没有相遇且当前左边的值小于等于基准值tmp
left++;
if (left < right)//如果此时,左右边界没有相遇,那就只能证明左边left找到了一个大于基准值tmp的值
{
arr[right] = arr[left];
}
else
{
break;
}
}
arr[left] = tmp;//此时 因为 left == right
return left;//return right ok
}
//快速排序
void Quick(int* arr, int left, int right)
{
if (left < right)//通过left <right 保证[left, right]这个范围内至少两个数据
{
int par = Partition(arr, left, right);
if (left < par - 1)//基准值左半部分 至少有两个值才有必要去递归
{
Quick(arr, left, par - 1);
}
if (par + 1 < right)//基准值右半部分 至少有两个值才有必要去递归
{
Quick(arr, par + 1, right);
}
}
}
//快速排序:时间复杂度O(nlogn) 空间复杂度O(1) 稳定性:不稳定
void QuickSort(int* arr, int len)
{
Quick(arr, 0, len - 1);
}
int main()
{
int arr[] = { 12,2,39,88,4,6,25,232,62,221 };
QuickSort(arr, sizeof(arr) / sizeof(arr[0]));
Show(arr, sizeof(arr) / sizeof(arr[0]));
return 0;
}
第二种方法:非递归法
头文件list_Stack.h的代码见博客https://blog.csdn.net/hi332516_1/article/details/123489105?spm=1001.2014.3001.5501
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"list_Stack.h"
//一次划分函数 核心函数 //返回基准值最终所在下标
int Partition(int* arr, int left, int right)
{
//先讲arr数组里的[left, right]的第一个值 作为基准值
int tmp = arr[left];
while (left < right)
{
while (left<right && arr[right] > tmp)//左右边界没有相遇且当前右边的值大于基准值tmp
right--;
if (left < right)//如果此时,左右边界没有相遇,那就只能证明右边right找到了一个小于等于基准值tmp的值
{
arr[left] = arr[right];
}
else
{
break;
}
while (left < right && arr[left] <= tmp)//左右边界没有相遇且当前左边的值小于等于基准值tmp
left++;
if (left < right)//如果此时,左右边界没有相遇,那就只能证明左边left找到了一个大于基准值tmp的值
{
arr[right] = arr[left];
}
else
{
break;
}
}
arr[left] = tmp;//此时 因为 left == right
return left;//return right ok
}
void Three_Mid(int *arr, int left, int mid, int right)
{
if (arr[mid] > arr[right])//保证中间值小于右边值
{
int tmp = arr[mid];
arr[mid] = arr[right];
arr[right]= tmp;
}
if (arr[left] < arr[mid])//保证中间值一定是三个数的最小值
{
int tmp = arr[mid];
arr[mid] = arr[left];
arr[left] = tmp;
}
//此时 不大不小的值要么在左边,要么在右边
if (arr[left] > arr[right])//保证中间值小于右边值
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
//快速排序的非递归写法
void Quick_Stack(int* arr, int left, int right)
{
LStack st;
Init_lstack(&st);
if (left < right)
{
int par = Partition(arr, left, right);
if (left < par - 1)
{
Push(&st, left);
Push(&st, par-1);
}
if (par+1 < right)
{
Push(&st, par+1);
Push(&st, right);
}
}
while (IsEmpty(&st))
{
int p;
int q;
Pop(&st, &q);
Pop(&st, &p);
int par = Partition(arr, p, q);
if (p < par - 1)
{
Push(&st, p);
Push(&st, par - 1);
}
if (par + 1 < q)
{
Push(&st, par + 1);
Push(&st, q);
}
}
}
void QuickSort(int* arr, int len)
{
if (len <= 1000)
{
return InsertSort(arr, len);
}
Quick_Stack(arr, 0, len - 1);
}
int main()
{
int arr[] = { 12,2,39,88,4,6,25,232,62,221 };
QuickSort(arr, sizeof(arr) / sizeof(arr[0]));
Show(arr, sizeof(arr) / sizeof(arr[0]));
return 0;
}
3.测试结果
4.复杂度分析
时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
对八大算法的总结:
稳定的排序: 直接插入排序、冒泡排序、基数(桶)排序、二路归并排序
不稳定的排序: 希尔排序、堆排序、选择排序、快速排序