···························································· 
第3 章 chapter3 
栈与队列
本章导读
栈和队列是两种特殊的线性结构,是线性表特例。在线性表上的插入、删除等操作
一般不受限制,而在栈和队列上的插入、删除操作会受某种限制,对它们来说,插入和删
除运算均是对首尾两个元素进行的。
本章主要介绍栈和队列的逻辑特征及其在计算机中的存储表示、栈和队列的基本运
算的算法描述,以及栈和队列的应用。
教学目标
本章要求掌握以下内容。
. 栈的基本概念和栈的基本运算。
. 栈的应用。
. 队列的基本概念和队列的基本运算。
. 队列的应用。
图3-1 栈结构示意图
3.1 栈
3.1.1 栈的定义 
栈(Stack)是限定仅在表的一端进行插入或删除操作的线性表。在表中允许进行插
入或删除操作的一端称为栈顶(Top),而另一端称为栈底
(Bottom)。栈的插入和删除操作分别称为进栈和出栈。进
栈是将一个数据元素存放在栈顶,出栈是将栈顶中的元素
取出。若给定栈S=(a1,a2,…,an ),如图3-1所示,a1 是
栈底元素,an 是栈顶元素。由于只允许在栈顶进行插入和
删除操作,因此栈的操作是按“后进先出”的原则进行的。
栈又称为后进先出表,即LIFO(LastInFirstOut)线性表。
不含元素的栈称为空栈。

第◆3 章 栈与队列6 7 
日常生活中有许多类似栈的例子,如洗盘子时,把洗净的盘子一个接一个地向上放
(相当于进栈),取盘子时,则是从上面一个接一个地向下拿(相当于出栈)。
栈的基本运算有4种。
. 初始化栈:将栈置为空栈,不含任何元素,只建立栈顶指针。
. 进栈:向栈顶插入一个新的元素。
. 出栈:删除栈顶元素。
. 判栈空:判断一个栈是否为空栈。
3.1.2 栈的顺序存储及其基本操作的实现
栈的顺序存储结构,简称顺序栈,它是用一组地址连续的存储单元依次存放自栈底
到栈顶的数据元素。栈的Python语言的类定义描述与顺序表的类定义描述类似。 
class Stack(object): 
def __init__(self,size): 
self.MAX = size #栈的大小 
self.s = [] #用列表存储栈里的元素 
self.top = 0 #初始化,若top=0,则为空栈
在这个描述中,列表s用于存放栈中的数据元素,栈中可存放数据的最大容量为
MAX,栈顶指针为top。
在这里,由于栈顶的位置经常变动,所以要设一个栈顶指针top,用于指向下一次进
栈的数据元素的存放位置。当栈中没有数据元素时,栈是空栈,这时top=0。当栈中放
满元素时,top= MAX,表示栈满。若再有数据元素进栈,栈将溢出,称为“上溢” 
(overflow),这是一个错误状态;反之,当top=0时,若再进行出栈操作,则会发生“下溢” 
(underflow)。在应用中,上溢和下溢通常作为控制程序转移的条件。下面以一个例子说
明栈的操作。
设有一个栈S=(a,b,c,d,e),则MAX为5,栈的顺序存储结构如图3-2所示。其
中,图3-2(a)是空栈;图3-2(b)是进栈一个元素a 之后的栈;图3-2(c)是在图3-2(b)基
础上连续将元素b、c、d、e 进栈之后的栈状况,此时是栈满状况,不允许再有元素进栈; 
图3-2(d)是在图3-2(c)基础上出栈一个元素后的栈。由此可见,非空栈中的栈顶指针
top始终指向栈顶元素的下一个位置。
图3-2 栈的顺序存储结构

