python 队列 一次取多个_数据结构系列:栈?队列?这俩货应该这么理解和掌握

上篇文章 数据结构系列:列表?线性表?这俩货到底什么关系?我们主要介绍了线性表的经典实现之一:列表。本篇将主要分享栈和队列这两种线性表结构的特点和一些经典应用。代码都是基于python语言。

7e731475-6913-eb11-8da9-e4434bdf6706.png

  1. 概念

栈(stack)是一种数据结构,属于线性表的一种实现。既然是线性表,那么肯定数据之间也有一定的特征,栈的特征如下

  • 数据项的加入和移除都限制在一端,这个端点一般称之为栈顶
  • 专业点的描述就是:后进先出(LIFO),越早进入栈结构的数据越晚离开栈
  1. 生活中的例子(加深理解)
  • 一摞盘子,想取的话只能先取最上面的一个,然后下一个,依次类推,但如果想放新的盘子上去,也只能将新盘子放在所有盘子的最上面,然后再摞一个,以此类推。这个(顶端)用来拿或者取盘子的位置,抽象出来一个概念,称之为栈顶。盘子最下面不参与任何的操作,称之为栈底。
  • 子弹夹,永远只能从一端压入子弹,且从同一端弹出子弹。这样的结果就是,最先压进去的子弹会最后才被弹出,而最后压进去的子弹会最先弹出,这个弹出的位置就称之为栈顶
  1. 栈的实现(基于python)

栈的底层同样可以基于数组或者链表实现,但这里为了简单,就基于python的内置结构列表构造一个简单的栈结构。你要知道,作为任何数据结构,用什么实现不重要,重要的是你设计出来的栈结构的接口要满足后进先出的结构特点

# 我们这里简单的用python内置列表作为基础结构实现栈结构class Stack:"""利用列表(底层为数组)构建栈的抽象结构"""    def __init__(self):        self._items = []    def size(self):        return len(self._items)    def push(self, item):        self._items.append(item)    def pop(self):        return self._items.pop()    def is_empty(self):        return len(self._items) == 0    def peek(self):        return self._items[len(self._items) - 1]

这里要强调一下,虽然从代码上看,确实满足了栈的特点,但这并不是一个严格意义上的栈,因为内部的一些列表方法都是可以使用的,而在抽象结构中这些方法应该谨慎地使用。

  1. 栈的经典应用
  • 编译器基本功能中的对于各种括号("(){}[]")的正确匹配
  • 程序运行中作为数据的隔离
  • 回溯算法
  • 管理计算机内存支持函数和方法的调用
  • 中缀表达式转换为后缀表达式;计算后缀表达式(思路如下)

-如果遇到操作数,我们就直接将其输出

-如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。

-如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。

