方法1–Hoare
#include"Sort.h"
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
}
void Swap(int* px, int* py)
{
int tmp = *px;
*px = *py;
*py = tmp;
}
int PartSort1(int* a, int left, int right)
{
int keyi = left;
while (left < right)
{
// 左边做key,右边先走找小
while (left < right && a[right] >= a[keyi])//等号是为了防止里面有和keyi相等的数据的时候会不动而产生的死循环
{
--right;
}
// 左边再走,找大
while (left < right && a[left] <= a[keyi])
{
++left;
}
// 交换,把大的换到右边,小的换到左边
Swap(&a[left], &a[right]);
}
Swap(&a[keyi], &a[left]);
return left;
}
void QuickSort(int* a, int left, int right)
{
if (left < right)
{
int keyi = PartSort1(a, left, right);
// [0, keyi-1] keyi [keyi+1, right]
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
}
void TestQuickSort()
{
int a[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PrintArray(a, sizeof(a) / sizeof(int));
QuickSort(a, 0, sizeof(a) / sizeof(int)-1);
PrintArray(a, sizeof(a) / sizeof(int));
}
左边为keyi,让右边先走是如何保证相遇位置的值小于key的:
时间复杂度
最好的情况:每次取到的数都为中间的数即可以像一颗二叉树一样展开–高度为logN,每一层单趟排为N则O(NlogN)
最坏的情况:有序 O(N^2)
方法1改进–针对key取值导致时间复杂度可能为O(N^2)的改进
1、随机选key
2、三数取中选key,不是最大也不是最小。有了三数取中,快排就不怕有序的这种最坏情况了
Sort.c
// Hoare
#include"Sort.h"
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
}
void Swap(int* px, int* py)
{
int tmp = *px;
*px = *py;
*py = tmp;
}
// 三数取中---left,right三个数比较大小取中间的那个数的下标
//获取那个适合的数的下标
int GetMidIndex(int* a, int left, int right)
{
//int mid = (left + right) / 2;
//为了防止越界
int mid = left + (right - left) / 2;
if (a[left] < a[mid])
{
if (a[mid] < a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return right;
}
else
{
return left;
}
}
else //(a[left] > a[mid])
{
if (a[mid] > a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return left;
}
else
{
return right;
}
}
}
int PartSort1(int* a, int left, int right)
{
//获取到那个数的下标
int midi = GetMidIndex(a, left, right);
//交换一下key和那个数,让那个数当key
Swap(&a[left], &a[midi]);
int keyi = left;
while (left < right)
{
// 左边做key,右边先走找小
//内循环的left<right是为了防止left和right跳过不相遇, a[right] >= a[keyi],等号是为了防止key的值和left/right的值相等的时候产生死循环
while (left < right && a[right] >= a[keyi])
{
--right;
}
// 左边再走,找大
while (left < right && a[left] <= a[keyi])
{
++left;
}
// 交换,把大的换到右边,小的换到左边
Swap(&a[left], &a[right]);
}
//相遇后交换,相遇的值一定比key小
Swap(&a[keyi], &a[left]);
//返回key的位置
return left;
}
void QuickSort(int* a, int left, int right)
{
if(left>=right)
{
return ;
}
//if (left < right)
//{
int keyi = PartSort1(a, left, right);
// [0, keyi-1] keyi [keyi+1, right]
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
//}
}
void TestQuickSort()
{
int a[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PrintArray(a, sizeof(a) / sizeof(int));
QuickSort(a, 0, sizeof(a) / sizeof(int)-1);
PrintArray(a, sizeof(a) / sizeof(int));
}
test.c
#include <time.h>
#include <stdlib.h>
#include "Sort.h"
void TestOP()
{
srand(time(0));
const int N = 100000;
int* a1 = (int*)malloc(sizeof(int)*N);
int* a2 = (int*)malloc(sizeof(int)*N);
int* a3 = (int*)malloc(sizeof(int)*N);
int* a4 = (int*)malloc(sizeof(int)*N);
int* a5 = (int*)malloc(sizeof(int)*N);
int* a6 = (int*)malloc(sizeof(int)*N);
int* a7 = (int*)malloc(sizeof(int)*N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
a7[i] = a1[i];
}
int begin5 = clock();
//QuickSort(a5, 0, N - 1);
int end5 = clock();
int begin6 = clock();
//(a6, N);
int end6 = clock();
printf("QuickSort:%d\n", end5 - begin5);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
}
int main()
{
//TestInsertSort();
//TestShellSort();
//TestSelectSort();
//TestHeapSort();
TestOP();
return 0;
}
时间复杂度::O(N*logN)—像一颗二叉树一样展开–高度为logN,每一层单趟排为N
空间复杂度:O(logN)
方法2–挖坑法
Sort.c
#include"Sort.h"
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
}
void Swap(int* px, int* py)
{
int tmp = *px;
*px = *py;
*py = tmp;
}
// 三数取中---中间,left,right三个数比较大小取第二大的那个数的下标
//获取那个适合的数的下标
int GetMidIndex(int* a, int left, int right)
{
//int mid = (left + right) / 2;
//为了防止越界
int mid = left + (right - left) / 2;
if (a[left] < a[mid])
{
if (a[mid] < a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return right;
}
else
{
return left;
}
}
else //(a[left] > a[mid])
{
if (a[mid] > a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return left;
}
else
{
return right;
}
}
}
int PartSort2(int* a, int left, int right)
{
//获取到那个数的下标
int midi = GetMidIndex(a, left, right);
//交换一下key和那个数,让那个数当key
Swap(&a[left], &a[midi]);
//一开始让left为坑,右边先去找小的数放到坑中,再左边取去找大的数放到右边刚产生的坑
int key = a[left];
int hole = left;
while (left < right)
{
while (left < right && a[right] >= key)
{
--right;
}
//把右边找的小的,填到左边的坑,自己形成新的坑
a[hole] = a[right];
hole = right;
while (left < right && a[left] <= key)
{
++left;
}
//把左边找的大的,填到右边的坑,自己形成新的坑
a[hole] = a[left];
hole = left;
}
//key填到最后剩下的坑
a[hole] = key;
return hole;
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
int keyi = PartSort2(a, left, right);
//PrintArray(a+left,right-left + 1);
// [left, keyi-1] keyi [keyi+1, right]
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
void TestQuickSort()
{
int a[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PrintArray(a, sizeof(a) / sizeof(int));
QuickSort(a, 0, sizeof(a) / sizeof(int)-1);
PrintArray(a, sizeof(a) / sizeof(int));
}
test.c
#include <time.h>
#include <stdlib.h>
#include "Sort.h"
void TestOP()
{
srand(time(0));
const int N = 100000;
int* a1 = (int*)malloc(sizeof(int)*N);
int* a2 = (int*)malloc(sizeof(int)*N);
int* a3 = (int*)malloc(sizeof(int)*N);
int* a4 = (int*)malloc(sizeof(int)*N);
int* a5 = (int*)malloc(sizeof(int)*N);
int* a6 = (int*)malloc(sizeof(int)*N);
int* a7 = (int*)malloc(sizeof(int)*N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
a3[i] = a1[i];
a4[i] = a1[i];
a5[i] = a1[i];
a6[i] = a1[i];
a7[i] = a1[i];
}
int begin5 = clock();
//QuickSort(a5, 0, N - 1);
int end5 = clock();
int begin6 = clock();
//(a6, N);
int end6 = clock();
printf("QuickSort:%d\n", end5 - begin5);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
}
int main()
{
//TestInsertSort();
//TestShellSort();
//TestSelectSort();
//TestHeapSort();
TestOP();
return 0;
}
方法1和方法2的缺陷:
1、都是假设左边(右边)做key就得右边(左边)先走才能保证相遇的位置的值比小
2、且有可能方法1和方法2产生的排序结果不一致–所以遇到要求第一次快排后的数组顺序的选择题两种方法都验证一下是否正确
方法3–前后指针
int PartSort3(int* a, int left, int right)
{
int midi = GetMidIndex(a, left, right);
Swap(&a[left], &a[midi]);
int keyi = left;
int prev = left;
int cur = prev + 1;
while (cur <= right)
{
//里面写while还要判断越界情况,里面用if可以把越界的情况丢给外循环while
if (a[cur] < a[keyi])
{
Swap(&a[++prev], &a[cur]);
cur++;
}
else
{
cur++;
}
//更好写法
当cur找到比keyi要小的值且++prev以后不和它相等,相等的时候不交换也可以
//if (a[cur] < a[keyi] && ++prev != cur)
// Swap(&a[prev], &a[cur]);
无论什么条件cur都得++
//++cur;
}
Swap(&a[prev], &a[keyi]);
return prev;
}
void QuickSort(int* a, int left, int right)
{
// if (left >= right)
// return;
if (left < right)
{
int keyi = PartSort3(a, left, right);
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
}
void TestQuickSort()
{
int a[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PrintArray(a, sizeof(a) / sizeof(int));
QuickSort(a, 0, sizeof(a) / sizeof(int)-1);
PrintArray(a, sizeof(a) / sizeof(int));
}
方法四–非递归
递归是正确的,但是递归的深度太深会奔溃即栈溢出
int func(int n)
{
if (n <= 1)
{
return n;
}
else
{
return func(n - 1) + n;
}
}
int main()
{
printf("%d\n", func(100000\));
return 0;
}
任何一个递归程序,我们要掌握把他改成非递归
- 循环
- 栈+循环
方法:栈+循环
Sort.c
#include"Stack.h"
int PartSort3(int* a, int left, int right)
{
int midi = GetMidIndex(a, left, right);
Swap(&a[left], &a[midi]);
int keyi = left;
int prev = left;
int cur = prev + 1;
while (cur <= right)
{
//里面写while还要判断越界情况,里面用if可以把越界的情况丢给外循环while
if (a[cur] < a[keyi])
{
Swap(&a[++prev], &a[cur]);
cur++;
}
else
{
cur++;
}
}
Swap(&a[prev], &a[keyi]);
return prev;
}
void QuickSort(int* a, int left, int right)
{
if (left < right)
{
int keyi = PartSort3(a, left, right);
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
}
void QuickSortNonR(int* a, int left, int right)
{
ST st;
StackInit(&st);
StackPush(&st, right);
StackPush(&st, left);
while (!StackEmpty(&st))
{
int begin = StackTop(&st);
StackPop(&st);
int end = StackTop(&st);
StackPop(&st);
//先单趟排
int keyi = PartSort3(a, begin, end);
// [begin, keyi-1] keyi [keyi+1, end]
//1、想要先处理左边,则需要右边先入栈
if (keyi + 1 < end)
{
//先入右
StackPush(&st, end);
//再入左
StackPush(&st, keyi + 1);
}
//2、左边再入栈
if (begin < keyi - 1)
{
//先入右
StackPush(&st, keyi - 1);
//再入左
StackPush(&st, begin);
}
}
StackDestroy(&st);
}
void TestQuickSort()
{
int a[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
PrintArray(a, sizeof(a) / sizeof(int));
QuickSort(a, 0, sizeof(a) / sizeof(int)-1);
QuickSortNonR(a, 0, sizeof(a) / sizeof(int)-1);
PrintArray(a, sizeof(a) / sizeof(int));
}
Stack.h
#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
// ̬
//#define N 100
//typedef int STDatatype;
//struct Stack
//{
// STDatatype a[N];
// int top; // ջ
//};
typedef int STDatatype;
typedef struct Stack
{
STDatatype* a;
int top; // ջ
int capacity;
}ST;
void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDatatype x);
void StackPop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
STDatatype StackTop(ST* ps);
Stack.c
#include "Stack.h"
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0; // -1
ps->capacity = 0;
}
void StackDestroy(ST* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void StackPush(ST* ps, STDatatype x)
{
assert(ps);
// 检查空间够不够,不够就增容
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDatatype* tmp = realloc(ps->a, sizeof(STDatatype)*newcapacity);
if (tmp == NULL)
{
printf("rellaoc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
--ps->top;
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
STDatatype StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}