N种排序算法
本文根据《算法(第4版)》和《算法图解》整理。文中代码使用python编写。
(一)选择排序
每次遍历整个数组,选出其中最小值。如果数组长度为n,则需要(n-1)+(n-2)+...+2+1次操作,则算法的时间复杂度用大O表示法表示为
,但是大O表示法省略诸如1/2这样的常数,因此该方法的大O表示为
。
选择排序的代码:
>>> def findSmallest(arr):
smallest = arr[0]
smallest_index = 0
for i in range(1, len(arr)):
if arr[i] < smallest:
smallest = arr[i]
smallest_index = i
return smallest_index >>> def selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append(arr.pop(smallest))
return newArr
(二)插入排序
用个例子来说明,arr=[5,1,2,4,7,3]。取arr[1]与arr[0]相比,如果arr[1]<arr[0],则交换arr[1]和arr[0],交换后的arr=[1,5,2,4,3,7]。再取arr[2]与arr[1]相比,如果arr[2]<arr[1],则交换arr[2]和arr[1],交换后的arr=[1,2,5,4,3,7],并再将新数组中的arr[1]与arr[0]相比,如果arr[1]<arr[0],则交换。
和选择排序不同的是,插入排序所需的时间取决于输入中元素的初始顺序。对于随机排列的长度为N且主键不重复的数组,平均情况下插入排序需要~N^2/4次比较以及~N^2/4次交换。最坏情况下需要~N^2/2次比较和~N^2/2次交换,最好情况下需要N-1次比较和0次交换。
插入排序的代码:
def insertSort(arr):
for i in range(1,len(arr)):
for j in range(i,0,-1):
if arr[j]<arr[j-1]:
temp=arr[j]
arr[j]=arr[j-1]
arr[j-1]=temp
return arr
通过在内循环中将较大的元素向右移动而不是交换,便可以大幅度提高插入排序的速度。同样以arr=[5,1,2,4,7,3]为例,令对比的基准base=arr[1],如果base<arr[0],则arr[0]向右移动一位,即arr[1]=arr[0],内循环结束,即arr[0]=base,新数组为arr=[1,5,2,4,7,3]。令base=arr[2],如果base<arr[1],则arr[1]向右移动一位,arr[2]=arr[1],继续内循环,即对比base和arr[0],在本例中base>arr[0],结束内循环,并且arr[1]=base。新数组为arr=[1,2,5,4,7,3]。
快速插入排序的代码:
def insertSort2(arr):
for i in range(1,len(arr)):
_base=arr[i]
k=0
for j in range(i,0,-1):
if _base<arr[j-1]:
arr[j]=arr[j-1]
else:
k=j
break
arr[k]=_base return arr
(三)希尔排序
对于大规模乱序数组而言,插入排序很慢,因为它只交换相邻的元素,因此元素只能一点一点地从数组的一段移动到另一端。希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。即希尔排序使数组中任意间隔为h的元素都是有序的。例如
[2, 6, 3, 5, 10, 4, 8, 21, 1, 34, 7, 9]
当h=4时,使得2-10-1有序,6-4-34有序,3-8-7有序,5-21-9有序,得到的新数组为
[1, 4, 3, 5, 2, 6, 7, 9, 10, 34, 8, 21]
然后h=1时,就是普通的插入排序,得到的新数组为
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 34]
对于本例,使用希尔排序一共交换13次,而使用插入排序的交换次数为21次
希尔排序的代码:
def ShellSort(arr):
h=1
while h<len(arr)/3:
h=3*h+1 while h>=1:
for i in range(h,len(arr)):
for j in range(i,h-1,-h):
if arr[j]<arr[j-h]:
temp=arr[j]
arr[j]=arr[j-h]
arr[j-h]=temp
else:
break
h=math.floor(h/3)
return arr
(四)归并排序
要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。归并的一种直截了当的做法是将两个不同的有序数组归并到第三个数组中。例如如下数组:
[2,4,9,13,1,5,8,23]
是由两个有序数组sub1=[2,4,9,13]和sub2=[1,5,8,23]组成的,将这两个有序的子数组归并为一个数组,就可以先创建一个长度为8的辅助数组aux,然后sub1[0]和sub2[0]作比较,把小的那个写入到aux[0]中,在本例中,sub2[0]<sub1[0],因此aux[0]=sub2[0],然后再取sub2[1]与sub1[0]对比,如此循环,终止条件为sub1或者sub2取到了最后一个数。
归并方法的代码:
def merge(arr,low,mid,high):
aux=[0]*len(arr)
i=low
j=mid+1 for k in range(low,high+1):
if i>mid:
aux[k]=arr[j]
j=j+1
elif j>high:
aux[k]=arr[i]
i=i+1
elif arr[i]<arr[j]:
aux[k]=arr[i]
i=i+1
else:
aux[k]=arr[j]
j=j+1
return aux
在构造两个有序的子数组时,可以分为自顶向下和自底向上两种方法。
自顶向下:递归得到树结构需要归并的子数组,以长度为16的数组为例,如下图所示。