-如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) “的情况下我们才弹出” ( “,其他情况我们都不会弹出” ( "

-如果我们读到了输入的末尾,则将栈中所有元素依次弹出

我这里分享两个通过栈结构实现的经典应用:进制转换和中缀转后缀表达式

  1. 10进制转换为2/8/16进制的字符串格式
# 10进制转换为2/8/16进制的字符串格式def to_str(n, base):    :param n: 十进制数    :param base: 想要转换的进制    :return:     if n < base:        return str(n)    elif 2 <= base <= 10:        return to_str(n // base, base) + str((n % base))    else:        convert_string = "0123456789abcdef"        return to_str(n // base, base) + convert_string[(n % base)]ret = to_str(541, 2)print(ret)
  1. 中缀转后缀表达式
# 中缀转后缀表达式def infix_to_suffix(expressions):    """当前约定中缀表达式是以空格进行分割, exp: a + b * c"""    s = Stack()    exp_list = expressions.split(" ")    # 存放操作数    num_list = []    # 自定义优先级,"("的存在是为了有多重括号存在时,保证所有的+-*/都可以顺利入栈    priority_dic = {"(": 1, "+": 2, "-": 2, "*": 3, "/": 3}    for token in exp_list:        if token not in "+-*/()":            num_list.append(int(token))        elif token == "(":            s.push(token)        elif token == ")":            while s.peek() != "(":                num_list.append(s.pop())            # 将"("弹出            s.pop()        else:            while s.size() > 0 and priority_dic.get(token) <= priority_dic.get(s.peek()):                num_list.append(s.pop())            s.push(token)    # 检查栈中是否还有操作符存在,有的话,依依弹出即可    while not s.is_empty():        num_list.append(s.pop())    return "".join([str(i) for i in num_list])
  1. 计算后缀表达式
# 计算后缀def calc_suffix(expression):    s = Stack()    for token in expression:        if token in "0123456789":            s.push(int(token))        else:            # 要考虑到减法和除法顺序不能颠倒的问题            num_first = s.pop()            num_second = s.pop()            tmp_result = convert_to_math(token, num_first, num_second)            s.push(tmp_result)    return s.pop()# 辅助函数,每两个弹出元素的计算结果def convert_to_math(token, num1, num2):    if token == "*":        return num1 * num2    elif token == "/":        return num2 / num1    elif token == "-":        return num2 - num1    elif token == "+":        return num1 + num2
  1. 测试代码如下
# 测试数据convert_ret = infix_to_suffix("3 + 5 * 2 - 5 * ( 3 + 2 )")calcu_ret = calc_suffix(convert_ret)print(calcu_ret)

队列

队列是一种有次序的数据集合,表现为向队列中添加数据时,永远在尾端添加,而移除数据则永远从队首删除,专业化的表述为:先进先出的结构特点(FIFO),越早进入栈结构的数据越早离开栈

  1. 生活中的例子(加深理解)
  • 排队买票,一般情况下,你应该从买票队伍的最后一个位置进入列队
  • 一台打印机面向多个用户或是程序的情况
  1. 队列实现

队列也可以由数组或是链表结构设计实现,数组的实现(循环数组的方法)要难于链表,但是性能更优,这里还是基于python的列表实现

# 利用内置数据类型列表构建队列数据结构class Queue():    def __init__(self):        self._items = []    def size(self):        return len(self._items)    def enqueue(self, data):        self._items.insert(0, data)    def dequeue(self):        return self._items.pop()    def is_empty(self):        return len(self._items) == 0
  1. 经典应用
  • 模拟打印机处理多任务
  • 模拟道路交通,超市结账排队情况等
  • CPU访问
  • 多个进程访问同一个CPU,新进程会被添加到队列,这个队列就是等待CPU使用的进程
  1. 经典实现

约瑟夫/热土豆理论

热土豆问题:几个小朋友,围成一圈,用一个热土豆(或别的什么东西),数一个数就抛给下一个人,每数到3,手上有土豆的人就站出来,然后继续,问哪个位置的人最后剩下?

# 热土豆问题逻辑代码def hot_potato(lists, num):    q = Queue()    # 按人名将数据项入队    for person in lists:        q.enqueue(person)# 直到只剩下一个人,游戏结束    while q.size() > 1:        for i in range(num):            q.enqueue(q.dequeue())        q.dequeue()# 将最后剩下的人返回    return q.dequeue()# 模拟热土豆的实例,参数分表代表参加游戏的人名以及土豆每传递几次就出队一个人last_person = hot_potato(["A", "B", "C", "D", "E", "F", "G", "H", "I", "L", "M", "N", "O"], 3)print(last_person)

生产者消费者模型

这应该是最常用的了,经常要构建消息队列

import threadingimport queue  def producer():    """    模拟生产者    :return:    """    for i in range(10):        print("生产包子%s" %i)        q.put("包子 %s" % i)     print("开始等待所有的包子被取走...")    q.join()  # 等待这个包子队列被消费完毕    print("所有的包子被取完了...")  def consumer(n):    """    模拟消费者    :return:    """    while q.qsize() > 0:        print("%s 取到" % n, q.get())        q.task_done()  # 每取一个包子,便告知队列这个任务执行完了 q = queue.Queue() p = threading.Thread(target=producer,)p.start() c1 = consumer("xiaochen")

总结

本文基于python,分享了关于栈结构和线性表的相关实现和特点,并辅以相关的代码用各自的结构实现经典应用。希望你对这两种线性表有了一定的了解(数据结构是干嘛的,就是提高效率的,某些场景通过数据结构的套路辅以相关的算法,就会容易很多)

我是一名奋战在编程界的pythoner,工作中既要和数据打交道,也要和erp系统,web网站保持友好的沟通……时不时的会分享一些提高效率的编程小技巧,在实际应用中遇到的问题以及解决方案,或者源码的阅读等等,欢迎大家一起来讨论!如果觉得写得还不错,欢迎关注点赞,谢谢。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值