排序算法: 三大中级排序算法,原理解析及用法

三大中级算法

  • 难度 ★★
  • 算法复杂度O(nlogn)
  • 一般情况下排序时间: 快速排序< 归并排序 < 堆排序
  • 快速排序: 缺点极端情况下效率低
  • 堆排序: 缺点在快的排序算法中相对慢
  • 归并排序: 缺点要有额外内存空间

快速排序 ★★

quick Sort

算法复杂度: O(nlogn) <n乘logn>

思路

每趟取第一个元素,让列表被该元素分为两部分,左边的比他小,右边比他大, 重复用每次得来的第一个元素递归

问题1:

递归py有最大深度问题999次!(虽然可以设置)

问题2:

最坏的情况,如果列表本身是一个倒序列表,那么效率相对较低(解决方案:随机快排,一开始就随机取一个数放在最左边开始排)

def quick_sort(li, left, right):
    """
    快速排序
    """
    if left < right:  # 至少两个元素
        mid = partition(li, left, right)
        quick_sort(li, left, mid-1)
        quick_sort(li, mid+1, right)

def partition(li, left, right):
    """
    将下标为0的元素为参照物,左边的放比他小的,右边放比他大的
    """
    tmp = li[left]  # 第一个位置
    while left < right:  # 一直循环
        while left < right and li[right] >= tmp:  # 右边开始找比tmp 小的数放到左边的空位
            right -= 1  # 往左走一步
        li[left] = li[right]  # 把右边的值写到左边空位
        # 有可能所有数都比自己大
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]  # 把左边的值写到右边的空位上
    li[left] = tmp  # 把tmp归位
    return left  # 或者返回right都行

li_test = [3, 2, 7, 1, 6, 9, 8, 4]
quick_sort(li_test, 0, len(li_test)-1)
print(li_test)

堆排序 ★★

算法复杂度: O(nlogn) <n乘logn>

前提

二叉树的知识储备!!

效率:

快排的时间复杂度优于堆排

def sift(li, low, high):
    """
    调整堆
    :param li: 列表
    :param low: 堆的根节点
    :param high: 堆的最后一个元素位置
    :return:
    """
    i = low  # i最开始指向的父
    j = 2 * i + 1  # 堆顶左孩子
    tmp = li[low]  # 把堆顶存起来
    while j <= high:  # 只要j位置有数
        if j + 1 <= high and li[j + 1] > li[j]:  # 如果右还在比较大且右孩子有
            j = j + 1  # j指向右孩子
        if li[j] > tmp:
            li[i] = li[j]
            i = j  # 往下一步看
            j = 2 * i + 1
        else:  # tmp更大,把tmp放到i位置上
            li[i] = tmp  # 把tmp放到某一级领导位置上
            break
    else:
        li[i] = tmp  # 把tmp放到叶子节点上


def heap_sort(li):
    """
    堆排序
    """
    n = len(li)
    for i in range((n-2)//2, -1, -1):
        # i表示建堆时调整的部分的根下标
        sift(li, i, n-1)
    # 建堆完成了
    for i in range(n-1, -1, -1):
        # i向当前堆最后一个元素
        li[0], li[i] = li[i], li[0]
        sift(li, 0, i-1)  # i-1是新的high


li_test = [3, 2, 7, 1, 6, 9, 8, 4]
heap_sort(li_test)
print(li_test)

堆排序py 模块

import heapq  # q : queue 优先列队
import random

li = list(range(100))

random.shuffle(li)  # 打乱

print(li)

heapq.heapify(li)  # 建堆

n = len(li)
for i in range(n):
    print(heapq.heappop(li), end=',')

堆排序 topk问题 【常用】

问题:比如热搜网,有n个数,取前k打的数(排序好的)

思路

  1. 取列表前k个元素建立小根堆,堆顶是目前第k大的数
  2. 依次向后面遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素,反之换为该元素,并进行一次调整
  3. 遍历素有元素后,倒序弹出堆顶
import random


def sift(li, low, high):
    """
    调整为小根堆
    :param li: 列表
    :param low: 堆的根节点
    :param high: 堆的最后一个元素位置
    :return:
    """
    i = low  # i最开始指向的父
    j = 2 * i + 1  # 堆顶左孩子
    tmp = li[low]  # 把堆顶存起来
    while j <= high:  # 只要j位置有数
        if j + 1 <= high and li[j + 1] < li[j]:  # 如果右还在比较大且右孩子有
            j = j + 1  # j指向右孩子
        if li[j] < tmp:
            li[i] = li[j]
            i = j  # 往下一步看
            j = 2 * i + 1
        else:  # tmp更大,把tmp放到i位置上
            li[i] = tmp  # 把tmp放到某一级领导位置上
            break
    else:
        li[i] = tmp  # 把tmp放到叶子节点上


def top_key(li, k):
    heap = li[0:k]
    for i in range((k-2)//2, -1, -1):
        sift(heap, i, k-1)
    # 1.建堆
    for i in range(k, len(li)-1):
        if li[i] > heap[0]:
            heap[0] = li[i]
            sift(heap, 0, k-1)
    # 2.遍历
    for i in range(k-1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i-1)
    # 3.出数
    return heap


li = list(range(1000))
random.shuffle(li)


print(top_key(li, 10))  # 测试 前10数

归并排序 ★★

算法复杂度: O(nlogn) <n乘logn>
空间复杂度: O(n)

思路

假设两个列表已有序,那么将他们合并在一起,将列表越分越小,直到分为一个元素

def merge(li, low, mid, high):
    """
    :param li: 列表
    :param low: 左列表第一个元素
    :param mid: 左列表最后一个元素,那么右列表第一个就是mid+1
    :param high: 右列表最后一个元素
    :return:
    """
    i = low  # 第一段第一个元素
    j = mid + 1  # 第二段的第一个元素
    tmp_list = []
    while i <= mid and j <= high:  # 必须左右有数
        if li[i] < li[j]:
            tmp_list.append(li[i])
            # 移动后箭头必须移动一位,因为已经把小的值提出到临时list中
            i += 1
        else:
            tmp_list.append(li[j])
            j += 1
    # while 执行完,说明有一段执行完了,剩下的接到tmp中即可
    # 分别判断一下那个还有数
    while i <= mid:
        tmp_list.append(li[i])
        i += 1
    while j <= high:
        tmp_list.append(li[j])
        j += 1
    li[low:high+1] = tmp_list  # 写回去


def merge_sort(li, low, high):
    if low < high:  # 至少有2个元素,递归
        mid = (low + high) // 2  # 整除2
        merge_sort(li, low, mid)
        merge_sort(li, mid+1, high)
        merge(li, low, mid, high)


li_test = [3, 2, 7, 1, 6, 9, 8, 4]
merge_sort(li_test, 0, len(li_test)-1)
print(li_test)

猜你喜欢

转载自blog.csdn.net/weixin_42329277/article/details/84405317