1 快排:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
data = [2, 5, 1, 6, 3, 9, 7] def quick_sort(data, left, right): if left < right: # 1 确定中间数的位置, 求出中间数的索引 mid = partition(data, left, right) print(mid) # 2 对剩余的两部分排序, 采用递归 quick_sort(data, left, mid - 1) quick_sort(data, mid + 1, right) print(data) def partition(data, left, right): tmp = data[left] while left < right: # 加left < right的原因: 由于跳不出循环, 所以加上条件, 要么是left +1 , 或者是right - 1. while tmp < data[right] and left < right: right = right - 1 data[left] = data[right] while tmp > data[left] and left < right: left = left + 1 data[right] = data[left] data[left] = tmp return left quick_sort(data, 0, len(data) - 1)
2 堆排:
a :堆的相关知识:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
树是一种数据结构, 比如目录结构 二叉树--是一种特殊且常用的树: 度不超过2的树(节点最多有两个叉) 满二叉树: 每一个节点都达到最大值,这个二叉树成为满二叉树 完全二叉树:叶节点只能出现在最下层和次下层, 并且最下层的节点都集中在该层的最左边的若干位置的二叉树. 二叉树的两种存储方式: 链式存储和顺序存储(列表) 二叉树可以用列表来存储,通过规律找到父亲或从孩子找到父亲. 堆: 大根堆: 一颗完全二叉树, 满足任一节点都比其孩子节点大 小跟堆: 一颗完全二叉树, 满足任一节点都比其孩子节点小
b : 堆排序的过程:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 1 建立堆(从最后一个根节点开始调整) # 2 得到堆顶元素为最大元素 # 3 去掉堆顶, 将堆最后一个元素放到堆顶, 此时可通过一次调整重新使堆有序 # 4 堆顶元素为第二大元素 # 5 重复步骤三, 直到堆变空
c : 堆的向下调整:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def sift(li, low, high): tmp = li[low] print('low', low) i = low j = 2 * i + 1 # 此时要进行循环, 因为要进行多次: while j <= high: # 第二种情况退出: i 已经是最后一层了 if j < high and li[j] < li[j + 1]: # 当右节点存在且右节点比左节点值大 j = j + 1 if li[j] > tmp: # 当子节点的值比父节点的值大的时候, 交换, 获取新的i和j li[i] = li[j] i = j j = 2 * i + 1 else: break # 第一种情况退出: tmp的值比j位置的值大 li[i] = tmp return li
d : 堆排代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def heap_sort(data): # 构建堆 n = len(data) print("n", n) for low in range(n // 2 - 1, -1, -1): # 构建堆的过程,从最后一个根节点开始. sift(data, low, n - 1) print('data', data) # 挨个出数 for high in range(n - 1, -1, -1): data[0], data[high] = data[high], data[0] sift(data, 0, high - 1) return data print(heap_sort([2, 6, 8, 7, 4, 3, 1, 0])) # 先构建堆, 再进行调整
3 合并排序
a : 原理:
分解 将列表越分越小,直至分成一个元素
终止条件:一个元素是有序的
分解: 将两个有序列表合并,列表越来越大
b : 一次归并的代码:
对于一个列表, low表示开始, high表示结束的索引, mid表示中间值,用左半部分的值和右半部分的值进行比较.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 一次合并 def merge(li, low, mid, high): i = low j = mid + 1 li_temp = [] # 左右两边都有值的情况 while i <= mid and j <= high: if li[i] < li[j]: li_temp.append(li[i]) i = i + 1 else: li_temp.append(li[j]) j = j + 1 # 只剩下左边的值, while有if的作用 while i <= mid: li_temp.append(li[i]) i = i + 1 # 只剩下右边的值 while j <= high: li_temp.append(li[j]) j = j + 1 for k in range(low, high + 1): li[k] = li_temp[k - low] return li
c :合并的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def merge_sort(li, low, high): if low < high: mid = (low + high) // 2 print(li[low:mid+1],li[mid+1:high+1]) # 递归左半部分 merge_sort(li, low, mid) # 递归右半部分 merge_sort(li, mid + 1, high) # 归并 merge(li,low,mid,high) print(li[low:mid+1],li[mid+1:high+1]) li = [10, 4, 6, 3, 8, 5, 2, 7] print(merge_sort(li,0,(len(li)-1))) print(li)
d : 合并算法的时间复杂度:
一次归并的时间复杂度是o(n)
mid一次的时间复杂度是0(1),分解了logn次, 时间复杂度是logn
合并的过程是nlogn
即时间复杂度是:nlogn
4 三种算法的总结:
a : 时间复杂度都是o(nlogn)
b : 效率: 快排 >归并>堆排
c : 三种算法的优缺点:
快排: 极端情况下排序效率很低
归并:需要额外的内存开销
堆排序: 在快的排序算法中,相对较慢, 但应用很广.