python——常见排序算法解析

 

算法是程序员的灵魂。
下面的博文是我整理的感觉还不错的算法实现
原理的理解是最重要的,我会常回来看看,并坚持每天刷leetcode
本篇主要实现九(八)大排序算法,分别是冒泡排序,插入排序,选择排序,希尔排序,归并排序,快速排序,堆排序,计数排序。希望大家回顾知识的时候也能从我的这篇文章得到帮助。

概述

十种常见排序算法可以分为两大类:

  1. 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
  2. 线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。

基础定义

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

图示

为了防止误导读者,本文所有概念性内容均截取自对应Wiki。

冒泡排序

原理

冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

步骤

冒泡排序算法的运作如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

代码

def bubble_sort(list):
length = len(list)
# 第一级遍历
for index in range(length):
# 第二级遍历
for j in range(, length - index):
if list[j - ] > list[j]:
# 交换两者数据,这里没用temp是因为python 特性元组。
list[j - ], list[j] = list[j], list[j - ]
return list

这种排序其实还可以稍微优化一下,添加一个标记,在排序已完成时,停止排序。

def bubble_sort_flag(list):
length = len(list)
for index in range(length):
# 标志位
flag = True
for j in range(, length - index):
if list[j - ] > list[j]:
list[j - ], list[j] = list[j], list[j - ]
flag = False
if flag:
# 没有发生交换,直接返回list
return list
return list

选择排序

原理

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理大致是将后面的元素最小元素一个个取出然后按顺序放置。

步骤

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

代码

def selection_sort(list):
n=len(list)
for i in range (,n):
min = i
for j in range(i+,n):
if list[j]<list[min]:
min=j
list[min],list[i]=list[i],list[min]
return list

插入排序

原理

插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

步骤

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

代码

def insert_sort(list):
n = len(list)
for i in range(, n):
# 后一个元素和前一个元素比较
# 如果比前一个小
if list[i] < list[i - ]:
# 将这个数取出
temp = list[i]
# 保存下标
index = i
# 从后往前依次比较每个元素
for j in range(i - , -, -):
# 和比取出元素大的元素交换
if list[j] > temp:
list[j + ] = list[j]
index = j
else:
break
# 插入元素
list[index] = temp
return list

希尔排序

原理

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

步骤

每次以一定步长(就是跳过等距的数)进行排序,直至步长为1.

代码

def shell_sort(list):
n = len(list)
# 初始步长
gap = n //
while gap > :
for i in range(gap, n):
# 每个步长进行插入排序
temp = list[i]
j = i
# 插入排序
while j >= gap and list[j - gap] > temp:
list[j] = list[j - gap]
j -= gap
list[j] = temp
# 得到新的步长
gap = gap //
return list

步长使用的是Donald Shell的建议,另外步长还可以使用Sedgewick提出的(1, 5, 19, 41, 109,…)。
也可以使用斐波那契数列除去0和1将剩余的数以黄金分区比的两倍的幂进行运算得到的数列。

归并排序

原理

归并操作(归并算法),指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。

步骤

1.迭代法

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
    3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  3. 重复步骤3直到某一指针到达序列尾
  4. 将另一序列剩下的所有元素直接复制到合并序列尾

递归法

假设序列共有n个元素:

  1. 将序列每相邻两个数字进行归并操作,形成 {\displaystyle floor(n/2)} floor(n/2)个序列,排序后每个序列包含两个元素
  2. 将上述序列再次归并,形成 {\displaystyle floor(n/4)} floor(n/4)个序列,每个序列包含四个元素
  3. 重复步骤2,直到所有元素排序完毕

代码

# 递归法
def merge_sort(list):
# 认为长度不大于1的数列是有序的
if len(list) <= :
return list
# 二分列表
middle = len(list) //
left = merge_sort(list[:middle])
right = merge_sort(list[middle:])
# 最后一次合并
return merge(left, right)
# 合并
def merge(left, right):
l,r=,
result=[]
while l<len(left) and r<len(right):
if left[l] <right[r]:
result.append(left[l])
l+=
else:
result.append(right[r])
r +=
reslut +=left[l:]
result+=right[r:]
return result

