第六节--堆栈与队列算法

第六节–堆栈与队列算法

堆栈结构在计算机领域中的应用相当广泛,常用于计算机程序的运行,例如递归调用,子程序的调用。堆栈在python程序设计中包含两种方式,分别是数组结构(在python语言中是以列表List仿真数组结构)与链表结构

队列在计算机领域中的应用也相当广泛,例如计算机的模拟(simulation),cpu的作业调度(job scheduling)以及图形遍历的广度优先搜索法(BFS)

堆栈与队列都是抽象数据类型(Abstract Data Type,ADT)

一.数组实现堆栈

以数据结构来实现堆栈的好处是设计的算法相当简单,但是如果堆栈本身的大小是变动的,而数组大小只能事先规划和声明好,那么数组规划太大会浪费空间,规划调小则不够用

python的相关算法如下:

# 判断是否为空堆栈
def isEmpty():
  if top==-1:
    return True
  else:
    return False
# 将指定的数据存入堆栈
def push(data):
  global top
  global MAXSTACK
  global stack
  if top>=MAXSTACK-1:
    print("堆栈已满,无法再加入")
  else:
    top+=1
    stack[top]=data
# 从堆栈取出数据
def pop():
  global top
  global stack
  if isEmpty():
    print("堆栈是空的")
  else:
    print("弹出的元素为:%d" %stack[top])
    top=top-1

1.功能要求

用循环来控制元素压人堆栈或弹出堆栈,并仿真堆栈的各种操作,此堆栈最多可容纳100个元素,其中必须包括压人(push)和弹出(pop)函数,在最后输出堆栈内的所有元素

2.源程序

MAXSTACK=100 #定义堆栈的最大容量
global stack
stack=[None]*MAXSTACK  #堆栈的数组声明
top=-1 #堆栈的顶端

#判断是否为空堆栈
def isEmpty():
    if top==-1:
        return True
    else:
        return False

#将指定的数据压入堆栈
def push(data):
    global top
    global MAXSTACK
    global stack
    if top>=MAXSTACK-1:
        print('堆栈已满,无法再加入')
    else:
        top +=1
        stack[top]=data #将数据压入堆栈

#从堆栈弹出数据*/
def pop():
    global top
    global stack
    if isEmpty():
        print('堆栈是空')
    else:
        print('弹出的元素为: %d' % stack[top])
        top=top-1     
        
#主程序
i=2
count=0
while True:
    i=int(input('要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: '))
    if i==-1:
        break
    elif i==1:
        value=int(input('请输入元素值:'))
        push(value)
    elif i==0:
        pop()

print('============================')
if top <0:
    print('\n 堆栈是空的')
else:
    i=top
    while i>=0:
        print('堆栈弹出的顺序为:%d' %(stack[i]))
        count +=1
        i =i-1
    print 

print('============================')  

3.运行结果

要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 1
请输入元素值:9
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 1
请输入元素值:5
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 1
请输入元素值:7
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 0
弹出的元素为: 7
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: -1
============================
堆栈弹出的顺序为:5
堆栈弹出的顺序为:9
============================

二.链表实现堆栈

用链表来实现堆栈的优点是随时可以动态改变链表长度,能有效利用内存资源,不过缺点是设计的算法较复杂

python的相关算法如下:

class Node:
  def __init__(self):
    self.data=0
    self.next=None
    
    
top=None
def isEmpty():
  global top
  if(top==None):
    return 1
  else:
    return 0
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 isEmpty():
    print("===当前为空堆栈===")
    return -1
  else:
    ptr=top         # 指向堆栈的顶端
    top=top.next    # 将堆栈顶端的指针指向下一个节点
    temp=ptr.data   # 弹出堆栈的数据
    return temp     # 将从堆栈弹出的数据返回给主程序

1.功能要求

循环来控制元素的压人堆栈或弹出堆栈,其中必须包括压人(push)与弹出(pop)函数

2.源程序

class Node:  #堆栈链结节点的声明
    def __init__(self):
        self.data=0  #堆栈数据的声明
        self.next=None  #堆栈中用来指向下一个节点

top=None

def isEmpty():
    global top
    if(top==None):
        return 1
    else:
        return 0
    
