排序
排序中python中,nums.sort()或者sorted()。sorted会更为灵活,目前经常对字典排序也是用sorted key = lambda…的办法进行。
排序的时间复杂O(nlgn)程序中内置的一般是快速排序与归并排序,都是使用了分治的思想。在这两个排序中,会混用插入排序。
还是先从基本的开始。
冒泡排序
def bubble(nums):
if len(nums) <= 1:
return nums
for i in range(len(nums)):
is_sort = True
for j in range(0,len(nums)- i - 1):
if nums[j] > nums[j+1]:
nums[j],nums[j+1] = nums[j+1],nums[j]
is_sort = False
if is_sort:
break
return nums
就是遍历数组,相邻两两比较,将最大的放到最后。
改进点:设置一个flag。如果遍历完是有序的,直接退出循环。
时间复杂
O
(
n
2
)
O(n^2)
O(n2)
选择排序
def selection(nums):
for i in range(len(nums)):
idx = len(nums) - i -1
for j in range(0,len(nums)-i):
if nums[j] > nums[idx]:
idx = j
nums[idx],nums[len(nums)-i-1] = nums[len(nums)-i-1] ,nums[idx]
return nums
也是遍历数组,找到最大的index,进行交换,放到最后。
时间复杂
O
(
n
2
)
O(n^2)
O(n2)
插入排序
def insert(nums):
n = len(nums)
if n ==0:
return nums
for i in range(1,n):
idx = i
while idx >= 1 and nums[idx] < nums[idx-1]:
nums[idx-1],nums[idx] = nums[idx],nums[idx-1]
idx -=1
return nums
想想打牌是如何摸牌理牌,每次抽一张后,插入原来摆好的顺序中。
时间复杂
O
(
n
2
)
O(n^2)
O(n2) 想想最坏情况,每次我都要把摸到的牌放到最前面。
希尔排序
def shell(nums):
n = len(nums)
gap = n
while gap > 0:
for i in range(gap,n,gap):
idx = i
while idx >= gap and nums[idx] < nums[idx-gap]:
nums[idx-gap],nums[idx] = nums[idx],nums[idx-gap]
idx -= gap
gap = gap // 2
return nums
对于子列进行插入排序
计数排序
def count(nums):
if len(nums) == 0:
return []
#找到最大最小
nmin,nmax = nums[0],nums[0]
for i in nums:
if i > nmax:nmax = i
if i < nmin:nmin = i
#创建放进抽屉
lens = nmax - nmin + 1
count = [0]* lens
for num in nums:
count[num-nmin] += 1
#从抽屉中取出
for i in range(lens):
for j in range(count[i]):
nums[i+j] = nmin + i
return nums
适用于最大最小值差距不是很大的情况。
想想抽屉,我创建m个抽屉,每个抽屉放相同的值。
时间复杂度
O
(
n
)
O(n)
O(n),空间复杂度
O
(
m
)
O(m)
O(m)
归并排序
def merge_(a,b):
n,m = len(a),len(b)
i,j = 0,0
new = []
while i < n and j <m:
if a[i] < b[j]:
new.append(a[i])
i += 1
else :
new.append(b[j])
j += 1
while i < n:
new.append(a[i])
i += 1
while j < m :
new.append(b[j])
j += 1
return new
def merge_sort(nums):
if len(nums) <= 1:
return nums
mid = len(nums)// 2
left = merge_sort(nums[:mid])
right = merge_sort(nums[mid:])
return merge_(left,right)
简单来说,分治我要排序整个数组,我就排序两部分,依次拆下去,然后再合并。
所以需要一个辅助函数。
主函数就是假设每次我排序完的left,right都是有序的,我只要把他们有序合并即可。(因为Base情况就是单个数组,肯定是有序的)
辅助函数可以改进写法:
def _merge(a: list, b: list) -> list:
"""Merge two sorted list"""
c = []
while len(a) > 0 and len(b) > 0:
if a[0] < b[0]:
c.append(a[0])
a.remove(a[0])
else:
c.append(b[0])
b.remove(b[0])
if len(a) == 0:
c += b
else:
c += a
return c
就是用数组来写,每次添加完就删去,最后就是数组合并。
快速排序
def pivot(nums,t):
n = len(nums)
left = [x for x in nums[:] if x < t]
right = [x for x in nums[:] if x >= t]
return left + [t] + right
def quick(nums):
if len(nums) <= 3:
return insert(nums) #达到一定小序列插入快速排序
# <= 1 return nums 是原版
t = nums[0]
left = quick([x for x in nums[1:] if x < t])
right = quick([x for x in nums[1:] if x >= t])
return left + [t] + right
快速排序就是每次把一个元素放到正确的位置(即左边都小于它,右边都大于等于它)然后两部分再次进行相同操作。
这个元素怎么选取?最简单就是第一个元素,或者就是 中间元素,头,尾取中间那个。
当base情况,也可以考虑用插入排序,比如长度小于3这样,长度小的插入排序是很快的。