python数组排序的⽅法
排序算法是《数据结构与算法》中最基本的算法之⼀。
排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进⾏排序,⽽外部排序是因排序的数据很⼤,⼀次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插⼊排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。⽤⼀张图概括:
关于时间复杂度:
1. 平⽅阶 (O(n2)) 排序各类简单排序:直接插⼊、直接选择和冒泡排序。
2. 线性对数阶 (O(nlog2n)) 排序快速排序、堆排序和归并排序;
3. O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。希尔排序
4. 线性阶 (O(n)) 排序基数排序,此外还有桶、箱排序。
关于稳定性:
排序后 2 个相等键值的顺序和排序之前它们的顺序相同
稳定的排序算法:冒泡排序、插⼊排序、归并排序和基数排序。
不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。
名词解释:
n:数据规模k:“桶”的个数In-place:占⽤常数内存,不占⽤额外内存Out-place:占⽤额外内存
1、冒泡排序
冒泡排序(Bubble Sort)也是⼀种简单直观的排序算法。它重复地⾛访过要排序的数列,⼀次⽐较两个元素,如果他们的顺序错误就把他们交换过来。⾛访数列的⼯作是重复地进⾏直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越⼩的元素会经由交换慢慢“浮”到数列的顶端。
作为最简单的排序算法之⼀,冒泡排序给我的感觉就像 Abandon 在单词书⾥出现的感觉⼀样,每次都在第⼀页第⼀位,所以最熟悉。冒泡排序还有⼀种优化算法,就是⽴⼀个flag,当在⼀趟序列遍历中元素没有发⽣交换,则证明该序列已经有序。但这种改进对于提升性能来说并没有什么太⼤作⽤。
(1)算法步骤
1. ⽐较相邻的元素。如果第⼀个⽐第⼆个⼤,就交换他们两个。
2. 对每⼀对相邻元素作同样的⼯作,从开始第⼀对到结尾的最后⼀对。这步做完后,最后的元素会是最⼤的数。
3. 针对所有的元素重复以上的步骤,除了最后⼀个。
4. 持续每次对越来越少的元素重复上⾯的步骤,直到没有任何⼀对数字需要⽐较。
(2)动图演⽰
(3)Python 代码
def bubbleSort(arr):
for i in range(1, len(arr)):
for j in range(0, len(arr)-i):
if arr[j] > arr[j+1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
2、选择排序
选择排序是⼀种简单直观的排序算法,⽆论什么数据进去都是 O(n²) 的时间复杂度。所以⽤到它的时候,数据规模越⼩越好。唯⼀的好处可能就是不占⽤额外的内存空间了吧。(1)算法步骤
1. ⾸先在未排序序列中到最⼩(⼤)元素,存放到排序序列的起始位置
2. 再从剩余未排序元素中继续寻最⼩(⼤)元素,然后放到已排序序列的末尾。
3. 重复第⼆步,直到所有元素均排序完毕。
(2)动图演⽰
(3)Python 代码
def selectionSort(arr):
for i in range(len(arr) - 1):
# 记录最⼩数的索引
minIndex = i
for j in range(i + 1, len(arr)):
if arr[j] < arr[minIndex]:
minIndex = j
# i 不是最⼩数时,将 i 和最⼩数进⾏交换
if i != minIndex:
arr[i], arr[minIndex] = arr[minIndex], arr[i]
return arr
3、插⼊排序
插⼊排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的⼈都应该能够秒懂。插⼊排序是⼀种最简单直观的排序算法,它的⼯作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,到相应位置并插⼊。
插⼊排序和冒泡排序⼀样,也有⼀种优化算法,叫做拆半插⼊。
(1)算法步骤
1. 将第⼀待排序序列第⼀个元素看做⼀个有序序列,把第⼆个元素到最后⼀个元素当成是未排序序列。
2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插⼊有序序列的适当位置。(如果待插⼊的元素与有序序列中的某个元素相等,则将待插⼊元素插⼊到相等元素的后
⾯。)
(2)动图演⽰
(3)Python 代码
def insertionSort(arr):
for i in range(len(arr)):
preIndex = i-1
current = arr[i]
while preIndex >= 0 and arr[preIndex] > current:
arr[preIndex+1] = arr[preIndex]
preIndex-=1
arr[preIndex+1] = current
return arr
4、希尔排序
希尔排序,也称递减增量排序算法,是插⼊排序的⼀种更⾼效的改进版本。但希尔排序是⾮稳定排序算法。
希尔排序是基于插⼊排序的以下两点性质⽽提出改进⽅法的:
插⼊排序在对⼏乎已经排好序的数据操作时,效率⾼,即可以达到线性排序的效率;
但插⼊排序⼀般来说是低效的,因为插⼊排序每次只能将数据移动⼀位;
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若⼲⼦序列分别进⾏直接插⼊排序,待整个序列中的记录“基本有序”时,再对全体记录进⾏依次直接插⼊排序。(1)算法步骤
1. 选择⼀个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
2. 按增量序列个数 k,对序列进⾏ k 趟排序;
3. 每趟排序,根据对应的增量 ti,将待排序列分割成若⼲长度为 m 的⼦序列,分别对各⼦表进⾏直接插⼊排序。仅增量因⼦为 1 时,整个序列作为⼀个表来处理,表长度即
为整个序列的长度。
()Python 代码
def shellSort(arr):
import math
gap=1
while(gap < len(arr)/3):
gap = gap*3+1
while gap > 0:
for i in range(gap,len(arr)):
temp = arr[i]
j = i-gap
while j >=0 and arr[j] > temp:
arr[j+gap]=arr[j]
j-=gap
arr[j+gap] = temp
gap = math.floor(gap/3)
return arr
}
5、归并排序
归并排序(Merge sort)是建⽴在归并操作上的⼀种有效的排序算法。该算法是采⽤分治法(Divide and Conquer)的⼀个⾮常典型的应⽤。
作为⼀种典型的分⽽治之思想的算法应⽤,归并排序的实现由两种⽅法:
⾃上⽽下的递归(所有递归的⽅法都可以⽤迭代重写,所以就有了第 2 种⽅法);
⾃下⽽上的迭代;
和选择排序⼀样,归并排序的性能不受输⼊数据的影响,但表现⽐选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。
(1)算法步骤
1. 申请空间,使其⼤⼩为两个已经排序序列之和,该空间⽤来存放合并后的序列;
2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
3. ⽐较两个指针所指向的元素,选择相对⼩的元素放⼊到合并空间,并移动指针到下⼀位置;
4. 重复步骤 3 直到某⼀指针达到序列尾;
5. 将另⼀序列剩下的所有元素直接复制到合并序列尾。
(2)动图演⽰
(3)Python 代码
def mergeSort(arr):
import math
if(len(arr)<2):
return arr
middle = math.floor(len(arr)/2)
left, right = arr[0:middle], arr[middle:]
return merge(mergeSort(left), mergeSort(right))
def merge(left,right):
result = []
while left and right:
if left[0] <= right[0]:
result.append(left.pop(0));
else:
result.append(right.pop(0));
while left:
result.append(left.pop(0));
while right:
result.append(right.pop(0));
return result
6、快速排序
快速排序是由东尼·霍尔所发展的⼀种排序算法。在平均状况下,排序 n 个项⽬要Ο(nlogn) 次⽐较。在最坏状况下则需要Ο(n2) 次⽐较,但这种状况并不常见。事实上,快速排序通常明显⽐其他Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在⼤部分的架构上很有效率地被实现出来。
快速排序使⽤分治法(Divide and conquer)策略来把⼀个串⾏(list)分为两个⼦串⾏(sub-lists)。
快速排序⼜是⼀种分⽽治之思想在排序算法上的典型应⽤。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
快速排序的名字起的是简单粗暴,因为⼀听到这个名字你就知道它存在的意义,就是快,⽽且效率⾼!它是处理⼤数据最快的排序算法之⼀了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是⼈家就是优秀,在⼤多数情况下都⽐平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症⼜犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上到了满意的答案:
快速排序的最坏运⾏情况是 O(n²),⽐如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因⼦很⼩,⽐复杂度稳定等于 O(nlogn)的归并排序要⼩很多。所以,对绝⼤多数顺序性较弱的随机数列⽽⾔,快速排序总是优于归并排序。
(1)算法步骤
1. 从数列中挑出⼀个元素,称为 “基准”(pivot);
2. 重新排序数列,所有元素⽐基准值⼩的摆放在基准前⾯,所有元素⽐基准值⼤的摆在基准的后⾯(
相同的数可以到任⼀边)。在这个分区退出之后,该基准就处于数列的中
间位置。这个称为分区(partition)操作;
3. 递归地(recursive)把⼩于基准值元素的⼦数列和⼤于基准值元素的⼦数列排序;
递归的最底部情形,是数列的⼤⼩是零或⼀,也就是永远都已经被排序好了。虽然⼀直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它⾄少会把⼀个元素摆到它最后的位置去。
(2)动图演⽰
(3)Python 代码
def quickSort(arr, left=None, right=None):
left = 0 if not isinstance(left,(int, float)) else left
right = len(arr)-1 if not isinstance(right,(int, float)) else right
if left < right:
partitionIndex = partition(arr, left, right)
quickSort(arr, left, partitionIndex-1)
quickSort(arr, partitionIndex+1, right)
return arr
def partition(arr, left, right):
pivot = left
index = pivot+1
i = index
while i <= right:
快速排序python实现if arr[i] < arr[pivot]:
swap(arr, i, index)
index+=1
i+=1
swap(arr,pivot,index-1)
return index-1
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
7、堆排序
堆排序(Heapsort)是指利⽤堆这种数据结构所设计的⼀种排序算法。堆积是⼀个近似完全⼆叉树的结构,并同时满⾜堆积的性质:即⼦结点的键值或索引总是⼩于(或者⼤于)它的⽗节点。堆排序可以说是⼀种利⽤堆的概念来排序的选择排序。分为两种⽅法:
1. ⼤顶堆:每个节点的值都⼤于或等于其⼦节点的值,在堆排序算法中⽤于升序排列;
2. ⼩顶堆:每个节点的值都⼩于或等于其⼦节点的值,在堆排序算法中⽤于降序排列;
堆排序的平均时间复杂度为Ο(nlogn)。
(1)算法步骤
1. 创建⼀个堆 H[0……n-1];
2. 把堆⾸(最⼤值)和堆尾互换;
3. 把堆的尺⼨缩⼩ 1,并调⽤ shift_down(0),⽬的是把新的数组顶端数据调整到相应位置;
4. 重复步骤 2,直到堆的尺⼨为 1。
(2)动图演⽰
(3)Python 代码
def buildMaxHeap(arr):
import math
for i in range(math.floor(len(arr)/2),-1,-1):
heapify(arr,i)
def heapify(arr, i):
left = 2*i+1
right = 2*i+2
largest = i
if left < arrLen and arr[left] > arr[largest]:
largest = left
if right < arrLen and arr[right] > arr[largest]:
largest = right
if largest != i:
swap(arr, i, largest)
heapify(arr, largest)
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
def heapSort(arr):
global arrLen
arrLen = len(arr)
buildMaxHeap(arr)
for i in range(len(arr)-1,0,-1):
swap(arr,0,i)
arrLen -=1
heapify(arr, 0)
return arr
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论