一、快排基础

1.1 快排的流程

将数组A进行快速排序的基本步骤-quick_sort(A):

  • 递归基础情况:如果A中的元素个数是1或0,则返回
  • 选取主元:取A中的任意一个元素v,作为主元(pivot)。
  • 交换策略:将A-{v}即A中剩余元素,划分成两个不相交的集合(多重集)A1和A2,
  • 递归处理:递归调用quick_sort(A1),再调用quick_sort(A2)

伪代码

可视化快排



选取主元后,将原数组划分为:值小于等于主元的左子数组,值大于等于主元的右子数组。然后递归地对左右子数组进行上述操作,知道递归的基础情况直接返回子数组(递归调用的过程类似二叉树的前序遍历)。

比较基础的情况: 输入数组大小为3, 选取主元并划分后,左子数组和右子数组包含1个数,递归调用快排直接返回,此时已经是有序的。

1.2 主元选取

选取主元: 最优选取到输入数组的中位数,可以均衡进行递归处理。

1)如果直接选择输入数组的第一个元素,在输入数组是有序的情况下,那么将导致快排的最坏情况-平方复杂度。



左边每次取一个最小数,每次划分的复杂度和长度成线性递减,但需划分N-1次

2)随机选取主元: 代价不小

3)三数中值分割法:返回Left, Center, Right三个数中的中位数作为主元,避免最坏情况

  • 先让Left<=Center, Left<=Right(若已满足不用管,不满足则交换元素), 使Left是最小数;
  • 那么只需要确定Center, Right的大小。直接通过交换,让Center<=Right, Center即中位数

这时,三数中的最小者被分配在A[Left],其小于pivot; 最大者被分配在A[Right], 其大于pivot。



可以把pivot放在 A[Right - 1]并在分割阶段,将左右开始索引i=left, j=right-1(A[Right]已经划分好了)

Left和Right为输入数组的左右边界索引。

C语言实现主元选取
  1. typedef int ElementType;
  2. void Swap( int *a, int *b )
  3. {
  4. int t = *a;
  5. *a = *b;
  6. *b = t;
  7. }
  8. ElementType Median3( ElementType A[], int Left, int Right )
  9. {
  10. int Center = ( Left + Right) / 2;
  11. if ( A[Left] > A[Center] )
  12. Swap( &A[Left], &A[Center] ); /* 如果左大于中则交换,保证A[Left] <= A[Center] */
  13. if ( A[Left] > A[Right] )
  14. Swap( &A[Left], &A[Right] ); /* 如果左大于右则交换,保证A[Left] <= A[Right] */
  15. if ( A[Center] > A[Right] )
  16. Swap( &A[Center], &A[Right] ); /* 如果中大于右则交换,保证A[Center] <= A[Right] */
  17. /* A[Left] <= A[Cetner] <= A[Right] */
  18. Swap( &A[Center], &A[Right-1] ); /* 将Pivot藏到右边Right-1作为哨兵*/
  19. return A[Right-1]; /* 返回实际主元值 */
  20. }

1.3 交换策略

元素的交换策略:左边找大于等于主元,右边找小于等于主元;

刚刚越界时i>=j, i元素 >= 主元, j元素 <= 主元, 两边已经交换好; i和right-1交换,即把主元和大于等于它的值交换。

快排实现-C
  1. void Q_sort( ElementType A[], int Left, int Right )
  2. {
  3. if (Right - Left <= 0) {
  4. return;
  5. } else {
  6. int i = Left;
  7. int j = Right - 1;
  8. ElementType pivot = Median3( A, Left, Right );
  9. if (Right - Left == 1)
  10. return;
  11. for ( ; ; ) {
  12. while( A[++i] < pivot ); /* 从左边找个大于等于pivot的数 */
  13. while( A[--j] > pivot ); /* 从右边找个小于等于pivot的数 */
  14. if ( i < j )
  15. Swap( &A[i], &A[j] ); /* 未越界,则交换 */
  16. else
  17. break;
  18. }
  19. Swap( &A[i], &A[Right - 1] ); /* restroe pivot*/
  20. Q_sort( A, Left, i - 1 ); /* from pivot to divid sort */
  21. Q_sort( A, i + 1, Right );
  22. }
  23. }
  24. void Quick_sort( ElementType A[], int N )
  25. {
  26. Q_sort( A, 0, N-1 );
  27. }

