冒泡排序
"""
O(n^2)
交换相邻元素,每轮都有一个最大的沉底
内部循环的每轮比较数量随着已经排好序的元素会减少
根据升序排序:
冒泡排序随着外部循环
数组左半部分待排序
数组右半部分有序
"""
def bubble_sort(nums):
for i in range(len(nums)-1):
# 每轮有一个最大的沉到底部,因此下一轮需要遍历的数量会减少一个
for j in range(1, len(nums)-i):
if nums[j-1] > nums[j]:
nums[j-1], nums[j] = nums[j], nums[j-1]
return
改进冒泡
"""
O(n^2)
当某一轮未进行元素交换,则停止冒泡
"""
def bubble_sort_exch(nums):
exch = True
i = 0
while i < len(nums) - 1 and exch:
exch = False
for j in range(1, len(nums)-i):
if nums[j-1] > nums[j]:
exch = True
nums[j-1], nums[j] = nums[j], nums[j-1]
i += 1
return
选择排序
"""
O(n^2)
左半部分有序
右半部分待排序
每轮挑选右半部分中最小的元素放入当前位置
"""
def choice_sort(nums):
for i in range(len(nums)-1):
min_index = i
for j in range(i+1, len(nums)):
if nums[min_index] > nums[j]:
min_index = j
nums[i], nums[min_index] = nums[min_index], nums[i]
return
插入排序
"""
O(n^2)
# 拿出当前位置的元素,插入到数组左半部分的正确位置
# 将当前元素暂存起来,左半部分元素大于当前元素时,将该元素右
"""
def insert_sort(nums):
for i in range(1, len(nums)):
j = i
current_num = nums[i]
while j > 0 and nums[j-1] > current_num: ###
nums[j] = nums[j-1]
j -= 1
nums[j] = current_num
return
希尔排序(间隔h插入)
def shell_sort(nums):
h = 1
while 3*h < len(nums):
h = 3 * h + 1
while h > 0:
# ===== 间隔为 h 的插入排序, 这段代码设定h为3 ====
for i in range(h, len(nums)):
tmp = nums[i]
j = i
while j > 0 and nums[j-h] > tmp:
nums[j] = nums[j-h]
j -= h
nums[j] = tmp
# ==============================
h //= 3
return
归并排序
分治
"""
O(nlogn)
一分为二,单独排序,排好序的进行归并,空间换时间
"""
def merge_sort(nums, low, high):
# terminator
if low >= high:
return
# prepare data, split subproblem
mid = low + (high - low) // 2
# drill down
merge_sort(nums, low, mid)
merge_sort(nums, mid+1, high)
# merge subresult
merge(nums, low, mid, high)
# reverse if needed
return
def merge(nums, lo, mid, hi):
# lo~mid 有序, mid+1~hi有序。合并有序数组
aux = [0] * len(nums)
aux[lo: hi+1] = nums[lo: hi+1]
i, j = lo, mid+1
for k in range(lo, hi+1):
if i > mid:
nums[k] = aux[j]
j += 1
elif j > hi:
nums[k] = aux[i]
i += 1
elif aux[i] > aux[j]:
nums[k] = aux[j]
j += 1
else:
nums[k] = aux[i]
i += 1
return
快速排序
递归
"""
# O(nlogn),当切分元素不好时,会倾向O(n2)
#
# 对于某个 j, alist[j]已经排定
# alist[low] 到 alist[j-1] 中所有元素 不大于 alist[j]
# alist[j+1] 到 alist[high] 中所有元素 不小于 alist[j]
# 在 初次调用快速排序 之前,可以对待排序数组进行shuffle,避免选取的切分元素不好
"""
def quick_sort(nums, low, high):
# terminator
if low >= high:
return
# process current level
divide = parition(nums, low, high)
# drill down
quick_sort(nums, low, divide-1)
quick_sort(nums, divide+1, high)
# reverse if needed
return
def parition(nums, low, high):
value = nums[low]
i, j = low+1, high
while i <= j:
# alist[low] 到 alist[j-1] 中所有元素 不大于 alist[j]
while i <= j and nums[i] <= value:
i += 1
# alist[j+1] 到 alist[high] 中所有元素 不小于 alist[j]
while i <= j and nums[j] >= value:
j -= 1
if i > j:
break
nums[i], nums[j] = nums[j], nums[i]
nums[j], nums[low] = nums[low], nums[j]
return
三切分快速排序(针对有重复元素)
def quick_sort_3way(nums, lo, hi):
# terminator
if lo >= hi:
return
# process current level
"""
# lt and gt 是 数组中重复元素的开始结束索引
# 通过使用索引 i 和 value比较
# 将小于 value 的值交换到数组左端
# 将大于 value 的值交换到数组右端
"""
lt, gt = lo, hi
i = lo + 1
value = nums[lo] # 切分元素
while i <= gt:
if nums[i] < value:
nums[i], nums[lt] = nums[lt], nums[i]
lt += 1
i += 1
elif nums[i] > value:
nums[i], nums[gt] = nums[gt], nums[i]
# 交换到i索引处的元素不一定小于value,因此i没有变化
gt -= 1
elif nums[i] == value:
i += 1
# drill down
quick_sort_3way(nums, lo, lt-1)
quick_sort_3way(nums, gt+1, hi)
# reverse if needed
return