先将arr[0-1]处理为有序数组,再将arr[2-3]处理为有序子数组,然后归并arr[0-1]和arr[2-3]这两个有序子数组,得到有序的arr[0-3]子数组。同样地,将arr[4-5]和arr[6-7]处理为有序子数组,然后归并得到arr[4-7]这个有序子数组。再将arr[0-3]和arr[4-7]归并为有序的arr[0-7]子数组。以同样的方式得到有序的arr[8-15]子数组,最后将arr[0-7]和arr[8-15]归并为有序的新数组。
自顶向下的归并排序代码:
def merge(low,mid,high):
i=low
j=mid+1 for k in range(low,high+1):
aux[k]=arr[k] for k in range(low,high+1):
if i>mid:
arr[k]=aux[j]
j=j+1
elif j>high:
arr[k]=aux[i]
i=i+1
elif aux[i]<aux[j]:
arr[k]=aux[i]
i=i+1
else:
arr[k]=aux[j]
j=j+1 def recurisonSort(low,high):
if low<high:
mid=math.floor(low+(high-low)/2)
recurisonSort(low,mid)
recurisonSort(mid+1,high)
merge(low,mid,high) def mergeSort(_arr):
global aux
aux=[0]*len(_arr) global arr
arr=[]
for i in _arr:
arr.append(i) recurisonSort(0,len(arr)-1)
return arr
对于长度为N的任意数组,自顶向下的归并排序需要1/2NlgN-NlgN次比较,最多需要访问数组6NlgN次。
自底向上:先归并微型数组,然后在成对归并得到的子数组。即首先进行的是两两归并,然后是四四归并,进而八八归并,一直下去。这种实现方法比标准递归方法所需要的代码量更少。
自底向上归并排序代码:
def mergeSort2(_arr):
length=len(_arr)
global aux
aux=[0]*length global arr
arr=[]
for j in _arr:
arr.append(j) i=1
while(i<length):
for k in range(0,length-i,2*i):
high=min(k+2*i-1,length-1)
if k<high:
merge(k,k+i-1,high)
i=i*2 print(arr)
(五)快速排序
分而治之(divide and conquer, D&C)的思想:1.找出简单的基线条件;2.确定如何缩小问题的规模,使其符合基线条件。
那么将D&C思想应用于排序任务中,其思路应如下:
基线条件就是只有一个元素的数组,这样的数组顺序就是自己。在数组中任取一个元素作为基准值,那么该数组将会被划分为三部分
小于基准值的子数组 + 基准值 + 大于基准值的子数组
这样就会不断地缩小数组的规模,直到只剩一个元素为止。
快速排序的代码:
>>> def quicksort(arr):
if len(arr) < 2:
return arr
else:
pivot = arr[0]
less = [i for i in arr[1:] if i <= pivot]
greater = [i for i in arr[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater) >>> arr = [3,5,1,9,7]
>>> quicksort(arr)
[1, 3, 5, 7, 9]
>>>
其实,排序的方法已经包含在各种语言中了,比如Python和C#都是使用Sort方法,就可以对一个数组进行从小到大的排序了。不过了解算法的本质应该也不是什么坏事吧。
(未完待续)
N种排序算法的更多相关文章
- 几种排序算法的学习,利用Python和C实现
之前学过的都忘了,也没好好做过总结,现在总结一下. 时间复杂度和空间复杂度的概念: 1.空间复杂度:是程序运行所以需要的额外消耗存储空间,一般的递归算法就要有o(n)的空间复杂度了,简单说就是递归集算 ...
- 秒杀9种排序算法(JavaScript版)
一:你必须知道的 1> JS原型 2> 排序中的有序区和无序区 3> 二叉树的基本知识 如果你不知道上面三个东西,还是去复习一下吧,否则,看下面的东西有点吃力. 二:封装丑陋的原型方 ...
- PHP的几种排序算法的比较
这里列出了几种PHP的排序算法的时间比较的结果,,希望对大家有所帮助 /* * php 四种排序算法的时间与内置的sort排序比较 * 3000个元素,四种算法的排序所用的时间比较 * 冒泡排序 85 ...
- 学习Java绝对要懂的,Java编程中最常用的几种排序算法!
今天给大家分享一下Java中几种常见的排序算法的Java代码 推荐一下我的Java学习羊君前616,中959,最后444.把数字串联起来! ,群里有免费的学习视频和项目给大家练手.大神有空时也 ...
- C#常用8种排序算法实现以及原理简介
public static class SortExtention { #region 冒泡排序 /* * 已知一组无序数据a[1].a[2].--a[n],需将其按升序排列.首先比较a[1]与a[2 ...
- 排序—时间复杂度为O(n2)的三种排序算法
1 如何评价.分析一个排序算法? 很多语言.数据库都已经封装了关于排序算法的实现代码.所以我们学习排序算法目的更多的不是为了去实现这些代码,而是灵活的应用这些算法和解决更为复杂的问题,所以更重要的是学 ...
- java算法03 - 常用的8种排序算法
Java常用的八种排序算法: 插入排序 - 直接插入排序 每次将待排序的记录按照关键字的大小,插入到前面已经排好序的记录的适当位置.直到全部记录插入完成. 代码实现 /** * 直接插入排序 O(n^ ...
- 用 C 语言描述几种排序算法
排序算法是最基本且重要的一类算法,本文基于 VS2017,使用 C 语言来实现一些基本的排序算法. 一.选择排序 选择排序,先找到数组中最小的元素,然后将这个元素与数组的第一个元素位置互换(如果第一个 ...
- 【C++】四种排序算法的时间比较
四种排序算法的时间比较 [注]clock函数对输入(用户输入)元素N排序的计时 #include<iostream> #include<time.h> using namesp ...
- 几种排序算法及Java实现排序的几种方式
几种排序算法 下面的例子介绍了4种排序方法: 冒泡排序, 选择排序, 插入排序, 快速排序 package date201709.date20170915; public class SortUtil ...
随机推荐
- dubbo系列一、dubbo启动流程
目录 dubbo启动流程分析记录 一.dubbo provider启动流程 1.自动装配 2.ServiceBean处理 3.服务暴露export() 3.1.检测dubbo.xxx.配置属性,配置到 ...
- vue体验
1.创建工作区 2.点击添加文件 3.新建一个html文件,并输入!+tab 自动生成html结构 4.输入div#app,生成头部 <!DOCTYPE html> <html l ...
- 带你学习BFS最小步数模型
最小步数模型 一.简介 最小步数模型和最短路模型的区别? 最短路模型:某一个点到另一个点的最短距离(坐标与坐标之间) 最小步数模型:不再是点(坐标),而是状态到另一个状态的转变 BFS难点所在(最短路 ...
- 学习AJAX必知必会(3)~自动重启工具nodemon、缓存问题、请求超时和网络异常、取消重复请求
1.nodemon 自动重启工具(自动重启基于nodejs开发的服务端应用) ■ nodemon 是一个工具,通过在检测到目录中的文件更改时自动重新启动node应用程序来帮助开发node.js. // ...
- Tomcat-IDEA整合Tomcat服务器
Tomcat(IDEA整合Tomcat服务器) 可以加多个版本tomcat
- IoC容器-Bean管理XML方式(注入空值和特殊符号)
Ioc操作Bean管理(xml注入其他类型属性), 字面量 (1)null值 (2)属性值包含特殊符号
- 【Vulnhub靶场】JANGOW: 1.0.1
时隔这么久,终于开始做题了 环境准备 下载靶机,导入到virtualBox里面,这应该不用教了吧 开机可以看到,他已经给出了靶机的IP地址,就不用我们自己去探测了 攻击机IP地址为:192.168.2 ...
- Python 使用 Windows10 桌面通知
前言 Win10 没有提供简单命令行方式来触发桌面通知,所以使用 Python 来写通知脚本. 一番搜索,找到 win10toast .但这开源仓库已无人维护,通过 github fork 的关系图, ...
- jsp FN 标签库的使用方法
1. 在jsp 导入标签库 <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="f ...
- 深入理解F1-score
本博客的截图均来自zeya的post:Essential Things You Need to Know About F1-Score | by Zeya | Towards Data Science ...