快速排序基本思想:通过一趟排序 将待排序序列 分割成独立的两个部分,其中一部分记录的关键字均比另一部分关键字小(分割位置的Key 已达到它应该在的位置 例如:升序 Key左边的都比它小,Key右边的都比它大 ,故Key即是最终排序时所在的位置,不需要在更改) 继续对这两个部分进行继续分割排序,最终达到整个序列有序的目的。
这里列举了 Hoare大佬、挖坑法、前后指针法、非递归、改进的快速排序结合了直接插入排序 五个方面进行 阐述
Hoare创世人大佬的方法:
void Swanp(int* x, int* y)
{
int a = *x;
*x = *y;
*y = a;
}
//Hoare版本
void QuickSortMethod1(int* head, int left, int right)
{
if (left >= right)
return;
//确定阈值
//三数取中 下面三个if 使右边的最大 中间的最小 左边的中间值即为阈值
int Keyi = (left + right) / 2;
if (head[left] > head[right])
Swanp(&head[left], &head[right]);
if (head[Keyi] > head[right])
Swanp(&head[Keyi], &head[right]);
if (head[left] < head[Keyi])
Swanp(&head[Keyi], &head[left]);
int fail = right;
int begin = left;
while (begin < fail)
{
while (fail > begin && head[fail] >= head[left]) //右边找小
fail--;
while (fail > begin && head[begin] <= head[left]) //左边找大
begin++;
if(fail!=begin)
Swanp(&head[fail], &head[begin]);
}
if(fail!=left)
Swanp(&head[fail], &head[left]);
if (fail - 1 > left)
QuickSortMethod1(head, left, fail - 1);
if (right> fail+1)
QuickSortMethod1(head, fail+1, right);
}
三数取中确定Key后面在叙述
这个算法的思想就是:
总体思想如上 不多赘述了。
挖坑法:
//挖坑法
void QuickSortMethod2(int* head, int left, int right)
{
if (left >= right)
return;
//确定阈值
//三数取中 下面三个if 使右边的最大 中间的最小 左边的中间值即为阈值
int Keyi = (left + right) / 2;
if (head[left] > head[right])
Swanp(&head[left], &head[right]);
if (head[Keyi] > head[right])
Swanp(&head[Keyi], &head[right]);
if (head[left] < head[Keyi])
Swanp(&head[Keyi], &head[left]);
int fail = right;
int begin = left;
int Key = head[left]; //存一下 关键字
while (begin < fail)
{
while (fail > begin && head[fail] >= Key) //右边找小
fail--;
if(begin!=fail)
head[begin] = head[fail];
while (fail > begin && head[begin] <= Key) //左边找大
begin++;
if (begin != fail)
head[fail] = head[begin];
}
head[fail] = Key;
if (fail - 1 > left)
QuickSortMethod2(head, left, fail - 1);
if (right > fail + 1)
QuickSortMethod2(head, fail + 1, right);
}
前后指针法:
//前后指针法
void QuickSortMethod3(int* head, int left, int right)
{
if (left >= right)
return;
//确定阈值
//三数取中 下面三个if 使右边的最大 中间的最小 左边的中间值即为阈值
int Keyi = (left + right) / 2;
if (head[left] > head[right])
Swanp(&head[left], &head[right]);
if (head[Keyi] > head[right])
Swanp(&head[Keyi], &head[right]);
if (head[left] < head[Keyi])
Swanp(&head[Keyi], &head[left]);
int slow = left;
int fast = left+1;
int Key = head[left]; //存一下 关键字
while (fast <= right)
{
if (head[fast] <= Key)
{
if (slow + 1 != fast) //说明中间有大于关键词的值了 要进行交换
Swanp(&head[slow+1], &head[fast]);
slow++;
}
fast++;
}
Swanp(&head[left], &head[slow]);
if (slow - 1 > left)
QuickSortMethod3(head, left, slow - 1);
if (right > slow + 1)
QuickSortMethod3(head, slow + 1, right);
}
非递归快速排序:
一般 递归改非递归
方法一 :直接改循环 (例如费波纳界数列(Fibonacci sequence))
方法二 : 使用栈辅助改循环
这里采用栈来操作比较简单
1、栈里面Pop得左右区间,单趟排序
2、单趟分割后,自区间入栈
3、子区间只有一个值或不存在时就不入栈
//非递归 快速排序
void QuickSortMethod4(int* head, int left, int right)
{
assert(head);
if (left >= right)
return;
Stack stack;
StackInit(&stack);
StackPush(&stack, right);
StackPush(&stack, left);
while (!StackEmpty(&stack))
{
int begin = StackTop(&stack);
StackPop(&stack);
int end= StackTop(&stack);
StackPop(&stack);
//确定阈值
//三数取中 下面三个if 使右边的最大 中间的最小 左边的中间值即为阈值
int Keyi = (begin + end) / 2;
if (head[begin] > head[end])
Swanp(&head[begin], &head[end]);
if (head[Keyi] > head[end])
Swanp(&head[Keyi], &head[end]);
if (head[begin] < head[Keyi])
Swanp(&head[Keyi], &head[begin]);
int slow = begin;
int fast = begin + 1;
int Key = head[begin]; //存一下 关键字
while (fast <= end)
{
if (head[fast] <= Key)
{
if (slow + 1 != fast) //说明中间有大于关键词的值了 要进行交换
Swanp(&head[slow+1], &head[fast]);
slow++;
}
fast++;
}
Swanp(&head[begin], &head[slow]);
if (slow - 1 > begin)
{
StackPush(&stack, slow - 1);
StackPush(&stack, begin);
}
if (slow + 1 < end)
{
StackPush(&stack, end);
StackPush(&stack, slow + 1);
}
}
}
栈函数原码(头文件):
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#define QuickSortThreshold 7
typedef int SatackDataType;
typedef struct Stack
{
int capacity;
int num;
SatackDataType* head;
}Stack;
void StackPush(Stack* stack, SatackDataType x);
void StackPop(Stack* stack);
StackInit(Stack* stack);
SatackDataType StackTop(Stack* stack);
bool StackEmpty(Stack* stack);
void StackDestroy(Stack* stack);
//直接插入排序(升序)
void InsertSort(int* a, int n);
栈函数(源文件)接口函数:
#define _CRT_SECURE_NO_WARNINGS
#include"标头.h"
StackInit(Stack* stack)
{
assert(stack);
stack->head = NULL;
stack->capacity = stack->num = 0;
}
void StackPush(Stack* stack, SatackDataType x)
{
assert(stack);
if (stack->capacity == stack->num) //扩容
{
stack->capacity = stack->capacity == 0 ? 2 : stack->capacity * 2;
stack->head = (SatackDataType*)realloc(stack->head, sizeof(SatackDataType) * stack->capacity);
if (stack->head == NULL)
{
perror("StackPush realloc fail");
exit(-1);
}
}
stack->head[stack->num] = x;
stack->num++;
}
void StackPop(Stack* stack)
{
if (stack->num == 0)
return;
stack->num--;
}
SatackDataType StackTop(Stack* stack)
{
if (stack->num == 0)
return;
return stack->head[stack->num - 1];
}
bool StackEmpty(Stack* stack)
{
if (stack->num == 0)
return true;
return false;
}
void StackDestroy(Stack* stack)
{
free(stack->head);
stack->head = NULL;
stack->capacity = stack->num = 0;
}
//直接插入排序 (升序)
//排升序
void InsertSort(int* a, int n)
{
int i = 1;
while (i <= n - 1)
{
int j = 0;
while (j <= i) //找到i应该在的位置
{
if (a[i] < a[j])
break;
j++;
}
int tem = i;
int obj = a[i]; //暂存需要插入的值
while (tem > j)
{
a[tem] = a[tem - 1];
tem--;
}
a[tem] = obj;
i++;
}
}
最后快速排序的优化:
在待排序序列较小时,快速排序的速度其实并不理想,反而没有直接插入排序来的快,故可以考虑,当元素个数小于一定值时,进行插入排序,大于这个值时采用快速排序。
有的资料显示认为 阈值为7比较合适,也有资料认为阈值为50较为合适,经过个人测试 选择了7作为阈值
源码:
//快速排序优化+直接插入排序
void QuickSortMethod5(int* head, int left, int right)
{
if (left >= right)
return;
if (right - left + 1 > 7)
{
//确定阈值
//三数取中 下面三个if 使右边的最大 中间的最小 左边的中间值即为阈值
int Keyi = (left + right) / 2;
if (head[left] > head[right])
Swanp(&head[left], &head[right]);
if (head[Keyi] > head[right])
Swanp(&head[Keyi], &head[right]);
if (head[left] < head[Keyi])
Swanp(&head[Keyi], &head[left]);
int slow = left;
int fast = left + 1;
int Key = head[left]; //存一下 关键字
while (fast <= right)
{
if (head[fast] <= Key)
{
if (slow + 1 != fast) //说明中间有大于关键词的值了 要进行交换
Swanp(&head[slow + 1], &head[fast]);
slow++;
}
fast++;
}
Swanp(&head[left], &head[slow]);
if ((slow - 1 > left) && (slow - left > 7))
QuickSortMethod5(head, left, slow - 1);
else
InsertSort(head + left, slow - left);
if ((right > slow + 1) && (right - slow > 7))
QuickSortMethod5(head, slow + 1, right);
else
InsertSort(head + slow + 1, right - slow);
}
else
InsertSort(head, right-left+1);
}
测试结果:测试结果都是没问题的 大家可用 百万 千万 亿个量级的数 来跑一跑 测试一下