堆排序+归并排序(原理+python代码实现)

堆排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
堆是一个特殊的完全二叉树。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
挨个出数得过程要清晰。下面得问题是怎么建立一个堆?
看最后一个非叶子节点,对这个子树做一个调整,大的调整到上面,然后依次往前推。详看视频23,农村包围城市。
建立好堆之后,就可以用挨个出数来。

def sift(li, low, high):  # 先写调整函数。
    """
    :param li:列表
    :param low:堆的根节点位置。
    :param high:堆的最后一个元素的位置。
    """
    i = low  # i最开始指向根节点
    j = 2 * i + 1  # j开始是左孩子
    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


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 = [i for i in range(100)]
import random
random.shuffle(li)
print(li)
heap_sort(li)
print(li)
# 堆排序的时间复杂度为nlogn,sift调整的过程为logn,排序过程为n。和快速排序时间复杂度一样。但是实际表现快排优于堆排。
import heapq  # 内置模块实现堆排序。
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=',')

Top K问题

在这里插入图片描述

1、NB三人组里面全排完然后切片。
2、LOW B三人组里面,冒泡排序,排序一趟最大的数字上去了。选择排序和插入排序也类似。
3、堆排序的思想处理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# 用堆排序的思路实现topk问题。
def sift(li, low, high):  # 先写调整函数。
    """

    :param li:列表
    :param low:堆的根节点位置。
    :param high:堆的最后一个元素的位置。
    """
    i = low  # i最开始指向根节点
    j = 2 * i + 1  # j开始是左孩子
    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


def topk(li, k):
    heap = li[0:k]   # 取出来列表的前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):  # i指向当前堆的最后一个元素。
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i - 1)   # 出数
    return heap

归并排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上过程为一次归并。
归并过程代码如下所示:

def merge(li, low, mid, high):
    i = low
    j = mid + 1
    ltmp = []
    while i<=mid and j<= high:   # 只要左右两边都有数
        if li[i] < li[j]:
            ltmp.append(li(i))
            i +=1
        else:
            ltmp.append(li[j])
            j += 1
# while执行完,肯定有一部分没有数了。
    while i <= mid:
        ltmp.append(li[i])
        i += 1
    while j <= mid:
        ltmp.append(li[j])
        j += 1
    li[low:high+1] = ltmp   # 切片可以写回去。


li = [2, 4, 5, 7, 1, 3, 6, 8]
merge(li, 0, 3, 7)


def merge_sort(li, low, high):    # 归并排序递归的终止条件是小列表只剩一个元素。
    if low < high:   #  至少有两个元素,递归。
        mid = (low + high) // 2
        merge_sort(li, low, mid)
        merge_sort(li, mid+1, high)
        merge(li, low, mid, high)

递归的思想,类似于汉诺塔的操作。左边排好序,右边排好序,左边右边做归并就可以。
在这里插入图片描述
归并排序的时间复杂度。
在这里插入图片描述
归并排序不是原地排序,需要一些新的额外的空间。

nb三人组小结

在这里插入图片描述
快速排序倒序排序是一种极端情况。
在这里插入图片描述
在这里插入图片描述
递归需要系统空间,走了N层,空间复杂度就需要O(n)。函数栈的列表,记录函数的位置。
排序的稳定性:能保证相同数字的相对位置不变。
在这里插入图片描述
在这里插入图片描述
有顺序挨个换的都是稳定的。直接选择排序是跳着换的,因此不稳定。快速排序也是飞着换的,堆排序虽然是父亲到孩子,但是作为一个列表,也是飞着换的。python、java排序现在都用归并排序,因此比较稳定。快速排序和堆排序比较复杂,需要理解递归的部分。堆排序最难理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值