#将指定的数据压入堆栈
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 isEmpty():
        print('===目前为空堆栈===')
        return -1
    else:
        ptr=top         #指向堆栈的顶端
        top=top.next    #将堆栈顶端的指针指向下一个节点
        temp=ptr.data   #弹出堆栈的数据
        return temp     #将从堆栈弹出的数据返回给主程序
        
#主程序
while True:
    i=int(input('要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: '))
    if i==-1:
        break
    elif i==1:
        value=int(input('请输入元素值:')) 
        push(value)
    elif i==0:
        print('弹出的元素为%d' %pop())
    
print('============================')
while(not isEmpty()): #将数据陆续从顶端弹出
    print('堆栈弹出的顺序为:%d' %pop()) 
print('============================')

3.运行结果

要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 1
请输入元素值:8
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 1
请输入元素值:6
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 1
请输入元素值:7
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: 0
弹出的元素为7
要压入堆栈,请输入1,要弹出则输入0,停止操作则输入-1: -1
============================
堆栈弹出的顺序为:6
堆栈弹出的顺序为:8
============================

三.汉诺塔问题的求解算法

在古印度神庙,庙中有三根木桩,天神希望和尚们把某些数量大小不同的盘子,从第一个木桩全部移动到第三个木桩

搬动时必须遵守下列规则:

  1. 直径较小的盘子永远只能置于直径较大的盘子上
  2. 每一次只能移动一个盘子,而且只能从最上面的盘子开始移动

结论如下:例如当有n个盘子时,可将汉诺塔问题归纳成三个步骤
步骤1:将n-1个盘子从木桩1移动到木桩2
步骤2:将第n个最大盘子从木桩1移动到木桩3
步骤3:将n-1个盘子从木桩2移动到木桩3

def hanoi(n,p1,p2,p3):
  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)

1.源程序

def hanoi(n, p1, p2, p3):
    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)

j=int(input('请输入要移动盘子的数量:'))
hanoi(j,1, 2, 3)

2.运行结果

请输入要移动盘子的数量:5
盘子从 1 移到 3
盘子从 1 移到 2
盘子从 3 移到 2
盘子从 1 移到 3
盘子从 2 移到 1
盘子从 2 移到 3
盘子从 1 移到 3
盘子从 1 移到 2
盘子从 3 移到 2
盘子从 3 移到 1
盘子从 2 移到 1
盘子从 3 移到 2
盘子从 1 移到 3
盘子从 1 移到 2
盘子从 3 移到 2
盘子从 1 移到 3
盘子从 2 移到 1
盘子从 2 移到 3
盘子从 1 移到 3
盘子从 2 移到 1
盘子从 3 移到 2
盘子从 3 移到 1
盘子从 2 移到 1
盘子从 2 移到 3
盘子从 1 移到 3
盘子从 1 移到 2
盘子从 3 移到 2
盘子从 1 移到 3
盘子从 2 移到 1
盘子从 2 移到 3
盘子从 1 移到 3

四.八皇后问题的求解算法

