目录
一、排序算法比较
名词解释:
- n:数据规模。
- k:"桶"的个数。
- In-place:占用常数内存,不占用额外内存。
- Out-place:占用额外内存。
- 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同。
二、利用python实现排序算法
2.1 冒泡排序
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个;
-
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数;
-
针对所有的元素重复以上的步骤,除了最后一个;
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2. 动态演示
3. python实现
def bubble_sort(list):
n = len(list)
for i in range(n):
for j in range(1, n-i): # 每一次冒泡将最大的数交换到数列的最后一位
if list[j-1] > list[j]:
list[j-1], list[j] = list[j], list[j-1]
return list
2.2 选择排序
1. 算法步骤
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置;
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾;
- 重复第二步,直到所有元素均排序完毕。
2. 动态演示
3. python实现
def select_Sort(list):
n = len(list)
for i in range(n - 1):
# 记录最小数的索引
minIndex = i
for j in range(i+1, n):
if list[j] < list[minIndex]:
minIndex = j
# i 不是最小数时,将 i 和最小数进行交换
if i != minIndex:
list[i], list[minIndex] = list[minIndex], list[i]
return list
2.3 插入排序
1. 算法步骤
- 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列;
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
2. 动态演示
3. python实现
def insert_Sort(list):
n = len(list)
for i in range(n):
preIndex = i-1
current = list[i]
while preIndex >= 0 and list[preIndex] > current:
list[preIndex+1] = list[preIndex]
preIndex -= 1
list[preIndex+1] = current
return list
2.4 希尔排序
1. 算法步骤
- 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
- 按增量序列个数 k,对序列进行 k 趟排序;
- 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
2. 动图演示
3. python实现
def shell_Sort(list):
import math
n = len(list)
gap = 1
while(gap < n/3):
gap = gap*3 + 1
while gap > 0:
for i in range(gap, n):
temp = list[i]
j = i - gap
while j >=0 and list[j] > temp:
list[j+gap] = list[j]
j -= gap
list[j+gap] = temp
gap = math.floor(gap/3)
return list
2.5 归并排序
1. 算法步骤
-
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
-
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
-
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
-
重复步骤 3 直到某一指针达到序列尾;
-
将另一序列剩下的所有元素直接复制到合并序列尾。
2. 动图演示
3. python实现
def merge_Sort(list):
import math
n = len(list)
if(n < 2):
return list
middle = math.floor(n / 2) # 向下取舍
left, right = list[0:middle], list[middle:]
return merge(merge_Sort(left), merge_Sort(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
2.6 快速排序
1. 算法步骤
-
从数列中挑出一个元素,称为 "基准"(pivot);
-
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
-
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
2. 动图演示
3. python实现
def quick_Sort(list, left=None, right=None):
n = len(list)
left = 0 if not isinstance(left,(int, float)) else left
right = n-1 if not isinstance(right,(int, float)) else right
if left < right:
partitionIndex = partition(list, left, right)
quick_Sort(list, left, partitionIndex-1)
quick_Sort(list, partitionIndex+1, right)
return list
def partition(list, left, right):
pivot = left
index = pivot + 1
i = index
while i <= right:
if list[i] < list[pivot]:
list[i], list[index] = list[index], list[i]
index += 1
i += 1
list[pivot], list[index-1] = list[index-1], list[pivot]
return index - 1
2.7 堆排序
1. 算法步骤
-
创建一个堆 H[0……n-1];
-
把堆首(最大值)和堆尾互换;
-
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
-
重复步骤 2,直到堆的尺寸为 1。
2. 动图演示
3. python实现
def heap_Sort(list):
global arrLen
arrLen = len(list)
buildMaxHeap(list)
for i in range(len(list)-1, 0, -1):
swap(list, 0, i)
arrLen -= 1
heapify(list, 0)
return list
def buildMaxHeap(list):
import math
for i in range(math.floor(len(list)/2), -1, -1):
heapify(list, i)
def heapify(list, i):
left = 2*i + 1
right = 2*i + 2
largest = i
if left < arrLen and list[left] > list[largest]:
largest = left
if right < arrLen and list[right] > list[largest]:
largest = right
if largest != i:
swap(list, i, largest)
heapify(list, largest)
def swap(list, i, j):
list[i], list[j] = list[j], list[i]
2.8 计数排序
1. 算法步骤
- 找出待排序的数组中最大和最小的元素;
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
2. 动图演示
3. python实现
def count_Sort(list, maxValue):
bucketLen = maxValue + 1
bucket = [0]*bucketLen
sortedIndex =0
n = len(list)
for i in range(n):
if not bucket[list[i]]:
bucket[list[i]] = 0
bucket[list[i]] += 1
for j in range(bucketLen):
while bucket[j] > 0:
list[sortedIndex] = j
sortedIndex += 1
bucket[j] -= 1
return list
2.9 桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
- 在额外空间充足的情况下,尽量增大桶的数量;
- 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中。
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
1. 算法思想
- 把数组A划分为n个大小相同的区间(即桶),每个子区间各自排序,最后合并。
- 桶排序要求数据的分布必须均匀,否则可能会失效。
- 计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。
2. 示意图
元素分布在桶中:
然后,元素在每个桶中排序:
3. python实现
def bucket_sort(list):
buckets = [0] * ((max(list) - min(list)) + 1) # 初始化桶元素为0
for i in range(len(list)):
buckets[list[i] - min(list)] += 1 # 遍历数组a,在桶的相应位置累加值
a = []
for i in range(len(buckets)):
if buckets[i] != 0:
a += [i + min(list)] * buckets[i]
return a
2.10 基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
1. 基数排序 vs 计数排序 vs 桶排序
- 基数排序:根据键值的每位数字来分配桶;
- 计数排序:每个桶只存储单一键值;
- 桶排序:每个桶存储一定范围的数值;
2. 动图演示
3. 代码实现
def radix_sort(list, d=3): # 默认三位数,如果是四位数,则d=4,以此类推
for i in range(d): # d轮排序
s = [[] for k in range(10)] # 因每一位数字都是0~9,建10个桶
for j in list:
s[int(j / (10 ** i)) % 10].append(j)
result = [a for b in s for a in b]
return result