鄙人不才,不知归并排序的迭代法如何用Python实现,望指教。

快速排序

原理

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

步骤

  1. 从数列中挑出一个元素,称为”基准”(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

代码

普通版

def quick_sort(list):
less = []
pivotList = []
more = []
# 递归出口
if len(list) <= :
return list
else:
# 将第一个值做为基准
pivot = list[]
for i in list:
# 将比急转小的值放到less数列
if i < pivot:
less.append(i)
# 将比基准打的值放到more数列
elif i > pivot:
more.append(i)
# 将和基准相同的值保存在基准数列
else:
pivotList.append(i)
# 对less数列和more数列继续进行排序
less = quick_sort(less)
more = quick_sort(more)
return less + pivotList + more

咳咳,下面这段代码出自《Python cookbook 第二版》传说中的三行实现python快速排序。

def qsort(arr):
if len(arr) <= :
return arr
else:
pivot = arr[]
less = [x for x in arr[:] if x < pivot]
greater = [x for x in arr[:] if x >= pivot]
return qsort(less) + [pivot] + qsort(greater)

当然还有一行语法糖版本:

qs = lambda xs : ( (len(xs) <=  and [xs]) or [ qs( [x for x in xs[:] if x < xs[]] ) + [xs[]] + qs( [x for x in xs[:] if x >= xs[]] ) ] )[]

是不是感受到了Python的魅力?

堆排序

原理

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

步骤

  1. 创建最大堆:将堆所有数据重新排序,使其成为最大堆
  2. 最大堆调整:作用是保持最大堆的性质,是创建最大堆的核心子程序
  3. 堆排序:移除位在第一个数据的根节点,并做最大堆调整的递归运算

代码

def heap_sort(list):
# 创建最大堆
for start in range((len(list) - ) // 2, -1, -1):
sift_down(list, start, len(list) - ) # 堆排序
for end in range(len(list) - , , -):
list[], list[end] = list[end], list[]
sift_down(list, , end - )
return list # 最大堆调整
def sift_down(lst, start, end):
root = start
while True:
child = * root +
if child > end:
break
if child + <= end and lst[child] < lst[child + ]:
child +=
if lst[root] < lst[child]:
lst[root], lst[child] = lst[child], lst[root]
root = child
else:
break

计数排序

原理

当输入的元素是n个0到k之间的整数时,它的运行时间是Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。

由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序算法中,能够更有效的排序数据范围很大的数组。

步骤

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为i的元素出现的次数,存入数组 C 的第 i 项
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

代码

def count_sort(list):
min =
max =
# 取得最大值和最小值
for x in list:
if x < min:
min = x
if x > max:
max = x
# 创建数组C
count = [] * (max - min +)
for index in list:
count[index - min] +=
index =
# 填值
for a in range(max - min+):
for c in range(count[a]):
list[index] = a + min
index +=
return list

第九种排序

None?
当然不会
自然就是系统自带的

list.sort()

python常见排序算法解析的更多相关文章

  1. python——常见排序算法解析

    算法是程序员的灵魂. 下面的博文是我整理的感觉还不错的算法实现 原理的理解是最重要的,我会常回来看看,并坚持每天刷leetcode 本篇主要实现九(八)大排序算法,分别是冒泡排序,插入排序,选择排序, ...

  2. 常见排序算法-Python实现

    常见排序算法-Python实现 python 排序 算法 1.二分法     python    32行 right = length-  :  ]   ):  test_list = [,,,,,, ...

  3. python 的常见排序算法实现

    python 的常见排序算法实现 参考以下链接:https://www.cnblogs.com/shiluoliming/p/6740585.html 算法(Algorithm)是指解题方案的准确而完 ...

  4. [算法] 常见排序算法总结(C语言版)

    常见排序算法总结 本文对比较常用且比较高效的排序算法进行了总结和解析,并贴出了比较精简的实现代码,包括选择排序.插入排序.归并排序.希尔排序.快速排序等.算法性能比较如下图所示: 1 冒泡排序 基本原 ...

  5. python 经典排序算法

    python 经典排序算法 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.常见的内部排序算 ...

  6. Java基础语法(8)-数组中的常见排序算法

    title: Java基础语法(8)-数组中的常见排序算法 blog: CSDN data: Java学习路线及视频 1.基本概念 排序: 是计算机程序设计中的一项重要操作,其功能是指一个数据元素集合 ...

  7. 常见排序算法(附java代码)

    常见排序算法与java实现 一.选择排序(SelectSort) 基本原理:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换:接着对不包括第一个记录以外的其他 ...

  8. python 常见排序实例

    使用Python 基础排序算法设计,冒泡排序,插入排序,快速排序... 需求 对一组无序数据进行排序算法设计,要求如下: 输入:[1, 3, 5, 23, 75, 34, 456, 86, 22, 7 ...

  9. JS常见排序算法

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. jQuery动态添加、删除按钮及input输入框

    输入框的加减实现: <html> <head> <meta charset="utf-8"> <title>动态创建按钮</t ...

  2. (转)Java并发包基石-AQS详解

    背景:之前在研究多线程的时候,模模糊糊知道AQS这个东西,但是对于其内部是如何实现,以及具体应用不是很理解,还自认为多线程已经学习的很到位了,贻笑大方. Java并发包基石-AQS详解Java并发包( ...

  3. 蓝书例题之UVa 10253 Series-Parallel Networks

    挺有趣的一道题 首先转化模型,思路参考蓝书,可得出等同于求共n个叶子,且每个非叶结点至少有两个子结点的无标号树的个数的二倍,设个数为\(f[n]\) 考虑怎么求\(f[n]\),假设有一个\(n\)的 ...

  4. [BJOI2019]奥术神杖(分数规划+AC自动机+DP)

    题解:很显然可以对权值取对数,然后把几何平均值转为算术平均值,然后很显然是分数规划.先对每个模式串建立AC自动机,每个节点w[i],sz[i]分别表示以其为前缀的字符串,然后再二分最优解k,然后w[i ...

  5. linux 命令之文件读取,head, tail, tailf, sed

    head 看文件的前100行head -100  filename tail/tailf查看文件的后100行tail -100  filename 或 tail -n 100  filename ta ...

  6. 关于4A系统(我对4A系统的维护的理解)

    4A系统 4A系统是统一安全管理平台解决方案,指认证Authentication.账号Account.授权Authorization.审计Audit,中文名称为统一安全管理平台解决方案.即将身份认证. ...

  7. Mac 下 Eclipse 添加 Dynamic Web Project 并配置 Tomcat

    最近拿到了一个 Dynamic Web Project,我的 Mac 上的 Eclipse 之前没有过这类型的项目,所以导入之后无法正常运行.下面是我记录的如何配置 Eclipse 使之能够运行 Dy ...

  8. beego学习1

    下载 go get -u -v github.com/astaxie/beego   beego源码 go get -u -v  github.com/beego/bee   bee开发工具 bee ...

  9. 域 搭建OU 组织单元

    以这个界面开始操作: 在 baidu.com 右键---新建----组织单位----北京分公司 在 baidu.com 右键---新建----组织单位----北京分公司 在北京分公司 和南京分公司下面 ...

  10. Android防止手动添加的本地库文件被NDK工具清理掉

    比如要添加一个名为libocr的本地库,先将libocr.so文件拷贝到jni目录下,然后在Android.mk文件中添加以下语句即可. 例: LOCAL_PATH := $(call my-dir) ...