快速排序 O(n logn) 堆排序 O(n logn) 归并排序 O(n logn)

NB三人组

快速排序

  • 思路"
    • 取一个元素P (第一个元素), 使元素归位
    • 列表被P 分成两部分,左边都比P小,右边比P大;
    • 递归完成排序.
  • 问题 如果是已经排序好的 倒叙 列表 则会 递归深度越界
    • 每次
# 时间复杂度: O(n*logn)
import sys
import random
from cal_time import cal_time
# 设置递归深度 
sys.setrecursionlimit(10000)

def _quick_sort(li, left, right):
    if left < right: # 递归区域至少有两个元素
        # 归位 [left, right] --> [left, mid-1] [mid+1, right]
        mid = partition(li, left, right) 
        _quick_sort(li, left, mid-1)
        _quick_sort(li, mid+1, right)

@cal_time
def quick_sort(li):
    _quick_sort(li, 0, len(li)-1)
 

def partition(li, left, right):
    # 解决最坏情况 
    # i = random.randint(left, right)
    # li[i], li[left] = li[left], li[i]
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]
    li[left] = tmp  # 此时 left = right
    return left


def gogogo2(li, left, right):
    # 区域1:[left, i] 区域2:[i+1, j-1]
    i = left - 1 # 初识区域1和区域2都空
    for j in range(left, right):
        if li[j] < li[right]: # 归到区域1
            i += 1
            li[j], li[i] = li[i], li[j]
    li[right], li[i+1] = li[i+1], li[right]
    return i+1



@cal_time
def quick_sort2(li):
    return _quick_sort2(li)


def _quick_sort2(li):
    if len(li) < 2:
        return li
    x = li[0]
    left = [li[i] for i in range(1, len(li)) if li[i] <= x]
    right = [li[i] for i in range(1, len(li)) if li[i] > x]
    _quick_sort2(left)
    _quick_sort2(right)
    return left + [x] + right



# @cal_time
# def sys_sort(li):
#     li.sort()

import copy

li = list(range(10000))
random.shuffle(li)
li2 = copy.copy(li)

quick_sort(li)
quick_sort2(li2)

# sys_sort(li)

# 另类写法

def my_partition(li, l, r):
    a = li[l]
    i = l + 1
    while i <= r:
        if li[i] >= a:
            li[r], li[i] = li[i], li[r]
            r -= 1
        else:
            i += 1
    li[i - 1], li[l] = li[l], li[i - 1]
    return i


def quick_sort(li, left, right):
    if left < right:
        tmp = my_partition(li, left, right)
        quick_sort(li, left, tmp -1)
        quick_sort(li, tmp, right)


@cal_time
def _quick_sort(li):
    quick_sort(li, 0, len(li) - 1)


堆排序

  • 前传: (树与二叉树简介)
    • 树是一种数据结构 比如目录结构
    • 树是一种可以递归定义的数据结构
    • 树是由N个节点组成的集合
      • 如果n=0,那么是一颗空树:
      • 如果n>0,那么在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树.
    • 概念:
      • 根节点,叶子节点
      • 树的深度(高度)
      • 树的度
      • 孩子节点/父节点
      • 子树
  • 时间复杂度 O(n log n)
import random
from cal_time import cal_time


def sift(li, low, high):
    # low 表示堆顶下标, high表示堆中最后一个元素下标
    tmp = li[low]
    i = low
    j = 2 * i + 1
    while j <= high:  # 第二种循环退出情况,没有孩子和tmp竞争i这个位置
        if j+1 <= high and li[j+1] > li[j]:  # 如果右孩子存在并且比左孩子大 j指向右孩子
            j += 1
        if li[j] > tmp:  # 判断 证明 孩子比父亲大 交换
            li[i] = li[j]
            i = j  # 从新赋值堆顶
            j = 2 * i + 1  # 重新赋值孩子
        else:
            break  # 第一种循环退出情况,tmp比目前两个孩子都大
    # 将取出的 tmp 从新赋值回空出的位置
    li[i] = tmp

