算法: 快排, 堆排, 合并排序

1 快排:

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)
View Code

2 堆排:

  a :堆的相关知识:

树是一种数据结构, 比如目录结构
二叉树--是一种特殊且常用的树:  度不超过2的树(节点最多有两个叉)
  满二叉树: 每一个节点都达到最大值,这个二叉树成为满二叉树
  完全二叉树:叶节点只能出现在最下层和次下层, 并且最下层的节点都集中在该层的最左边的若干位置的二叉树.
二叉树的两种存储方式:  链式存储和顺序存储(列表)
    二叉树可以用列表来存储,通过规律找到父亲或从孩子找到父亲.

堆: 大根堆: 一颗完全二叉树, 满足任一节点都比其孩子节点大
    小跟堆: 一颗完全二叉树, 满足任一节点都比其孩子节点小
View Code

    b : 堆排序的过程:

# 1 建立堆(从最后一个根节点开始调整)
# 2 得到堆顶元素为最大元素
# 3  去掉堆顶, 将堆最后一个元素放到堆顶, 此时可通过一次调整重新使堆有序
# 4 堆顶元素为第二大元素
# 5 重复步骤三, 直到堆变空
View Code

  c : 堆的向下调整:

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
View Code

  d : 堆排代码:

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]))
# 先构建堆, 再进行调整
View Code

3 合并排序  

  a : 原理: 

    分解 将列表越分越小,直至分成一个元素

    终止条件:一个元素是有序的

    分解: 将两个有序列表合并,列表越来越大

  b : 一次归并的代码:

    对于一个列表, low表示开始, high表示结束的索引, mid表示中间值,用左半部分的值和右半部分的值进行比较.

# 一次合并
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
View Code

  c :合并的代码:

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)
View Code

  d : 合并算法的时间复杂度:

    一次归并的时间复杂度是o(n)

    mid一次的时间复杂度是0(1),分解了logn次, 时间复杂度是logn

    合并的过程是nlogn

    即时间复杂度是:nlogn

 4 三种算法的总结:

  a : 时间复杂度都是o(nlogn)

  b : 效率: 快排 >归并>堆排

  c : 三种算法的优缺点:

    快排: 极端情况下排序效率很低

    归并:需要额外的内存开销

    堆排序: 在快的排序算法中,相对较慢, 但应用很广.

    

 

posted @ 2018-12-18 21:41  ...绿茵  阅读(606)  评论(1编辑  收藏  举报
1