6 8 ◆数据结构(Python 版) 
顺序栈的基本运算有初始化栈、判栈满、进栈、判栈空和出栈,下面分别介绍这几种运算。
1.初始化栈
初始化栈的算法描述如下。 
class Stack(object): 
def __init__(self,size): 
self.MAX = size 
self.s = [] 
self.top = 0 #初始化,若top=0,则为空栈
if __name__ == '__main__': 
s=Stack(50) #实例化栈,并把栈的最大容量定义成50 
2.判栈满
在判栈满时,如果栈满,则返回True;如果栈不满,则返回False。 
def stackFull(self): 
if self.top == self.MAX: #判栈满!!! 
return True 
else: 
return False 
3.进栈
进栈是在栈顶插入新的元素。假设栈里原有的数据为S=[1,2,3,4,5],由于进栈
时可能遇到栈满(又称“上溢”),导致操作不能进行,所以此时应调用stackFull()方法判
断栈是否满,如果栈满,就返回“真”值,并提示:“栈已经满,不能进行入栈操作!”;如果栈
未满,则栈顶指针加1,插入新元素。进栈的算法描述如下。
例3-1 
class Stack(object): 
def __init__(self,size): 
self.MAX = size 
self.s = [] 
self.top = 0#初始化,若top=0,则为空栈 
def stackFull(self): 
if self.top == self.MAX:#判栈满!!! 
return True 
else: 
return False 
def push(self,x): 
if self.stackFull():#进栈之前检查栈是否已满

第◆3 章 栈与队列6 9 
raise Exception("栈已经满,不能进行入栈操作!") 
else: 
self.s.append(x) 
self.top=self.top+1#push 进去的第一个元素下标为1 
if __name__ == '__main__': 
s=Stack(50) 
s.s=[1,2,3,4,5] 
while True: 
print("----请选择操作方式----") 
print("----1.入栈\n----0.退出") 
number=int(input()) 
if number==1: 
x=eval(input("请输入入栈元素")) 
s.push(x) 
print(s.s) 
elif number==0: 
break 
执行上述程序,结果如下。 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素45 
[1, 2, 3, 4, 5, 45] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素67 
[1, 2, 3, 4, 5, 45, 67] 
----请选择操作方式---- 
----1.入栈
----0.退出
从栈的入栈操作算法可以看出,该算法的时间复杂度为O(1)。
4.判栈空
在判栈空时,如果栈空,则返回True;如果栈不空,则返回False。
判栈空的算法描述如下。 
def stackEmpty(self):

7 0 ◆数据结构(Python 版) 
if self.top == 0:#判栈空 
return True 
else: 
return False 
} 
5.出栈
出栈是从栈顶删除元素。由于出栈时可能遇到栈空(又称为“下溢”),导致操作不能
进行,所以此时应调用stackEmpty()方法判断栈是否为空,如果栈空,就返回True,并提
示:“栈已经空,不能进行出栈操作!”;如果栈未空,则栈顶指针减1,返回出栈元素。出
栈的算法描述如下。
出栈算法描述如下。
例3-2 
class Stack(object): 
def __init__(self,size): 
self.MAX = size 
self.s = [] 
self.top = 0#初始化,若top=0,则为空栈 
def stackEmpty(self): 
if self.top == 0:#判栈空 
return True 
else: 
return False 
def pop(self): 
if self.stackEmpty(): 
raise Exception("栈已经空,不能进行出栈操作! !") 
else: 
self.top=self.top-1 
return self.s.pop()#利用Python 的内建函数pop()实现弹出
if __name__ == '__main__': 
s=Stack(50) 
s.s=[1,2,3,4,5] 
s.top=5 
while True: 
print("----请选择操作方式----") 
print("----1.出栈\n----0.退出") 
number=int(input()) 
if number==1: 
print(s.pop())

第◆3 章 栈与队列7 1 
elif number==0: 
break 
运行上述程序,结果如下。 
----请选择操作方式---- 
----1.出栈
----0.退出
1 
5-
---请选择操作方式---- 
----1.出栈
----0.退出
1 
4-
---请选择操作方式---- 
----1.出栈
----0.退出
从栈的出栈操作算法可以看出,该算法的时间复杂度为O(1)。
6.多个栈共享存储空间
图3-3 两个栈共享一个
存储空间示意图
对于进栈算法中的“上溢”情况,应设法避免,而避免出现
“上溢”的唯一方法是将栈的容量加到足够大。若一个程序使
用多个栈,往往事先难以估计每个栈所需容量,在一个栈发生
“上溢”时,其他栈可能还留有很多空间,此时可以用移动数据
元素位置的方法加以调整,以达到多个栈共享存储空间的
目的。现
在以两个栈为例,讨论共享存储空间(列表s[MAX], 
MAX是预先定义的容量)的方法。可以把两个栈的栈底分
别设在给定存储空间的两端,然后各自向中间伸展,如图3-3 
所示,因为此时仅当两个栈的栈顶相遇时才可能发生上溢,这
样两个栈之间可以做到互补余缺,使得某个栈实际可利用的
最大空间大于MAX/2。这里设第一个栈自顶向下伸展,第二
个栈自底向上伸展,t1指向第一个栈的栈顶元素的下一个位
置,t2指向第二个栈的栈顶元素的上一个位置,两个栈的初
态分别为t1=0,t2=MAX-1,上溢条件是t2+1=t1。双栈
的Python语言描述如下。 
class Doublestack(object): 
def __init__(self, size):