@cal_time
def heap_sort(li):
    # 1. 从列表构造堆 low的值和high的值
    n = len(li)
    for low in range(n//2-1, -1, -1):
        sift(li, low, n-1)
    # 2. 挨个出数 利用原来的空间存储下来的值,但是这些值不属于堆
    for high in range(n-1, -1, -1): # range(n-1,0,-1)
        li[high], li[0] = li[0], li[high] # 1.退休 2.棋子
        sift(li, 0, high-1) # 3.调整


li = list(range(100000))
random.shuffle(li)
heap_sort(li)


内置模块做堆

import heapq
import random
# heap queue
# priority queue 优先队列

li = [9,5,8,4,7,6,3,2,1]
heapq.heapify(li)  # 小顶堆
print(li)
heapq.heappush(li, 1)  # 给堆里添加值
print(li)
ele = heapq.heappop(li)  # 从堆顶取值
print(ele)
print(li)
# ele = heapq.heappop(li)
# print(ele)
# print(li)

# 做堆排序
heapq.heapify(li)  # 建堆 
res = []
while li:
    res.append(heapq.heappop(li))
print(res)


归并排序

  • 假设现在的列表分两段有序,如何将其合成为一个有序列表
import random
from cal_time import cal_time


def merge(li, low, mid, high):
    i = low  # 左边的指针
    j = mid + 1  # 右边的指针
    li_tmp = []
    while i <= mid and j <= high: # 两边都有数
        if li[i] <= li[j]:
            li_tmp.append(li[i])
            i += 1
        else:
            li_tmp.append(li[j])
            j += 1
    # i<=mid 和 j<=high 两个条件 只能有一个满足
    while i <= mid:
        li_tmp.append(li[i])
        i += 1
    while j <= high:
        li_tmp.append(li[j])
        j += 1
    # li_tmp 0~high-low 复制回li low~high
    for i in range(len(li_tmp)):
        li[low+i] = li_tmp[i]


def _merge_sort(li, low, high):
    if low < high: # 至少两个元素
        # print(li[low:high+1], '->', end=' ')
        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) # 递归排序右边
        # print(li[low:mid+1], li[mid+1: high+1], '->', end=' ')
        merge(li, low, mid, high) # 一次归并 合并
        # print(li[low:high+1])

@cal_time
def merge_sort(li):
    _merge_sort(li, 0, len(li)-1)

# li = [10,4,6,3,8,2,5,7]
# merge_sort(li, 0, len(li)-1)
# # print(li)

# li = list(range(100000))
# random.shuffle(li)
# merge_sort(li)

# li.sort()

li = [
    {'age': 22, 'name':'abc'},
    {'age': 18, 'name':'qwe'},
    {'age': 22, 'name':'asd'},
    {'age': 26, 'name':'zxc'},
    {'age': 22, 'name':'tyu'},
]
li.sort(key=lambda x:x['age'])
print(li)

NB三人组-小结

  • 三种排序算法的时间复杂度都是
    • O(n log n)
  • 一般情况下,就运行时间而言:

    • 快速排序 < 归并排序 < 堆排序
  • 三种排序算法的缺点:

    • 快速排序:极端情况下排序效率低
    • 归并排序:需要额外的内存开销
    • 堆排序:在快的排序算法中相对较慢
排序方法 时间复杂度 空间复杂度 稳定性 代码复杂度
最坏情况 平均情况 最好情况
冒泡排序 O(n2) O(n2) O(n) O(1) 稳定 简单
直接选择排序 O(n2) O(n2) O(n2) O(1) 不稳定 简单
直接插入排序 O(n2) O(n2) O(n2) O(1) 稳定 简单
快速排序 O(n2) O(nlogn) O(nlogn) 平均情况O(logn); 最坏情况O(n) 不稳定 较复杂
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定 复杂
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定 较复杂
posted @ 2019-04-22 12:53  拐弯  阅读(299)  评论(0编辑  收藏  举报