方法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和那个数,让那个数当keySwap(&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和那个数,让那个数当keySwap(&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可以把越界的情况丢给外循环whileif (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可以把越界的情况丢给外循环whileif (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; // -1ps->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];
}

快排 递归三种方式+非递归 --排序相关推荐

  1. 数据结构之排序【归并排序和快排的顶级优化和快排的三种原理的实现及分析】 内含动态演示图

    文章目录 引言: 1.归并排序(MergeSort) 2.快速排序的优化(顶级优化) 3.快速排序的三种思路的代码实现及分析 4.归并排序和快排第3原理的测试 引言: 刚刚去回顾了一下递归实现的几个小 ...

  2. 快排的三种优化方式。

    对于快排而言,其核心在partition中,主要是对于pivot的选取上,所以我们可以按以下三种方案进行优化: 1.在数组长度大于某一个阈值范围时,我们进行递归快排,当数据长度小于阈值时,我们进行插入 ...

  3. 详解C#实例化对象的三种方式及性能对比

    前言 做项目过程中有个需求要实例化两万个对象并添加到List 中,这个过程大概需要1min才能加载完(传参较多),于是开启了代码优化之旅,再此记录. 首先想到的是可能实例化比较耗时,于是开始对每种实例 ...

  4. 三种快排及四种优化方式

    本文是转载文章,文章的来源:csdn博客 博主:silentsharer 文章: 三种快排及四种优化方式 博文地址:https://blog.csdn.net/hacker00011000/artic ...

  5. 一种二叉树非递归遍历的简单写法

    一种二叉树非递归遍历的简单写法 目录 一种二叉树非递归遍历的简单写法 先序遍历 中序遍历 后序遍历 二叉树的遍历是数据结构中非常基础的一个知识点,也是面试手撕代码环节的一个常见题目.这个问题的递归写法 ...

  6. Java~二叉树的前中后序遍历的几种方式(递归法,迭代法,标记法等)

    目录 一.结点的定义 二.递归法遍历二叉树 前序遍历 中序遍历 后序遍历 三.迭代(非递归)遍历二叉树 (1).迭代模拟法 前序遍历 中序遍历 后序遍历 (2).空指针标记法 前序遍历 中序遍历 后序 ...

  7. APS计划排程系统之下的MRPII、JIT、TOC三种方式对比分析

    1.生产物流计划的制订方式对比 ①MRPII采用的是集中式的物料计划方式,建立好产品加工程序,在电脑中确定好准确的订单需求和库存量,对各个生产单元传送生产指令: ②JIT利用的是看板管理控制方式,按照 ...

  8. STM32芯片烧录的三种方式介绍,串口、STM32 ST-LINK Utility以及STM32CubeProgrammer

    STM32芯片烧录的三种方式介绍,串口.STM32 ST-LINK Utility以及STM32CubeProgrammer 1 概述 1.1资源概述 1.2 STM32串口烧录方式 2.KEIL软件 ...

  9. PHP压缩和解压缩文件的三种方式

    前言:平时在项目中,偶尔会遇到文件备份导入导出的需求,在此记录使用PHP执行文件压缩和解压缩的三种方式** 目录 1. 使用PHP扩展的ZipArchive类 2. 使用压缩与解压缩类PclZip.p ...

最新文章

  1. embed的名词_常见名词解释
  2. 网页编程,JS模态窗口,子页面向父页面传递值
  3. ArcGIS对AutoCAD操作的图文教程
  4. java——原型模式
  5. 有没有大神知道国产加密算法SM2的详细介绍
  6. 单片机入门学习笔记7:人机交互界面
  7. SAP S/4 HANA与SAP Business Suite/R3(ECC)的区别
  8. Postman界面了解
  9. Java 读取指定目录下的文件名和目录名
  10. WordPress post和page的区别
  11. 使用valgrind对gperftools(tcmalloc)进行内存泄漏和越界检测
  12. 内容库-管理介质和模板的最佳办法(转)
  13. 大数据_Flink_Java版_数据处理_流处理API_Transform(5)_connect合流---Flink工作笔记0033
  14. 在你的网站中使用 AdSense广告
  15. 比较awk python: [文件]web日志信息统计 。 [命令]netstat命令状态统计
  16. 信息林-青岛分类信息网.net源码
  17. 2.http dns
  18. 离散数学真值表c语言实验报告,离散数学五人表决真值表实验报告
  19. 搜狗双拼--提升打字效率
  20. 找坏球——面试智力题

热门文章

  1. 一文搞懂redis的存储机制AOF与RDB
  2. 软件设计实验(一)C#火车售票系统-基于UML的软件分析与设计模型建模实验(用例图、类图、时序图)
  3. win10开启蓝牙虚拟串口
  4. 今天卡巴开始封key了,现在分享 整合Kav 6.0(KIS6.0) 一组可用key 至 2008-01-2
  5. 腾讯QQ同时在线人数突破2亿
  6. 推荐系统应用---音乐类
  7. gmail怎么登陆?
  8. C++入门级——函数重载
  9. 乘法原理的例题和答案_乘法原理的例题
  10. Redis集群搭建linux