7 2 ◆数据结构(Python 版) 
self.s = ['null' for i in range(size)] #s 栈里初始化均填入null 
self.MAX = size #初始化容量 
self.t1 = -1 #第一个栈的栈顶指针 
self.t2 = size #第二个栈的栈顶指针
下面介绍双栈的入栈、出栈操作。
1)入栈
向第i 个栈插入元素x,若插入成功,则输出入栈之后的结果,否则给出栈满信息,算
法如下。
例3-3 
#两个栈共享入栈操作
class Doublestack(object): 
def __init__(self, size): 
self.s = ['null' for i in range(size)] #s 栈里初始化均填入null 
self.MAX = size #初始化容量 
self.t1 = -1 #第一个栈的栈顶指针 
self.t2 = size #第二个栈的栈顶指针 
def StackFull(self): 
if self.t1 + 1 == self.t2: 
print('共享空间栈已满') 
return True 
else: 
return False 
def StackPush(self, flag, data): 
if self.StackFull(): 
print('无法再添加数据,栈满') 
else: 
if flag == 1: 
self.t1 += 1 
self.s[self.t1] = data 
else: 
self.t2 -= 1 
self.s[self.t2] = data 
if __name__ == '__main__': 
#实例化双栈 
s=Doublestack(10) 
print(s.s) 
while True: 
print("----请选择操作方式----")