二、Python版快排

  1. class Solution:
  2. def sortArray(self, nums: List[int]) -> List[int]:
  3. def swap(nums, i, j):
  4. nums[i], nums[j] = nums[j], nums[i]
  5. def median3(nums, left, right):
  6. center = (left+right) // 2
  7. if nums[left] > nums[center]:
  8. swap(nums, left, center)
  9. if nums[left] > nums[right]:
  10. swap(nums, left, right)
  11. if nums[center] > nums[right]:
  12. swap(nums, center, right)
  13. swap(nums, center, right-1)
  14. return nums[right-1]
  15. def quick_sort(nums, left, right):
  16. if right-left > 0:
  17. pivot = median3(nums, left, right)
  18. if right-left == 1:
  19. return
  20. i, j = left, right-1
  21. while True:
  22. i += 1
  23. while nums[i] < pivot: i += 1
  24. j -= 1
  25. while nums[j] > pivot: j -= 1
  26. if i < j:
  27. swap(nums, i, j)
  28. else:
  29. break
  30. swap(nums, i, right-1)
  31. quick_sort(nums, i+1, right)
  32. quick_sort(nums, left, i-1)
  33. quick_sort(nums, 0, len(nums)-1)
  34. return nums

arr_len = 输入数组arr长度 = right -left + 1, 需要处理好基准情况:

  • Case1: arr_len 小于等1时,直接返回(不处理了);
  • Case2: arr_len =2时, 这时right - left =1,取到的pivot实际为arr[left], left = center, = right-1, 不需要也不能进入划分子数组阶段(会越界),而且通过median3已经将长为2的数组排序,故可在调用median3后直接当前递归程序;
  • Case3: arr_len == 3,上述快排程序已经可以处理, 并可以进一步通过Case1结束递归;
  • Case4: arr_len > 3: 进行快排程序,并通过Case1到3结束递归;

处理基准情况,当输入数组较小时,right - left > 5,直接调用内置排序或插入排序处理,避免进一步递归调用。相当于把更下层的递归调用,直接实现而不用快排实现(快排更慢)。

点击查看代码
  1. def quick_sort(arr, left, right):
  2. if right - left > 5:
  3. pivot = mcedian3(arr, left, right)
  4. i = left
  5. j = right-1
  6. while True:
  7. i += 1
  8. while arr[i] < pivot:
  9. i += 1
  10. j -= 1
  11. while arr[j] > pivot:
  12. j -= 1
  13. if i < j:
  14. swap(arr, i, j)
  15. else:
  16. break
  17. swap(arr, i, right-1)
  18. quick_sort(arr, left, i-1)
  19. quick_sort(arr, i+1, right)
  20. else:
  21. arr[left:right+1] = sorted(arr[left:right+1])

快排的主元选取和划分操作,可以衍生出减治-快速选择。

