目录
复杂度为O(n²)的排序
1.冒泡排序:
2.选择排序:
3.插入排序:
复杂度为O(nlogn)的排序
4.希尔排序:
5.归并排序:
6.快排:
7.堆排:
复杂度为O(n+k)的排序
8.计数排序:
9.桶排序:
复杂度为O(n*k)的排序
10.基数排序:
各种排序的算法复杂度如下:
各个算法具体的排序逻辑可视化图可见:https://visualgo.net/zh
时间复杂度:O(n²),冒泡排序相对比较稳定
def bs(L):
for i in range(len(L)):
for j in range(i,len(L)):
if L[i] > L[j]:
L[i],L[j] = L[j],L[i]
return L
时间复杂度:O(n²),选择排序相对来说不太稳定,最好和最坏情况复杂度都是O(n²)
在进行选择排序的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
1. 算法步骤
默认序列起始位置的值为最小值min,并对起始位置之后的序列进行遍历
如果存在某个值小于min,则将此值定义为min,直到遍历到序列末尾
将起始位置的值和当前min的值进行对换,确保最小值始终在起始位置,起始位置+1
重复1-3步,直到结束。
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
时间复杂度:O(n²)
1. 算法步骤
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
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
关于希尔排序的解释可以看这篇文章:https://blog.csdn.net/qq_39207948/article/details/80006224
希尔排序是一种优化的插入排序,对于数据量较大且比较无序的数组效果较好。排序不需要外部空间,不稳定。
它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高
def shell_sort(arr):
n = len(arr)
gap = n//2 #这里的代码显示了分成两组的情况,分成多组情况修改数字2
while gap > 0:
for i in range(gap, n):
while i >= gap and arr[i] < alist[i - gap]:
arr[i], arr[i - gap] = arr[i - gap], arr[i]
i -= gap
#print(arr)
gap //= 2
return arr
(照搬了这篇文章https://blog.csdn.net/perfer258/article/details/81985349)
归并思想其实是依据了一种二分的思维。在进行排序后合并时,有点像合并两个有序数组。
时间复杂度:O(n log n),空间复杂度:O(n),排序需要外部空间,较为稳定
算法逻辑:
1.确定middle划分左右子集(递归思想);
2.对比左右子集的值,进行左右子集的排序合并,并对arr的相应序列进行改变;
3.返回排序的数组
def merge_sort(arr):
sort(arr, 0, len(arr)-1)
def sort(arr, low, high):
if low < high:
mid = (low + high) // 2
sort(arr, low, mid)
sort(arr, mid+1, high)
merge(arr, low, mid, high)
def merge(arr, low, mid, high):
container = [] # 用于存储有序数字
i, j = low, mid+1
while i <= mid and j <= high:
if arr[i] <= arr[j]:
container.append(arr[i])
i += 1
else:
container.append(arr[j])
j += 1
if i <= mid:
container.extend(arr[i:mid+1])
elif j <= high:
container.extend(arr[j:high+1])
arr[low:high+1] = container
arr = merge_sort(arr)
时间复杂度:O (nlogn)
快排的算法中其实包含了一种二分的思维在里面,因此可以用递归的方式简化
def qs(L):
# 终止条件必须要有
if L == []: return []
axis = L[0]
less = qs([i for i in L[1:] if i < axis])
more = qs([j for j in L[1:] if j >= axis])
#axis外加[]
return less + [axis] + more
时间复杂度:O (nlogn),空间复杂度:O(1),不稳定。
#根结点和最右最下侧节点进行替换
def adjust_heap(l, parent):
length = len(l)
parent_value = l[parent]
child = parent * 2 + 1
while child < length:
if child + 1 < length and l[child + 1] > l[child]:
child += 1
if l[child] <= parent_value:
break
l[parent] = l[child]
parent = child
child = 2 * parent + 1
l[parent] = parent_value
def heap_sort(l, result=None):
if len(l) == 1:
result.append(l[0])
return l
if result is None:
result = []
parents = [i for i in range(len(l))]
parents.reverse()
for parent in parents:
adjust_heap(l, parent)
l[0],l[len(l) - 1] = l[len(l) - 1],l[0]
result.append(l.pop())
heap_sort(l, result)
return result
l = [99,7, 4, 5, 3, 6, 9, 7, 8, 0]
s = heap_sort(l)
时间复杂度:O (n+k),空间复杂度:O(k),稳定算法。
计数排序的逻辑其实非常简单,就是构建一个maxValue+1长度的列表,初始化值均为0。此列表的索引代表值,列表的值代表每个值出现的次数。统计完成后,循环更新原列表的值即可。
def countingSort(arr, maxValue):
bucketLen = maxValue+1
bucket = [0]*bucketLen
sortedIndex =0
arrLen = len(arr)
for i in range(arrLen):
# if not bucket[arr[i]]:
# bucket[arr[i]]=0
bucket[arr[i]]+=1
for j in range(bucketLen):
while bucket[j]>0:
arr[sortedIndex] = j
sortedIndex+=1
bucket[j]-=1
return arr
桶排序是计数排序的升级版(https://www.jianshu.com/p/204ed43aec0c)。不同于计数排序的是,桶排序是基于映射关系生成序列,而不是一个长度为maxValue的序列
它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
在额外空间充足的情况下,尽量增大桶的数量
使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
1. 什么时候最快
当输入的数据可以均匀的分配到每一个桶中。
2. 什么时候最慢
当输入的数据被分配到了同一个桶中。
算法示例:
def bucketSort(arr):
maximum, minimum = max(arr), min(arr)
bucketArr = [[] for i in range(maximum // 10 - minimum // 10 + 1)] # set the map rule and apply for space
for i in arr: # map every element in array to the corresponding bucket
index = i // 10 - minimum // 10
bucketArr[index].append(i)
arr.clear()
for i in bucketArr:
heapSort(i) # sort the elements in every bucket
arr.extend(i) # move the sorted elements in bucket to array
基数排序是按照数字的位数进行排序的,从个位到十位,百位依次进行排序。
为了更直观的理解,可以参见其可视化演示图:https://visualgo.net/zh/sorting
def RadixSort(a):
i = 0 #初始为个位排序
n = 1 #最小的位数置为1(包含0)
max_num = max(a) #得到带排序数组中最大数
while max_num > 10**n: #得到最大数是几位数
n += 1
while i < n:
bucket = {} #用字典构建桶
for x in range(10):
bucket.setdefault(x, []) #将每个桶置空
for x in a: #对每一位进行排序
radix =int((x / (10**i)) % 10) #得到每位的基数
bucket[radix].append(x) #将对应的数组元素加入到相应位基数的桶中
j = 0
for k in range(10):
if len(bucket[k]) != 0: #若桶不为空
for y in bucket[k]: #将该桶中每个元素
a[j] = y #放回到数组中
j += 1
i += 1