def InsertSort(nums):
"""
空间复杂度:o(1)
时间复杂度:o(n^2)。折半插入排序的时间复杂度也是o(n^2),但对于数据量不大时性能比较好。
稳定性:由于每次是先比较再移动,所以不会产生相同元素相对位置变化的情况,所以是稳定的。
适用性:直接插入排序适用于顺序存储和链式存储的线性表。当为链式存储时,从后往前查找比较移动。(大部分排序仅适用于顺序存储)
"""
for i in range(2, len(nums)):
if nums[i] < nums[i - 1]:
nums[0] = nums[i]
for j in range(i - 1, -1, -1):
if nums[j] > nums[0]:
nums[j + 1] = nums[j]
else:
break
nums[j + 1] = nums[0]
return nums
def InsertSort02(nums):
"""
直接插入排序在是边查找边移动,在顺序存储下,通过将查找和移动分开来进行优化——通过折半查找加速查找。
依然有"哨兵"
空间复杂度:o(1)
时间复杂度:o(n^2) 相比直接插入排序,仅仅减少了比较次数,约为o(nlogn),与初始状态无关。移动次数没变,且依赖于初始状态。
稳定性:依然是稳定的。
适用性:自然不适用于链式存储。
"""
for i in range(2, len(nums)):
if nums[i] < nums[i - 1]:
nums[0] = nums[i]
low, high = 1, i - 1
while low <= high:
mid = (low + high) // 2
if nums[mid] > nums[0]:
high = mid - 1
else:
low = mid + 1
for j in range(i - 1, high, -1):
nums[j + 1] = nums[j]
nums[high + 1] = nums[0]
return nums
def ShellSort(nums):
"""
直接插入排序适用于 基本有序、数据量小 的场景。针对这两点提出希尔排序(缩小增量排序)
底层仍然是直接插入排序。
空间复杂度:o(1)
时间复杂度:o(n^1.3)~o(n^2) dk的划分涉及数学尚未解决的难题
稳定性:当相同元素划分到不同子序列时,其相对位置可能发生改变。
仅适用于顺序存储。
"""
dk = len(nums) // 2
while dk >= 1:
for i in range(dk, len(nums), dk):
if nums[i] < nums[i - dk]:
tmp = nums[i]
ind = 0
for j in range(i - dk, -1, dk):
if nums[j] > tmp:
nums[j + dk] = nums[j]
ind = j
else:
ind = j
break
if j - dk < 0:
ind = j
break
nums[ind + dk] = tmp
dk -= 1
return nums
def BubbleSort(nums):
"""
一趟冒泡的边界要特别注意,我是把大的值冒到后面。
空间复杂度:o(1)
时间复杂度:o(n^2) 初始序列有序,则仅需比较n-1次即最好情况为o(n); 初始序列逆序,此为最坏情况,为o(n^2)
稳定性:只有nums[j]>nums[j+1]时才往后冒泡,两个相同的值不会发生交换,故稳定。
不同于插入排序,每一次冒泡都将一个元素放到了最终排序的位置上(说的专业一点,冒泡过程产生的有序子序列是全局有序的)
"""
for i in range(len(nums) - 1):
flag = False
for j in range(len(nums) - i - 1):
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
flag = True
if not flag:
return nums
return nums
def QuickSort(nums, low, high):
"""
空间复杂度:最好o(logn), 最坏o(n). 最好的情况基准选择均衡,递归栈深度log(n+1);最坏情况下基准在边界,递归n-1次,栈深度为o(n)
时间复杂度:最坏o(n^2)
稳定性:在划分算法中,若右端存在两个小于基准的数,交换后其相对位置改变,故不稳定。
不产生有序子序列,但每次排序后基准会放在最终排序位置上。
"""
if low < high:
k = Paritition(nums, low, high)
QuickSort(nums, low, k - 1)
QuickSort(nums, k + 1, high)
return nums
def Paritition(nums, low, high):
pivot = nums[low]
while low < high:
while low < high and nums[high] >= pivot:
high -= 1
nums[low] = nums[high]
while low < high and nums[low] <= pivot:
low += 1
nums[high] = nums[low]
print("$$$ low, high", low, high)
nums[low] = pivot
return low
def SelectSort(nums):
"""
空间复杂度:o(1)
时间复杂度:o(n^2) 移动次数较少,最好的时候移动0次——表已经有序;比较次数与初始序列无关,始终是n(n-1)/2即o(n^2)
稳定性:不稳定。假如有两个相同元素,其相对位置为 ...A...B...,前一次排序min会先指向B,后一次才会指向A,排序后二者相对位置变成...B...A...
"""
for i in range(len(nums) - 1):
min = i
for j in range(i, len(nums)):
if nums[j] < nums[min]:
min = j
if min != i:
nums[min], nums[i] = nums[i], nums[min]
return nums
def HeapSort(nums):
"""
nums索引0位置不使用,仅仅用来暂存元素。
算法流程:首先BuildMaxHeap建堆,然后每输出一个堆顶元素就对以堆顶元素为根的树进行一次AdjustDown()
空间复杂度:o(1),即索引0
时间复杂度:o(nlogn),o(n)是建堆,算法平均下来时间复杂度是o(nlogn)
稳定性:不稳定。
"""
BuildMaxHeap(nums)
print('$$$建堆后:', nums)
for i in range(len(nums) - 1, 0, -1):
print('输出:', nums[1])
nums[i], nums[1] = nums[1], nums[i]
AdjustDown(nums, 1, i - 1)
print('$向下调整后:', nums)
def BuildMaxHeap(nums):
"""
建堆,从最后一个至第一个非叶节点,依次执行AdjustDown()
注意:索引0是暂存
"""
i = (len(nums) - 1) // 2
while i > 0:
AdjustDown(nums, i, len(nums) - 1)
i -= 1
return nums
def AdjustDown(nums, k, length):
"""
调整算法一定要手动模拟,这样理解才最透彻。
索引0节点是暂存作用,不使用它存储待排序数据。
:param nums:
:param k: 要调节的根节点. [k不能为0,否则会死循环,所以必须按书上的——索引0为暂存]
:param length: 根节点可以调节移动的范围,包括length
:return:
"""
nums[0] = nums[k]
i = 2 * k
while i <= length:
if i < length and nums[i] < nums[i + 1]:
i += 1
if nums[i] > nums[0]:
nums[k] = nums[i]
k = i
i *= 2
nums[k] = nums[0]
return nums
def MergeSort(nums):
"""
思路:分治法、递归
时间复杂度:O(nlogn) 合并算法O(n) 递归O(logn)
空间复杂度:O(n) 不只要递归栈深度,还有合并算法的临时数组,所以是O(n)
稳定性:稳定
"""
if len(nums) <= 1:
return nums
mid = len(nums) // 2
nums1 = MergeSort(nums[:mid])
nums2 = MergeSort(nums[mid:])
return Merge(nums1, nums2)
def Merge(nums1, nums2):
res = []
while nums1 and nums2:
if nums1[0] > nums2[0]:
res.append(nums2.pop(0))
else:
res.append(nums1.pop(0))
if nums1:
res += nums1
elif nums2:
res += nums2
return res