数据结构与算法(三)队列与堆(Queue and Heap)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_39538889/article/details/85879057

数据结构与算法(三)队列与堆(Queue and Heap)

队列(queue)

基本FIFO队列是先进先出( First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。

在这里插入图片描述

基本FIFO队列外, 还有LIFO队列( 后进先出 Last-In-First-Out, 也就是我们平时说的堆栈的栈)和优先级队列( Priority Queue)

队列的类型:链式队列:即用链表实现的队列。静态队列:即用数组实现的队列。

#十分钟python理解三种队列:

  1. Python Threading Beginners Tutorial- Queue (part#1 ) https://www.youtube.com/watch?v=bnm5_GH04fM 内容为FIFO
  2. Python Threading Beginners Tutorial- Queue -(part#2) https://www.youtube.com/watch?v=wkPMom77Hqk 内容为LIFO和Priority

我们通过内置queue模块来对比一下三个队列:

https://docs.python.org/3/library/queue.html #Python3官方文档-queue

***
import queue
#首先是FIFO
q = queue.Queue()

for i in range(5):
    q.put(i)

while not q.empty():
    print (q.get(),end = '   ') #结果:0   1   2   3   4

#然后是LIFO
q = queue.LifoQueue()

for i in range(5):
    q.put(i)

while not q.empty():
    print (q.get(),end = '   ') #结果:4   3   2   1   0

可以看到LIFO的结果和FIFO完全相反

#最后是Priority
import queue
q = queue.PriorityQueue()

q.put((1, 'Priority 1'))
q.put((3, 'Prioirty 3'))
q.put((4 ,'Priority 4'))
q.put((2 ,'Priority 2'))

for i in range(q.qsize()):
    print(q.get()[1])

#结果  Priority 1
#      Priority 2
#      Priority 3
#      Priority 4

优先级队列把后面的元素按照priority_number的顺序输出出来了

拓展阅读: 双端队列和多线程队列
详解Python中的四种队列 https://www.jb51.net/article/140534.htm

堆(Heap)

堆是一种经过排序的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆

堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常用来实现优先队列

在这里插入图片描述
堆分为最大堆和最小堆,其实就是完全二叉树。 如果要升序排序就建大顶堆,降序排序建小顶堆

https://baike.baidu.com/item/完全二叉树

堆和栈的区别:
堆的存取是随意,这就如同我们在图书馆的书架上取书,虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,我们可以直接取出我们想要的书。

堆排序(Heapsort)

堆排序就是把堆顶的最大数取出,
将剩余的堆继续调整为最大堆,具体过程在第二块有介绍,以递归实现
剩余部分调整为最大堆后,再次将堆顶的最大数取出,再将剩余部分调整为最大堆,这个过程持续到剩余数只有一个时结束

代码取自维基百科 https://zh.wikipedia.org/wiki/堆排序

#!/usr/bin/env python
#-*-coding:utf-8-*-


def heap_sort(lst):
    def sift_down(start, end):
        """最大堆调整"""
        root = start
         # 从root开始对最大堆调整
        while True:
            child = 2 * root + 1 #left child 因为我们是从0开始, 所以要+1
            if child > end:
                break
            # 找出两个child中交大的一个
            if child + 1 <= end and lst[child] < lst[child + 1]: #如果左边小于右边
                child += 1 #设置右边为大
            if lst[root] < lst[child]:
            # 最大堆小于较大的child, 交换顺序
                lst[root], lst[child] = lst[child], lst[root]
                # 正在调整的节点设置为root
                root = child
            else:
            	# 无需调整的时候, 退出
                break

# 创建最大堆
	# 从最后一个有子节点的位置开始调整最大堆
    for start in range((len(lst) - 2) // 2, -1, -1): 
        sift_down(start, len(lst) - 1)

# 堆排序
	# 将最大的放到堆的最后一个, 堆-1, 
	# 这是第一个数就是一个很小的数了,继续调整排序, 知道再次成为最大堆
	
    for end in range(len(lst) - 1, 0, -1):
        lst[0], lst[end] = lst[end], lst[0]
        sift_down(0, end - 1)
    print(lst) #验证一下结果
    return lst


def main():
    l = [9, 2, 1, 7, 6, 8, 5, 3, 4]
    heap_sort(l)


if __name__ == "__main__":
    main()
#结果 [1,2,3,4,5,6,7,8,9]

看了半天才把这个代码完全弄明白, 好在用纸把图画下来, 走一走就明白了, 很开心

LeetCode思想实践:

#239 Sliding Window Maximum

https://leetcode.com/problems/sliding-window-maximum/

相邻k个数就取一个最大值, 输出列表

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

LeetCode根本就不用考虑暴力双循环了, 肯定超时

用长度为k的双端队列存储,最大值总是存在队列头,较小值存在队列后,队列持续更新,每次输出队列头即可。想不明白可以加上print运行一边代码看每次循环后的队列更新情况

class Solution:
    def maxSlidingWindow(self, nums, k):
        res = []
        index = []
        for i in range(len(nums)):
            # index[0]已经走出k长度的判断条,淘汰
            while index and index[0] <= i - k: 
                index.pop(0)
            # index[-1]没有下一个值大,淘汰
            while index and nums[index[-1]] <= nums[i]: 
                index.pop(-1)
            index.append(i)
            if i < k-1: # 凑齐k个数才输出
                continue
            res.append(nums[index[0]])
        return res

结果: 在这里插入图片描述

今天的学习有用到前两天的知识, 要记得复习

哈希表 主要是一种以空间换时间的数据结构,追求效率,查找和插入的时间复杂度都为O(1)。
链表 主要是在插入的时候可以达到O(1)的复杂度,但是在查找特定编号节点需要O(n)的时间。

结尾不能忘了感谢体统资料的大神们:

  1. 详解Python中的四种队列 https://www.jb51.net/article/140534.htm
  2. 维基百科-堆排序 https://zh.wikipedia.org/wiki/堆排序
  3. 图解排序算法(三)之堆排序 http://www.cnblogs.com/chengxiao/p/6129630.html
  4. Python3-queue https://docs.python.org/3/library/queue.html
  5. 堆和栈的区别 之 数据结构和内存 https://blog.csdn.net/langb2014/article/details/79376349
  6. 用python实现堆排序 https://blog.csdn.net/dujiahaogod/article/details/79688331

下次见

猜你喜欢

转载自blog.csdn.net/weixin_39538889/article/details/85879057