数据结构和算法笔记--6堆排序

堆排序

  1. 数与二叉树
  • 树是一种数据结构 比如目录结构
  • 树是一种可递归定义的数据结构
  • 树是由n个节点组成的集合:
    • 如果n=0, 那这是一颗空树
    • 如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
      树

一些概念:
根节点,叶子节点
树的深度
树的度:度是该节点分叉的数量,树的度则是该树当中有着最多分叉的节点的分叉数量,如上图树的度为3,因为在这个树当中,A和C有着最多的三个分叉,所以这个树的度为3
孩子节点,父节点: 节点之间的关系,C为H的父节点,H为C的子节点
子树: HIJK为子树

二叉树
度不超过2的树
每个节点最多只有两个子节点
左子节点和右子节点

  • 满二叉树: 一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树
  • 完全二叉树: 叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边的若干位置的二叉树。
    二叉树
    二叉树的储存方式(表达方式)
  • 链式存储方式
  • 顺序存储方式 : 用列表存
    顺序储存
    顺序
    堆: 一种特殊的完全二叉树结构
    堆

堆排序

堆的向下调整性质

  • 假设根结点的子树都是堆,但是根结点不满足堆的性质。
  • 可以通过一次向下调整使其变成一个堆
    向下调整
    将根节点转移到合适的位置

堆排序的过程

  • 1.建立一个堆
  • 2.得到堆顶元素为最大元素
  • 3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整使得堆变得有序
  • 4.堆顶元素为第二大元素
  • 5.重复步骤3,直到堆变空

构造堆

由下至上,由叶至根。先调整叶子节点,再调整根节点。

调整函数

调整函数:

def sift(li, low, high):
	
	# param li:列表(用列表表示堆)
	# param low:堆的根节点位置
	# param high: 堆的最后一个元素的位置
	# return:
	
	# 用i和j来代表我们当前所观察的两个位置
	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 > li[j],把tmp放到i的位置上
			li[i] = tmp
			break
	else:
		li[i] = tmp # i位于最下面的子节点,并且下面没有继续的子节点。将堆顶的tmp放到i的位置上


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

时间复杂度:
sift函数是logn,走一个树的高度的过程。
heap_sort函数时间复杂度是O(nlogn)

内置模块

import heapq	# q-> queue 优先队列
li = list(range(100))
random.shuffle(li)
heapq.heapify(li) # 建堆
heapq.heappop(li) # 向外弹出最小元素

topk问题

现在有n个数,设计算法得到前k大的数(k<n)

  • 解决思路:
  1. 排序后切片 O(nlogn)
  2. 简单的排序算法三人组 O(kn)
  3. 堆排序思路 O(nlogk)

堆排序 topk问题思路
topk堆

取列表前k个元素建立一个小根堆,堆顶就是目前第k大的数。
依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略钙元素,如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整。
比那里列表所以元素后,倒序弹出堆顶。

小根堆:

def sift(li, low, high):
	
	# param li:列表(用列表表示堆)
	# param low:堆的根节点位置
	# param high: 堆的最后一个元素的位置
	# return:
	
	# 用i和j来代表我们当前所观察的两个位置
	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 > li[j],把tmp放到i的位置上
			li[i] = tmp
			break
	else:
		li[i] = tmp # i位于最下面的子节点,并且下面没有继续的子节点。将堆顶的tmp放到i的位置上


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


def topk(li, k):
	heap = li[0:k]
	# 1. 建堆
	for i in range((k-2)//2, -1, -1):
		sift(heap, i, k-1)
	# 2. 遍历
	for i in range(k, len(li)):
		if li[i] > heap[0]:
			heap[0] = li[i]
			sift(heap, 0, k-1)
	# 3. 出数
	for i in range(k-1, -1, -1):
		# i指向当前堆的最后一个元素
		heap[0], heap[i] = heap[i], heap[0]
		sift(heap, 0, i - 1) # i-1是新的high
	
	return heap
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值