引言
栈和队列是在程序设计中被广泛使用的两种重要的线性数据结构,都是在一个特定范围的存储单元中存储的数据。和线性表相比,它们多了很多的约束与限定,关于栈,它就像是一个底边封闭开口狭小的桶,先存进去的东西只能最后取出来,所以性质为LIFO(Last In First Out),即先进后出。而队列,就是一根引流的水管,进去的水往往最先流出来。所以性质为FIFO(First In First Out),即先进先出。我们可以看下图:(图片链接)
如何实现栈
栈作为一个先进后出的数据结构,具有压栈、弹栈、取栈顶元素、加入元素、判断为空以及获取栈中元素的方法。而为了实现这些功能,我们可以通过数组和链表来完成。
用数组实现栈
我们可以用Visio画出一个大致的图形,然后代码如下:
class MyStack(object):
"""模拟栈"""
def __init__(self):
self.items = []
def is_empty(self):
"""判断是否为空"""
return self.items == []
def size(self):
"""返回栈的大小"""
return len(self.items)
def push(self, item):
"""压栈(加入元素)"""
self.items.append(item)
def pop(self):
"""弹栈(弹出元素)"""
if len(self.items)>0:
return self.items.pop()
else:
print("栈已经为空")
return None
def top(self):
"""返回栈顶元素"""
if not self.is_empty():
return self.items[len(self.items) - 1]
else:
return None
然后我们可以进行实验:
s = MyStack()
s.push(4)
print("栈顶元素为:"+str(s.top()))
print("栈大小为:"+str(s.size()))
s.pop()
print("弹栈成功")
s.pop()
"""
栈顶元素为:4
栈大小为:1
弹栈成功
栈已经为空
"""
用链表实现栈
如图,压栈操作就是将新元素放到链表的首部,见第二个数据域的(1)、(2)操作,而弹栈只需要删除链表的第一个元素就可以了,具体操作见(3)。然后我们可以写出代码:
class LNode(object):
def __init__(self,x):
self.data = x
self.next = None
class MyStack(object):
def __init__(self):
#pHead = LNode
self.data = None
self.next = None
def is_empty(self):
"""判断是否为空"""
if self.next == None:
return True
return False
def size(self):
"""返回栈的大小"""
size=0
p = self.next
while p != None:
# while p is not None:
p = p.next
size += 1
return size
def push(self, element):
"""压栈(加入元素)"""
p = LNode(element)
p.data = element
p.next = self.next
self.next = p
def pop(self):
"""弹栈(弹出元素)"""
tmp = self.next
if tmp != None:
self.next = tmp.next
print("栈已经为空")
return None
def top(self):
"""返回栈顶元素"""
if self.next != None:
return self.next.data
print("栈已经为空")
return None
然后我们可以进行实验:
s = MyStack()
s.push(1)
print("栈顶元素为:"+str(s.top()))
print("栈大小为:"+str(s.size()))
s.pop()
print("弹栈成功")
s.pop()
"""
栈顶元素为:1
栈大小为:1
弹栈成功
栈已经为空
"""
两种方法的对比
-
采用数组实现栈的优点:一个元素值占用一个存储空间;它的缺点为:如果初始化申请的存储空间太大,会造成空间的浪费,如果申请的存储空间太小,后期会经常需要扩充存储空间,扩充存储空间是个费时的操作,这样会造成性能的下降。
-
采用链表实现栈的优点是:使用灵活方便,只有在需要的时候才会申请空间,它的缺点为:除了要存储元素外,还需要额外的存储空间存储指针信息。
如何实现队列
队列作为一个先进先出的数据结构,具有入队列、出队列、查看首元素以及大小的方法。而为了实现这些功能,我们同样可以通过数组和链表来完成。
用数组实现队列
和上面一样,我们可以用Visio总结一下思路,然后代码如下:
class MyQueue(object):
"""队列"""
def __init__(self):
self.items = []
self.front = 0 # 队列头
self.rear = 0 # 队列尾
def is_empty(self):
"""判断队列是否为空"""
return self.items == self.rear
def enQueue(self, item):
"""进队列,从队尾加入"""
self.items.append(item)
self.rear += 1
# self.items.insert(0,item) # 从对头进
def deQueue(self):
"""出队列,从队头出"""
if self.rear > self.front:
self.front += 1
else:
print("队列已经为空")
# return self.items.pop() # 从对尾出
def getFront(self):
if self.is_empty():
return None
return self.items[self.front]
def getBack(self):
if self.is_empty():
return None
return self.items[self.rear-1]
def size(self):
"""返回大小"""
return self.rear - self.front
# return len(self.items) # 看大小
然后我们可以试验:
queue = MyQueue()
queue.enQueue(1)
queue.enQueue(2)
print("队列头元素为:"+str(queue.getFront()))
print("队列尾元素为:"+str(queue.getBack()))
print("队列的大小为:"+str(queue.size()))
queue.deQueue()
# queue.deQueue()
print("队列头元素为:"+str(queue.getFront()))
print("队列尾元素为:"+str(queue.getBack()))
print("队列的大小为:"+str(queue.size()))
"""
队列头元素为:1
队列尾元素为:2
队列的大小为:2
队列头元素为:2
队列尾元素为:2
队列的大小为:1
"""
用链表实现队列
class LNode(object):
def __init__(self,x):
self.data = x
self.next = None
class MyQueue(object):
def __init__(self):
"""分配头结点"""
self.pHead = None
self.pEnd = None
def is_empty(self):
"""判断是否为空"""
if self.pHead == None:
return True
return False
def size(self):
"""获取队列的大小"""
size=0
p = self.pHead
while p != None:
# while p is not None:
p = p.next
size += 1
return size
def enQueue(self, element):
"""入队列,从队尾加"""
p = LNode(element)
p.data = element
p.next = None
if self.pHead == None:
self.pHead = self.pEnd=p
else:
self.pEnd.next = p
self.pEnd = p
def deQueue(self):
"""出队列,删除首元素"""
if self.pHead == None:
print("出队列失败,队列已经为空")
self.pHead = self.pHead.next
if self.pHead == None:
self.pEnd = None
def getFront(self):
"""返回队列首元素"""
if self.pHead == None:
print("获取队列首元素失败,队列已经为空")
return None
return self.pHead.data
def getBack(self):
"""返回队列尾元素"""
if self.pEnd == None:
print("获取队列尾元素失败,队列已经为空")
return None
return self.pEnd.data
然后我们同样拿上一份数据进行试验:
queue = MyQueue()
queue.enQueue(1)
queue.enQueue(2)
print("队列头元素为:"+str(queue.getFront()))
print("队列尾元素为:"+str(queue.getBack()))
print("队列的大小为:"+str(queue.size()))
queue.deQueue()
print("队列头元素为:"+str(queue.getFront()))
print("队列尾元素为:"+str(queue.getBack()))
print("队列的大小为:"+str(queue.size()))
"""
队列头元素为:1
队列尾元素为:2
队列的大小为:2
队列头元素为:2
队列尾元素为:2
队列的大小为:1
"""
两种方法的对比
和栈中的优缺点类似,但与栈不同的是,对于队列,用链表的方式比数组更好,因为指针空间在这里的发挥空间更大。
总结
大致总结了一下栈和列表的基础,如果要深入的话还远远不止这些,在书中《python程序员算法面试宝典》里还提及了用循环链表的方式来定义队列,这个我还没有时间实现,本篇博文的代码思路引用也出自这本书,另外就是错误稍微有点多,我在这里也一一修改并成功执行了,日后可能还会总结一下链表,毕竟现在开始刷剑指offer了,概念一定要熟悉。