如果放置新皇后的行(或列)的8个位置都没有办法放置新皇后(放入任何一个位置,都会被先前放置的就皇后吃掉),就必须从堆栈中弹出前一个皇后的位置,并在该行(或该列中重新寻找另一个新的位置来放,再将该位置压人堆栈中,而这种方式就是一种回溯(Backtracking)算法的应用

N皇后问题的解答就是结合堆栈和回溯两种数据结构,以逐行(或逐列)寻找新皇后合适的位置(如果找不到,就回溯到前一行寻找前一个皇后的另一个新位置,以此类推)的方式来寻找N皇后问题的其中一组解答

1.源程序

global queen
global number
EIGHT=8 #定义堆栈的最大容量
queen=[None]*8 #存放8个皇后的行位置

number=0    #计算总共有几组解的总数
#决定皇后存放的位置
#输出所需要的结果
def print_table():
    global number
    x=y=0
    number+=1
    print('')
    print('八皇后问题的第%d组解\t' %number)
    for x in range(EIGHT):
        for y in range(EIGHT):
            if x==queen[y]:
                print('<q>',end='')
            else:
                print('<->',end='')
        print('\t')
    input('\n..按下任意键继续..\n')

#测试在(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=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=i+1

#主程序
decide_position(0)

2.运行结果

八皇后问题的第1组解	
<q><-><-><-><-><-><-><->	
<-><-><-><-><-><-><q><->	
<-><-><-><-><q><-><-><->	
<-><-><-><-><-><-><-><q>	
<-><q><-><-><-><-><-><->	
<-><-><-><q><-><-><-><->	
<-><-><-><-><-><q><-><->	
<-><-><q><-><-><-><-><->	

..按下任意键继续..


八皇后问题的第2组解	
<q><-><-><-><-><-><-><->	
<-><-><-><-><-><-><q><->	
<-><-><-><q><-><-><-><->	
<-><-><-><-><-><q><-><->	
<-><-><-><-><-><-><-><q>	
<-><q><-><-><-><-><-><->	
<-><-><-><-><q><-><-><->	
<-><-><q><-><-><-><-><->	

..按下任意键继续..

五.数组实现队列

与堆栈不同的是需要拥有两种基本操作:加入与删除,而且要使用front与rear两个指针来分别指向队列的前端与末尾,缺点是数组大小无法根据队列的实际需要来动态申请,只能说明固定的大小

MAXSIZE=4
queue=[0]*MAXSIZE
front=-1
rear=-1       # 队列为空时,front=-1,rear=-1

队列操作的过程用python语言将以数组操作队列的相关算法:

def enqueue(item):    # 将新数据加入Q的末尾,返回新队列
  global rear
  global MAX_SIZE
  global queue
  if rear==MAX_SIZE-1:
    print("队列已满!")
  else:
    rear+=1
    queue[rear]=item
def dequeue(item):    # 删除队列前端的数据,返回新队列
  global rear
  global MAX_SIZE
  global front
  global queue
  if front==rear:
    print("队列为空!")
  else:
    front+=1
    item=queue[front]
def FRONT_VALUE(Queue):   #返回队列前端的值
  global rear
  global front
  global queue
  if front==rear:
    print("这是空队列!")
  else:
    print(queue[front])

1.功能要求

打印输出队列前端的值

2.源程序

import sys

MAX=10			#定义队列的大小
queue=[0]*MAX
front=rear=-1
choice=''
while rear<MAX-1 and choice !='e':
    choice=input('[a]表示加入一个数值,[d]表示取出一个数值,[e]表示跳出此程序: ')
    if choice=='a':
        val=int(input('[请输入数值]: '))
        rear+=1
        queue[rear]=val
    elif choice=='d':
        if rear>front:
            front+=1
            print('[取出数值为]: [%d]' %(queue[front]))
            queue[front]=0
        else:
            print('[队列已经空了]')
            sys.exit(0)
    else:
        print()

print('------------------------------------------')
print('[输出队列中的所有元素]:')

if rear==MAX-1:
    print('[队列已满]')
elif front>=rear:
    print('没有')
    print('[队列已空]')
else:
    while rear>front:
        front+=1
        print('[%d] ' %queue[front],end='')
    print()
    print('------------------------------------------')
print()

3.运行结果

[a]表示加入一个数值,[d]表示取出一个数值,[e]表示跳出此程序: a
[请输入数值]: 12
[a]表示加入一个数值,[d]表示取出一个数值,[e]表示跳出此程序: a
[请输入数值]: 8
[a]表示加入一个数值,[d]表示取出一个数值,[e]表示跳出此程序: a
[请输入数值]: 1
[a]表示加入一个数值,[d]表示取出一个数值,[e]表示跳出此程序: e

------------------------------------------
[输出队列中的所有元素]:
[12] [8] [1] 
------------------------------------------

六.链表实现队列

class student:
  def __init__(self):
    self.name=" "*20
    self.score=0
    self.next=None
    
front=student()
rear=student()
front=None
rear=None

在队列中加入新的节点,等于加到此队列的末端;在队列中删除节点,就是将此队列最前端的节点删除

def enqueue(name,score):
  global front
  global rear
  new_data=student()
  new_data.name=name
  new_data.score=score
  if rear==None:
    front=new_data
  else:
    rear.next=new_data
    
  rear=new_data
  new_data.next=None
def dequeue():
  global front
  global rear
  if front==None:
    print("队列已空!")
  else:
    print("姓名:%s\t成绩:%d...取出" %(front.name,front.score))
    front=front.next

1.功能要求

链表中元素节点仍为学生姓名及成绩的结构数据

2.源程序

class student:
    def __init__(self):
        self.name=' '*20
        self.score=0
        self.next=None
        
front=student()
rear=student()
front=None
rear=None

def enqueue(name, score):  # 把数据加入队列
    global front
    global rear
    new_data=student()  # 分配内存给新元素
    new_data.name=name  # 给新元素赋值
    new_data.score = score
    if rear==None:      # 如果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:
        print('姓名:%s\t成绩:%d ....取出' %(front.name, front.score))
        front = front.next    # 将队列前端移到下一个元素
        
def show():     # 显示队列中的数据
    global front
    global rear
    ptr = front
    if ptr == None:
        print('队列已空!')
    else:
        while ptr !=None: # 从front到rear遍历队列
            print('姓名:%s\t成绩:%d' %(ptr.name, ptr.score))
            ptr = ptr.next

select=0
while True:
    select=int(input('(1)加入 (2)取出 (3)显示 (4)离开 => '))
    if select==4:
        break
    if select==1:
        name=input('姓名: ')
        score=int(input('成绩: '))
        enqueue(name, score)
    elif select==2:
        dequeue()
    else:
        show()

3.运行结果

(1)加入 (2)取出 (3)显示 (4)离开 => 1
姓名: John
成绩: 96
(1)加入 (2)取出 (3)显示 (4)离开 => 1
姓名: SMith
成绩: 87
(1)加入 (2)取出 (3)显示 (4)离开 => 3
姓名:John	成绩:96
姓名:SMith	成绩:87
(1)加入 (2)取出 (3)显示 (4)离开 => 4

七.双向队列

双向队列(Double Ended Queues,DEQue)为一个有序线性表,加入与删除可在队列的任意一端进行
未命名文件.png

双向队列的应用可以区分为两种:第一种是数据只能从一端加入,但可从两端取出;;另一种则是可从两端加入,但从一端取出

下面我们将讨论第一种输入限制的双向队列

1.源程序

class Node:
    def __init__(self):
        self.data=0
        self.next=None
        
front=Node()
rear=Node()
front=None
rear=None

#方法enqueue:队列数据的加入
def enqueue(value):
    global front
    global rear
    node=Node()  #建立节点
    node.data=value
    node.next=None
    #检查是否为空队列
    if rear==None:
        front=node  #新建立的节点成为第1个节点
    else:
        rear.next=node  #将节点加入到队列的末尾
    rear=node  #将队列的末尾指针指向新加入的节点

#方法dequeue:队列数据的取出
def dequeue(action):
    global front
    global rear
    #从队列前端取出数据
    if not(front==None) and action==1:
        if front==rear:
            rear=None
        value=front.data  #将队列数据从前端取出
        front=front.next  #将队列的前端指针指向下一个
        return value
    #从队列末尾取出数据
    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
    
print('用链表来实现双向队列')
print('====================================')

ch='a'
while True:
    ch=input('加入请按 a,取出请按 d,结束请按 e:')
    if ch =='e':
        break
    elif ch=='a':
        item=int(input('加入的元素值:'))
        enqueue(item)
    elif ch=='d':
        temp=dequeue(1)
        print('从双向队列前端按序取出的元素数据值为:%d' %temp)
        temp=dequeue(2)
        print('从双向队列末尾按序取出的元素数据值为:%d' %temp)
    else:
        break

2.运行结果

用链表来实现双向队列
====================================
加入请按 a,取出请按 d,结束请按 e:a
加入的元素值:98
加入请按 a,取出请按 d,结束请按 e:a
加入的元素值:86
加入请按 a,取出请按 d,结束请按 e:d
从双向队列前端按序取出的元素数据值为:98
从双向队列末尾按序取出的元素数据值为:86
加入请按 a,取出请按 d,结束请按 e:e

八.优先队列

posted @ 2019-02-01 18:33  LQ6H  阅读(370)  评论(0编辑  收藏  举报