【算法】C和Python实现快速排序-三数中值划分选择主元(非随机)的更多相关文章

  1. 算法笔记_031:计算中值和选择问题(Java)

    目录 1 问题描述  2 解决方案 2.1 计算中值问题 2.2 选择问题   1 问题描述 中值问题是求一个n个数列表中某一数组下标k,它要求该下标元素比列表中的一半元素大,又比另一半元素小,这个中 ...

  2. 求三数中Max和猜拳游戏

    方法一: Console.WriteLine("请输入三个数字:"); int a = int.Parse(Console.ReadLine()); int b = int.Par ...

  3. Python自学:第三章 根据值删除元素

    motorcycles = ["honda", "yamaha", "suzuki", "ducati"] print( ...

  4. python数据结构-如何根据字典中值的大小对字典项排序

    如何根据字典中值的大小对字典项排序 问题举例 某班英语成绩以字典形式存储,如何根据成绩高低,计算学生成绩排名 { “tom”:80, "lily":88, "marton ...

  5. 快速排序及STL中的sort算法

    快速排序基本思想是,对待排序序列进行划分(Partition),一次划分,选择一个元素作为枢轴,然后将所有比枢轴小的元素放到枢轴的左边,将比枢轴大的元素放到枢轴的右边.然后对该枢轴划分的左右子序列分别 ...

  6. 【机器学习实战学习笔记(1-1)】k-近邻算法原理及python实现

    笔者本人是个初入机器学习的小白,主要是想把学习过程中的大概知识和自己的一些经验写下来跟大家分享,也可以加强自己的记忆,有不足的地方还望小伙伴们批评指正,点赞评论走起来~ 文章目录 1.k-近邻算法概述 ...

  7. python实现快速排序算法

    快速排序算法又称划分交换排序(partition-exchange sort),一种排序算法,最早由东尼·霍尔提出.在平均状况下, 排序n个项目要O(nlogn)次比较.在最坏状况下则需要O(n*2) ...

  8. 南大算法设计与分析课程OJ答案代码(4)--变位词、三数之和

    问题 A: 变位词 时间限制: 2 Sec  内存限制: 10 MB提交: 322  解决: 59提交 状态 算法问答 题目描述 请大家在做oj题之前,仔细阅读关于抄袭的说明http://www.bi ...

  9. 从三数之和看如何优化算法,递推-->递推加二分查找-->递推加滑尺

    人类发明了轮子,提高了力的使用效率. 人类发明了自动化机械,将自己从重复的工作中解脱出来. 提高效率的方法好像总是离不开两点:拒绝无效劳动,拒绝重复劳动.人类如此,计算机亦如是. 前面我们说过了四数之 ...

  10. python基础练习题(题目 三数排序。)

    day40 --------------------------------------------------------------- 实例066:三数排序 题目 输入3个数a,b,c,按大小顺序 ...

随机推荐

  1. scrapy抓取校花网图片

    一:基础版(抓取首页图片) 爬虫py文件代码: 1 # -*- coding: utf-8 -*- 2 import scrapy 3 import sys 4 import io 5 from sc ...

  2. python使用selenium控制已打开的Chrome浏览器

    环境 Python3.11 selenium 4.9.0 Chrome 112.0.5615.138 步骤 为了便于和平常用的Chrome浏览区分,可以先创建一个专门用于开发的Chrome浏览器, 添 ...

  3. JQ模糊查询插件

    //构造函数写法 ;(function($,window,document,undefined){//注意这里的分号必须加 //插件的全部代码 var FazzSearch = function (e ...

  4. DNS子域委派配置·

    实验介绍:DNS子域委派的作用 子域即为主域下的一个子域名,当一个子域的流量过大时,主域的DNS服务器可以把一个子域的查询授权给一台专门的子域服务器 注意被委派的服务器必须是委派服务器的子域服务器. ...

  5. OGG_Linux_x64_BigData启动ggsci时报错:error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory

    问题描述 [root@hadoop03 ggs]$ ./ggsci ./ggsci: error while loading shared libraries: libjvm.so: cannot o ...

  6. [Java][Spring]spring profile与maven profile多环境管理

    spring profile 与 maven profile 多环境管理 spring profile Spring profile是Spring提供的多环境管理方案. 如下图: 每种环境都对应一个y ...

  7. LVM(逻辑卷管理器)

    目录 一.LVM概述 二.基本术语 三.PE.PV.VG.LV之间的关系 四.LVM的工作原理 五.LVM的使用 1.部署逻辑卷 第一步: 还原快照,并在虚拟机添加两块新硬盘设备,开机 第二步: 让新 ...

  8. NEMU PA 4 实验报告

    一.实验目的 在前面的PA123中,我们分别实现了基本的运算单元,实现了各种指令和程序的装载,实现了存储器的层次结构.而在PA4中,为了让NEMU可以处理异常情况以及和外设交互,我们要做的事情有以下: ...

  9. Python subProcess库以及Popen类的使用

    subprocess库是一个十分强大且常用的库,它可以用来调用第三方工具(例如:exe.另一个python文件.命令行工具). 1.常用函数call() :执行由参数提供的命令,把数组作为参数运行命令 ...

  10. 【Unity3D】异步Socket通讯

    1 前言 ​ 同步 Socket 通讯 中的 Accept.Connect.Receive 等方法会阻塞当前线程,当前线程必须等待这些方法执行完,才会继续往下执行,用户需要另开线程执行这些耗时方法,否 ...