1、插入排序
插入排序的思想是由后往前插入,选择合适的位置插入之后,进行下一个操作。
稳定算法,时间复杂度O(n^2),空间复杂度O(1)。图源百度
def insert_sort(nums):
for i in range(len(nums)):
tmp = nums[i]
j = i
# 如果当前位数字比前位数字小,将前位数字后移
while j >=1 and nums[j-1] > tmp:
nums[j] = nums[j-1]
j -= 1
# 此时j之前的位置都小于当前位数字,j之后的位置都大于当前位数字,将数字插入
nums[j] = tmp
return nums
2、希尔排序
希尔排序和插入排序相似,只不过是插入排序每次只迈一步,希尔排序每次迈一大步,然后步子变小,最后步子变成1。
不稳定算法,时间复杂度O(n^1.3-2),空间复杂度O(1)。图源百度
def shell_sort(nums):
gap = len(nums)//2
while gap>0:
# 每次走一大步,进行插入排序
for i in range(gap,len(nums)):
tmp = nums[i]
j = i
while j >= gap and nums[j-gap] > tmp:
nums[j] = nums[j-gap]
j -= gap
nums[j] = tmp
# 步数变小,直至1
gap = gap//2
return nums
3、基数排序
基数排序是非比较的排序方法,先根据个位数插入到对应的桶中,再展开;然后根据十位数再插入到桶中,再展开;直到最高位。注意只能用于自然数的排序,毕竟有小数了,你没法插到桶里去啊!自己画一个图!
稳定排序,时间复杂度O((n+r)*k),空间复杂度O(r×n)。(n个数,r个桶,最大k位数)
def radix_sort(nums):
max_len = len(str(max(nums)))
for i in range(max_len):
# 分配10个桶
bucket_list = [[] for _ in range(10)]
for j in nums:
# 放到每个桶中
bucket_list[j//(10**i)%10].append(j)
# 展开
nums = [q for p in bucket_list for q in p]
return nums
4、冒泡排序
冒泡排序是每次找到最大(或最小)的数字,放在开头或者结尾。之所以叫冒泡的意思是泡泡是逐渐变大的过程。
稳定排序,时间复杂度O(n^2),空间复杂度O(1)
def bubble_sort(nums):
for i in range(len(nums)):
for j in range(1,len(nums)-i):
# 找到大的数字然后交换
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
5、归并排序
归并排序是分治的思想,将序列拆成两份、四份、八份,,,最后直到长度为1的有序数列,然后再进行合并。
稳定排序,复杂度O(nlogn),空间复杂度O(n)。
def merge(nums,l,mid,r):
# 分配空间
tmp = [0]*(r-l+1)
k = 0
i = l
j = mid+1
# 合并两个有序数列
while i<=mid and j<=r:
if nums[i] < nums[j]:
tmp[k] = nums[i]
k += 1
i += 1
else:
tmp[k] = nums[j]
k += 1
j += 1
# 如果左边数列未合并完
while i <= mid:
tmp[k] = nums[i]
k += 1
i += 1
# 如果右边数列未合并完
while j <= r:
tmp[k] = nums[j]
k += 1
j += 1
nums[l:r+1] = tmp[:]
return nums
def merge_sort(nums,l,r):
if l < r:
# 找到断开点
mid = int(l+r)//2
# 递归
nums = merge_sort(nums,l,mid)
nums = merge_sort(nums,mid+1,r)
# 合并
nums = merge(nums,l,mid,r)
return nums
6、堆排序
堆排序是利用二叉树,将大的数字放在根结点(大顶堆),每次取出根结点之后重新调整堆,直至堆为空。
不稳定排序,时间复杂度O(nlogn),建堆时间O(n),空间复杂度O(1)
def heapify(nums,n,i):
large = i
l = 2 * i + 1 # 左节点
r = 2 * i + 2 # 右节点
if l < n and nums[large] < nums[l]: # 往左节点走
large = l
if r < n and nums[large] < nums[r]: # 往右节点走
large = r
if large != i:
# 调整根结点和子节点的大小
nums[i],nums[large] = nums[large],nums[i]
# 当跟节点的值变化时,调整改变节点的堆(不一定是大顶堆)
heapify(nums,n,large)
def heap_sort(nums):
n = len(nums)
for i in range(n,-1,-1):
# n是长度,i是节点序号,保证每一个节点都是大顶堆
heapify(nums,n,i)
# 最大数在根结点
for i in range(n-1,-1,-1):
# 取出根结点之后放在最后的位置,将最后位置值调到根结点
nums[i],nums[0] = nums[0],nums[i]
# 调整堆
heapify(nums,i,0)
return nums
7、桶排序
桶排序就是将数据划分成几个大小区间,然后再进行排序,可以同其它排序想结合。
稳定性取决于桶内算法的稳定性,空间复杂度和时间复杂度同样受桶内排序算法的影响。
def bucket_sort(nums):
max_num = nums[0]
min_num = nums[0]
for i in nums:
if max_num < i:
max_num = i
if min_num > i:
min_num = i
# 根据最大值、最小值划分桶的个数
bucket_num = int((max_num - min_num)/len(nums)) + 1
bucket = [ [] for _ in range(bucket_num)]
for i in range(len(nums)):
bucket[int((nums[i] - min_num)/len(nums))].append(nums[i])
# 在每个桶内进行排序
for i in range(bucket_num):
# 偷懒,直接sort排序
bucket[i].sort()
k = 0
for i in range(bucket_num):
for val in bucket[i]:
nums[k] = val
k += 1
return nums
8、快速排序
快排是思路是将小于当前位的数字放在左边,大于当前位的数字放在右边,是比较算法里面最高效的算法。
不稳定、时间复杂度O(nlogn),空间复杂度O(1)。图源百度
def quick_sort(nums,left,right):
if left >= right:
return nums
# 取左边作为当前位的值
tmp = nums[left]
i = left
j = right
while i!=j:
# 如果右边的值比当前位大,右指针左移
while i<j and nums[j] >= tmp:
j -= 1
# 当右边的值比当前位小的时候,交换值
nums[i],nums[j] = nums[j],nums[i]
# 如果左边的值比当前位小,左指针右移
while i<j and nums[i] <= tmp:
i += 1
# 当左边的值比当前位大的时候,交换值
nums[i],nums[j] = nums[j],nums[i]
# 对左、右边进行排序
nums = quick_sort(nums,i+1,right)
nums = quick_sort(nums,left,i-1)
return nums
好了全部写完了,小伙伴们要记得动手哦!