• 06 堆栈与队列算法


    二者均是抽象数据类型( Abstract Data Type, ADT )
    堆栈在 Python 中包含两种方式,分别是数组结构(以List仿真数组结构)和链表结构

    • 用数组实现堆栈
      设计算法简单。但是,如果堆栈本身大小是可以变动的,而数组大小只能事先规划和声明好,那么数组规划大了会浪费空间,小了不够用。

      • 判断是否为空堆栈
          def is_empty():
              global top
              if top == -1:
                  return True
              else:
                  return False
        
      • 入栈
          def push(data):
              global top
              global MAXSTACK  # 堆栈最大容量
              global stack  # stack = [None] * MAXSTACK 声明堆栈
              if top >= MAXSTACK - 1:
                  return "堆栈已满"
              else:
                  top += 1
                  stack[top] = data
        
      • 出栈
          def pop():
              global top
              global stack
              if is_empty():
                  return "堆栈是空的"
              else:
                  data = stack[top]
                  top -= 1
                  return data
        
    • 用链表实现堆栈
      算法复杂。动态改变链表长度,有效利用内存资源。

        class Node:
            def __init__(self):
                self.data = 0
                self.next = None
      
      • 是否为空
          def is_empty():
              global top
              if top is None:
                  return True
              else:
                  return False
        
      • 入栈
          def push(data):
              global top
              new_add_node = Node()
              new_add_node.data = data
              new_add_node.next = top  # 新的数据放在头部
              top = new_add_node
        
      • 出栈
          def pop():
              global top
              if is_empty():
                  return "堆栈是空的"
              else:
                  ptr = top
                  top = top.next  # 从头部删除
                  data = ptr.data
                  return data
        
    • 汉诺塔问题
      递归(反复执行+有出口)+ 堆栈。T = 2^n - 1
      (1) 将 n-1 个盘子从 1 移到 2
      (2) 将第 n 个最大的盘子从 1 移到 3
      (3) 将 n-1 个盘子从 2 移到 3

        def hanoi(n, p1=1, p2=2, p3=3):
            if n == 1:
                print("盘子从 %d 移到 %d" % (p1, p3))
            else:
                hanoi(n-1, p1, p3, p2)
                print('盘子从 %d 移到 %d' % (p1, p3))
                hanoi(n-1, p2, p1, p3)
      
    • 八皇后问题
      在棋盘放入一个新皇后,且这个位置不会被就皇后吃掉(不限定走几格:直吃、横吃、对角斜吃),便将新皇后压入堆栈。
      如果放置新皇后的行(或列)的 8 个位置都没有办法放置一个新皇后(放入任何一个位置都会被吃掉),就必须从堆栈中弹出前一个皇后的位置,并在该行(或该列)重新寻找另一个位置,将新位置压入堆栈(如果找不到,就回溯到前一行寻找前一个皇后的另一个新位置)。回溯( Backtracking )算法。

        global queen
        global number
        EIGHT = 8  # 定义堆栈的最大容量
        queen = [None] * 8  # 存放8个皇后的行位置
      
        number = 0  # 计算共有几组解
      
        def print_table():
            global number
            x = y = 0
            number += 1
            print('第 %d 组解' % number)
            for x in range(EIGHT):
                for y in range(EIGHT):
                    if x == queen[y]:
                        print('<q>', end='')
                    else:
                        print('<->', end='')
                print('')
            input('
      ..按下任意键继续..
      ')
        
        # 测试在(row, col)上是否受到攻击。遭受攻击返回 1,否则返回 0
        def attack(row, col):
            global queen
            i = 0
            atk = 0
            offset_row = offset_col = 0
            while atk != 1 and i < col:
                offset_col = abs(i - col)
                offset_row = abs(queen[i] - row)
                # 判断两皇后是否在同一行或同一对角线上
                if queen[i] == row or offset_row == offset_col:
                    atk = 1
                i += 1
            return atk
      
        def decide_position(value):
            global queen
            i = 0
            while i < EIGHT:
                if attack(i, value) != 1:
                    queen[value] = i
                    if value == 7:
                        print_table()
                    else:
                        decide_position(value+1)
                i += 1
      
        decide_position(0)
      
    • 用数组实现队列
      算法简单。缺点数组大小无法根据队列的实际需求来动态申请,只能声明固定的大小。
      用 front 和 rear 两个指针来分别指向队列的前端和末尾。

        NAXSIZE = 4
        queue = [0] * MAXSIZE
        front = -1
        rear = -1
      

      添加一个元素,将 rear 值加 1;
      取出一个元素,将 front 值加 1。
      rear = MAXSIZE - 1,表示队列已满。

      • 入队
          def enqueue(item):
              global rear
              global MAXSIZE
              global queue
              if rear == MAXSIZE - 1:
                  print('队列已满')
              else:
                  rear += 1
                  queue[rear] = item  # 将新数据加到队列的末尾
        
      • 出队
          def dequeue():
              global rear
              global front
              global queue
              global MAXSIZE
              if front == rear:
                  print('队列已空')
              else:
                  front += 1
                  item = queue[front]
        
      • 返回队首值
          def front_value():
              global rear
              global front
              global queue
              if front == rear:
                  print('这是空队列!')
              else:
                  print(queue[front])
        
    • 用链表实现队列

      • 入队
            def enqueue(item):
                global front
                global rear
                new_data = Node()
                new_data.value = item
                if rear == None:
                    front = new_data
                else:
                    rear.next = new_data  # 将新元素连接到队列末尾
                rear = new_data  # 将 rear 指向新元素,这是新队列的队尾
                new_data.next = None
        
      • 出队
          def dequeue():
              global front
              global rear
              if front == None:
                  print('队列已空')
              else:
                  front = front.next
        
    • 双向队列( Double Ended Queues, DEQue )
      Lfront, Lrear Rfront, Rrear

      • 一端加入,两端取出
          def enqueue(value):
              global front
              global rear
              node = Node()
              node.data = value
              node.next = None
              if rear == None:  # 检查是否为空队列
                  front = node
              else:
                  rear.next = node  # 将节点加入到队列的末尾
              rear = node  # 将尾指针指向新加入的节点
        
          def dequeue(action):
              global front
              global rear
              # action = 1, 从前端取出数据
              if not(front == None) and action == 1:
                  if front == rear:
                      rear = None
                  value = front.data
                  front = front.next
                  return value
              # action = 2, 从队列末尾取出
              elif not (rear == None) and action == 2:
                  startNode = front
                  value = rear.data
                  # 查找队列末尾节点的前一个节点
                  tempNode = front
                  while front.next != rear and front.next != None:
                      front = front.next
                      tempNode = front
                  front = startNode  # 新前端指针
                  rear = tempNode  # 新尾端指针
                  # 队列中仅剩下最后一个节点时,取出后将 front 和 rear 指向 None
                  if front.next == None or rear.next == None:
                      front = None
                      rear = None
                  return value
              else:
                  return -1
        
      • 两端加入,一端取出
    • 优先队列( Priority Queue )
      加入元素时可任意加入,但先输出优先级高者( HPOF, Highest Priority Out First )
      当各元素按输入先后顺序为优先级时,就是普通队列。若以输入顺序的倒序为优先级,则是堆栈。


    堆栈:一组相同数据类型数据的集合,所有的操作都在堆栈顶端进行,具有“先进后出”的特性。
    常见的堆栈应用:

    1. 二叉树和森林的遍历运算,例如中序遍历Inorder、前序遍历Preorder等。
    2. 计算机中央处理单元的中断处理Interrupt Handling。
    3. 图的深度优先搜索法DFS。
    4. 某些所谓的堆栈计算机Stack Computer,采用空地址zero-address 指令,其指令没有操作数,大部分都通过弹出Pop和压入Push两个指令来处理程序。
    5. 递归程序的调用和返回。在每次递归之前,必须先把下一个指令的地址和变量值保存到堆栈中。当从递归返回时,则按序从堆栈顶端取出这些相关值,回到原来执行递归之前的状态,再往下继续执行。
    6. 算数表达式的转换和求值,例如中序法转换成后续法。
    7. 调用子程序和返回处理,例如在执行调用的子程序之前,必须先将返回地址(下一条指令的地址)压入堆栈中,然后才开始执行调用子程序的操作,等到子程序执行完毕之后,在从堆栈中弹出返回地址。
    8. 编译错误处理Compiler Syntax Processing。例如,当编译程发生错误或警告信息时,会将所在的地址压入堆栈中,之后才会显示出错误信息的对照表。

    队列的应用:

    1. 图的广度优先查找BFS
    2. 计算机的模拟
    3. CPU 的作业调度
    4. 外部设备联机并发处理系统
  • 相关阅读:
    BZOJ5104 二次剩余板子
    BZOJ5329 [Sdoi2018]战略游戏 圆方树+虚树
    BZOJ1095 动态点分治
    BZOJ3992: [SDOI2015]序列统计
    kd-tree板子
    thusc2018翻车记
    BZOJ5336 DP套DP
    BZOJ4316 仙人掌DP
    问题 F: 最小花费
    问题 C: 热浪
  • 原文地址:https://www.cnblogs.com/catyuang/p/11760499.html
Copyright © 2020-2023  润新知