第◆3 章 栈与队列7 3 
print("----1.入栈\n----0.退出") 
number=int(input()) 
if number==1: 
x=eval(input("请输入入栈元素给x:")) 
i= eval(input("请输入1 或者2 选择向哪个栈进行入栈操作,并将选择赋值
给i:")) 
s.StackPush(i,x) 
print(s.s) 
elif number==0: 
break 
运行上述程序,结果如下。 
['null', 'null', 'null', 'null', 'null', 'null', 'null', 'null', 'null', 'null'] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 12 
请输入1 或者2 选择向哪个栈进行入栈操作,并将选择赋值给i: 1 
[12, 'null', 'null', 'null', 'null', 'null', 'null', 'null', 'null', 'null'] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 34 
请输入1 或者2 选择向哪个栈进行入栈操作,并将选择赋值给i: 2 
[12, 'null', 'null', 'null', 'null', 'null', 'null', 'null', 'null', 34] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 56 
请输入1 或者2 选择向哪个栈进行入栈操作,并将选择赋值给i: 1 
[12, 56, 'null', 'null', 'null', 'null', 'null', 'null', 'null', 34] 
----请选择操作方式---- 
----1.入栈
----0.退出
由前述可知,该算法的时间复杂度为O(1)。
2)出栈
第i 个栈出栈算法是,若第i(i=1,2)个栈为空,则函数给出栈空的信息,否则显示
出栈后栈内数据的变化情况。

7 4 ◆数据结构(Python 版) 
例3-4 
#两个栈共享出栈操作
class Doublestack(object): 
def __init__(self, size): 
self.s = ['null' for i in range(size)] #s 栈里初始化均填入null 
self.MAX = size #初始化容量 
self.t1 = -1 #第一个栈的栈顶指针 
self.t2 = size #第二个栈的栈顶指针 
def StackEmpty(self): 
if (self.t1 == -1 and self.t2 == self.size): 
print('共享空间栈为空') 
return True 
else: 
return False 
def StackPop(self, flag): 
if flag == 1: 
self.s[self.t1]='null' 
self.t1-=1 
else: 
self.s[self.t2]='null' 
self.t2+=1 
if __name__ == '__main__': 
#实例化双栈 
s=Doublestack(10) 
#出栈前对栈进行初始化操作 
s.s=[34,56,78,'null', 'null', 'null', 'null', 'null',45,90] 
s.t1=2 #第一个栈的栈顶指针位置 
s.t2=8 #第二个栈的栈顶指针位置 
print(s.s) 
while True: 
print("----请选择操作方式----") 
print("----1.出栈\n----0.退出") 
number=int(input()) 
if number==1: 
i= eval(input("请输入1 或者2 选择对哪个栈进行出栈操作,并将选择赋值
给i:")) 
s.StackPop(i) 
print(s.s) 
elif number==0: 
break

第◆3 章 栈与队列7 5 
运行上述程序,结果如下。 
[34, 56, 78, 'null', 'null', 'null', 'null', 'null', 45, 90] 
----请选择操作方式---- 
----1.出栈
----0.退出
1
请输入1 或者2 选择对哪个栈进行出栈操作,并将选择赋值给i: 1 
[34, 56, 'null', 'null', 'null', 'null', 'null', 'null', 45, 90] 
----请选择操作方式---- 
----1.出栈
----0.退出
1
请输入1 或者2 选择对哪个栈进行出栈操作,并将选择赋值给i: 2 
[34, 56, 'null', 'null', 'null', 'null', 'null', 'null', 'null', 90] 
----请选择操作方式---- 
----1.出栈
----0.退出
由前述可知,该算法的时间复杂度为O(1)。
3.1.3 栈的链式存储及其基本操作的实现
当栈的最大容量事先不能估计时,也可采用链表存储结构,简称为链栈,其结点类型
定义为 
class Node(object): 
def __init__(self, data=None): 
self.data = data #链栈的数据域 
self.next = None #链栈的指针域
设top是指向栈顶结点的指针,其初值为空,即在初始状态下,栈中没有任何结点。
如图3-4(a)所示,当把一个数据元素x 入栈时,先向系统申请一个结点p,置其数据域值
为x,把top结点作为p 结点的直接后继,top改指到p 结点,如图3-4(b)所示。出栈时, 
先取出栈顶结点中的数据,然后把该结点链域的值赋给栈顶指针top,释放该结点,如
图3-4(c)所示。
链栈的主要运算有进栈和出栈。
1.进栈
当向链栈插入一个新元素时,首先向系统申请一个结点的存储空间,将新元素的值
写入新结点的数据域中,然后修改栈顶指针。设要插入的新元素为x,进栈的算法描述
如下。

7 6 ◆数据结构(Python 版) 
图3-4 栈的插入和删除运算 
例3-5 
class Node(object): 
def __init__(self, data=None): 
self.data = data #链栈的数据域 
self.next = None #链栈的指针域
class Crea_ListStack(object): 
def __init__(self): 
self.top = Node() 
self.count = 0 
#获取长度 
def get_length(self): 
return self.count 
#判断链栈是否为空,链栈没有满栈情况 
def Empty(self): 
return self.count == 0 
#入栈 
def push(self, x): 
node1 = Node(x) 
if self.Empty(): 
self.top = node1 
else: 
node1.next = self.top 
self.top = node1 
self.count += 1 
#输出栈里的元素 
def show_stack(self):

第◆3 章 栈与队列7 7 
s = [] 
if self.Empty(): 
raise IndexError('栈是空的') 
else: 
j = self.count 
p = self.top 
while j > 0 and p: 
s.append(p.data) 
p = p.next 
j -= 1 
print(s) 
if __name__ == '__main__': 
s=Crea_ListStack() 
while True: 
print("----请选择操作方式----") 
print("----1.入栈\n----0.退出") 
number=int(input()) 
if number==1: 
x=eval(input("请输入入栈元素给x:")) 
s.push(x) 
s.show_stack() 
elif number==0: 
break 
上述程序的运行结果如下。 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 34 
[34] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 6 
[6, 34] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 5

7 8 ◆数据结构(Python 版) 
[5, 6, 34] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 4 
[4, 5, 6, 34] 
----请选择操作方式---- 
----1.入栈
----0.退出
1
请输入入栈元素给x: 3 
[3, 4, 5, 6, 34] 
----请选择操作方式---- 
----1.入栈
----0.退出
由前述可知,该插入算法的时间复杂度为O(1)。
2.出栈
出栈时,先取出栈顶元素的值,再修改栈顶指针,最后输出原栈顶结点数据,并显示
出栈操作后栈内的数据情况。出栈的算法描述如下。
例3-6 
#链栈出栈操作
class Node(object): 
def __init__(self, data=None): 
self.data = data #链栈的数据域 
self.next = None #链栈的指针域
class Crea_ListStack(object): 
def __init__(self): 
self.top = Node() 
self.count = 0 
#获取长度 
def get_length(self): 
return self.count 
#判断链栈是否为空,链栈没有满栈情况 
def Empty(self): 
return self.count == 0 
#入栈

第◆3 章 栈与队列7 9 
def push(self, x): 
node1 = Node(x) 
if self.Empty(): 
self.top = node1 
else: 
node1.next = self.top 
self.top = node1 
self.count += 1 
#出栈 
def pop(self): 
if self.Empty(): 
raise IndexError('栈是空的') 
else: 
self.count -= 1 
x=self.top.data 
self.top = self.top.next 
return x 
#输出栈里的元素 
def show_stack(self): 
s = [] 
if self.Empty(): 
raise IndexError('栈是空的') 
else: 
j = self.count 
p = self.top 
while j > 0 and p: 
s.append(p.data) 
p = p.next 
j -= 1 
print(s) 
if __name__ == '__main__': 
s=Crea_ListStack() 
while True: 
print("----请选择操作方式----") 
print("----1.入栈\n----2.出栈\n----0.退出") 
number=int(input()) 
if number==1: 
x=eval(input("请输入入栈元素给x:")) 
s.push(x) 
s.show_stack() 
elif number==2: 
print("将出栈数据赋给x")

8 0 ◆数据结构(Python 版) 
x=s.pop() 
print("输出出栈数据") 
print(x) 
print("输出栈里当前数据") 
s.show_stack() 
elif number==0: 
break 
上述程序的运行结果如下所示。 
----请选择操作方式---- 
----1.入栈
----2.出栈
----0.退出
1
请输入入栈元素给x: 34 
[34] 
----请选择操作方式---- 
----1.入栈
----2.出栈
----0.退出
1
请输入入栈元素给x: 56 
[56, 34] 
----请选择操作方式---- 
----1.入栈
----2.出栈
----0.退出
1
请输入入栈元素给x: 78 
[78, 56, 34] 
----请选择操作方式---- 
----1.入栈
----2.出栈
----0.退出
2
将出栈数据赋给x 
输出出栈数据
78 
输出栈里当前数据
[56, 34] 
----请选择操作方式---- 
----1.入栈

第◆3 章 栈与队列8 1 
----2.出栈
----0.退出
2
将出栈数据赋给x 
输出出栈数据
56 
输出栈里当前数据
[34] 
----请选择操作方式---- 
----1.入栈
----2.出栈
----0.退出
由前述可知,该算法的时间复杂度为O(1)。
3.1.4 栈的应用举例
栈是计算机软件中应用最广泛的数据结构之一,比如,编译系统中的表达式求值、程
序递归的实现、子程序的调用和返回地址的处理等。下面介绍栈的几个应用实例。
1.算术表达式的计算
表达式求值是编译系统中的一个基本问题,目的是把人们平时书写的算术表达式变
成计算机能够理解并能正确求值的表达方法。
算术表达式中包含算术运算符和算术量;各运算符之间存在优先级,运算时必须按
优先级顺序进行运算,先运算级别高的,后运算级别低的,而不能简单地从左到右进行运
算。因此,进行表达式运算时,必须设置两个栈:一个栈用于存放运算符;另一个栈用于
存放操作数。在表达式运算过程中,编译程序从左到右进行扫描,遇到操作数,就把操作
数放入操作数栈;遇到运算符时,要比较该运算符与运算符栈的栈顶。如果该运算符的
优先级高于栈顶运算符的优先级,就把该运算符进栈,否则退栈。退栈后,在操作数栈中
退出两个元素,其中先退出的元素在运算符右,后退出的元素在运算符左,然后用运算符
栈中退出的栈顶元素(运算符)进行运算,运算的结果存入操作数栈中。反复进行上述操
作,直到扫描结束。此时,运算符栈为空,操作数栈只有一个元素,即最终的运算结果。
例3-7 用栈求表达式6-8/4+3*5的值,栈的变化见表3-1。
表3-1 用栈求表达式6-8/4+3*5的值
步 骤操作数栈运算符栈说 明
开始开始时两个栈为空
1 6 扫描到“6”,进入操作数栈
2 6 - 扫描到“-”,进入运算符栈
3 68 - 扫描到“8”,进入操作数栈

续表
8 2 ◆数据结构(Python 版) 
步 骤操作数栈运算符栈说 明
4 68 -/ 扫描到“/”,进入运算符栈
5 684 -/ 扫描到“4”,进入操作数栈
6 6 - 扫描到“+”,则“/”,“8”,“4”退栈
7 62 - 计算8/4=2,进入操作数栈
8 扫描到“+”,则“-”,“6”,“2”退栈
9 4 6-2=4,进入操作数栈
10 4 + 扫描到“+”,进入运算符栈
11 43 + 扫描到“3”,进入操作数栈
12 43 +* 扫描到“*”,进入运算符栈
13 435 +* 扫描到“5”,进入操作数栈
14 4 + 扫描完,“*”,“3”,“5”退栈
15 415 + 3*5=15,进入操作数栈
16 “+”,“4”,“15”退栈
17 19 4+15=19,进入操作数栈
18 19 表达式的值为19 
2.函数递归的实现
递归是程序设计中常用的方法之一,栈的一个重要应用是可以实现递归函数(或递
归过程)。
例3-8 通常用来说明递归的最简单的例子是阶乘的定义,它可以表示为
n!= 1 n =0,1 
n(n -1)! n >1 { 
由定义可知,为了定义n 的阶乘,必须先定义(n-1)的阶乘;为了定义(n-1)的阶
乘,又必须定义(n-2)的阶乘……这种用自身的简单情况定义自己的方式,称为“递归定
义”。一个递归定义必须一步比一步简单,而且最后是有终结的,绝不能无限循环下去。
在n 的阶乘定义中,当n 为0或1时定义为1,它就不再用递归定义。根据阶乘的定义, 
编写的Python语言程序如下。
例3-9 
#用递归函数求n 阶乘的值
def Fac(i): 
if i==0: 
return 1

第◆3 章 栈与队列8 3 
else: 
return i * Fac(i-1)#因为n!=n*(n-1)!,所以直接调用自身
if __name__=='__main__': 
n=int(input('请输入阶乘数:')) 
print('%d !值为%3d' %(n,Fac(n))) 
运行上述程序,结果如下。 
请输入阶乘数: 5 
5 !值为120 
由上述程序的运行过程可知,递归求阶乘的算法时间主要花费在递归调用函数的次
数,这与n 的大小有关,因此该算法的时间复杂度为O(n)。
函数直接或间接调用自身称为函数的递归调用,包含递归调用的函数称为递归函
数。若函数Fac(n)中含直接调用函数Fac(),则称为直接递归调用。若函数a 中调用函
数b,而函数b 中又调用了函数a,则称为间接递归调用。
实现递归调用的关键是建立一个栈,这个栈是由系统提供的运行工作栈。计算机在
执行递归算法时,是通过栈来实现的。在一层层递归调用时,系统自动将其返回地址和
每一调用层的变量数据一一记下并进栈。返回时,变量数据一一出栈并且被采用。图3-5 
所示展示了递归调用的执行情况,由此可以看到,由于主函数中调用了Fac(4),Fac函数
共被调用4次,即Fac(4)、Fac(3)、Fac(2)、Fac(1)。其中Fac(4)是在main()函数中调用
的,其余3次是在Fac()函数中调用的。在某一层递归调用时,并未立即得到结果,而是
进一步向深度递归调用,直到最内层函数执行n=1时,Fac(n)才有结果。然后在依次返
回时,不断得到中间结果,直至回到主程序得到n!的最终结果为止。
图3-5 递归调用示意图
实际上,递归是把一个不能或不方便直接求解的“大问题”转换成一个或几个“小问
题”,再把这些“小问题”进一步分解成更小的“小问题”,如此分解,直至每个“小问题”都
可以直接解决(此时分解到递归出口)。当然,被分解的“小问题”和“大问题”必须具有相
同的特征属性。
虽然递归算法简明、精炼,但运行效率较低,时空开销较大,并且某些高级语言没有
提供递归调用的语句及功能,因此,在实际应用中往往会使用非递归方法。为了提高程
序设计的能力,有必要进行由递归方法到非递归方法的基本训练。通过前面对递归算法
的分析,我们已经知道,系统内部是借助栈实现递归的,因此,在设计相应的非递归算法
时,也需要人为设置一个栈来实现,具体实现过程将在5.2.4节中详细介绍。

8 4 ◆数据结构(Python 版) 
3.2 队  列
3.2.1 队列的定义 
队列(Queue)是限定只能在表的一端进行插入,在表的另一端进行删除的线性表。
表中允许插入的一端称为队尾(rear),允许删除的一端称为队首(front)。
假设队列为Q=(a1,a2,a3,…,an),那么a1 就是队首元素,an 则是队尾元素。队列
中的元素是按照a1,a2,a3,…,an 的顺序进入的,出队列也只能按照这个次序依次退出, 
图3-6所示为队列的示意图。队列又称先进先出表(FirstInFirstOut,FIFO)。如果队
列中没有任何元素,则称为空队列,否则称为非空队列。
图3-6 队列示意图
日常生活中有许多队列的例子,如顾客排队购物,排在队伍前面的顾客先买,先离
队;排在队伍后面的顾客后买,后离队。队列的基本运算有5种。
. 初始化队列:将一个队列设置为空队列。
. 入队列:在队尾插入一个新的元素,也称进队或插入。
. 出队列:在队首删除一个元素,又称退队或删除。
. 取队首元素:得到队首元素的值。
. 判队空:判断队列是否为空队列。
3.2.2 队列的顺序存储及其基本操作的实现
队列的顺序存储结构称为顺序队列(SequentialQueue)。顺序队列与顺序表一样, 
用一个一维数组存放数据元素。在内存中,用一组连续的存储单元顺序存放队列中的各
元素。队列的Python语言描述如下。 
class Queue(object) : 
def __init__(self, size) : 
self. MAX = size #定义队列的尺寸 
self.q = [] #定义队列元素存放的列表
在这个描述中,用一列表q[]表示队列,其中MAX 表示队列允许的最大容量,用一
个指针front指示队列的首端,用另一个指针rear指示队列的尾端。为方便起见,约定头
指针front指向队首的前一个位置,尾指针rear指向队尾所在位置。
在队列顺序存储结构中,如图3-7所示,队首指针和队尾指针是数据元素的下标。在
队列为空的初始状态front=rear=-1。每当向队列中插入一个元素,尾指针rear向后

第◆3 章 栈与队列8 5 
移动一位,即rear=rear+1。当rear=MAX-1时,表示队满。每当从队列中删除一个
元素时,队首指针也向后移动一位,即front=front+1。经过多次入队和出队运算,可能
出现front=rear的时候,这时队列为空。
图3-7 元素出、入队操作示例
下面给出顺序队列中实现入队与出队运算的算法描述。
1.入队
在顺序队列中入队时,数据元素是从队尾进入的,此时应该先改变队尾指针,使队尾
指针指向新元素应插入的位置,即队尾位置,然后将数据插入。由于顺序队列有上界,因
此插入时会发生队满的情况,此时应返回队满信息。
例3-10 
#队列入队
class Queue(object): 
def __init__(self,size): 
self.MAX = size #队列最大尺寸 
self.q = [] #队列初始化为空列表 
self.front = -1 #初始化,若front=-1,则为空队列 
self.rear = -1 #初始化,若rear=-1,则为空队列 
def enqueueFull(self): 
if self.rear == self.MAX:#判队列满!!! 
return True 
else: 
return False 
def inqueue(self,x): 
if self.enqueueFull():#入队之前检查队列是否已满 
raise Exception("队列已经满,不能进行入队操作!") 
else: 
self.rear=self.rear+1 #队尾指针加1 
self.q.append(x) #元素入队
if __name__ == '__main__':