栈
栈的定义
对于栈,不用关心它在物理上是怎么存放的,关心这种数据结构支持什么操作?操作有什么特点?
类似于杯子,只有一个口,这种结构,先加的数据先到达杯底,取出数据时杯口的先出。
保存的数据如何存放?
-
a1到an数据用顺序表存储,顺序表的特点就是连续存放一组数据,只要在栈中封装顺序表时,保证使用时只能从一端添加和取出,另外一端不允许访问,
-
用链表
使用时链表只能在链表的尾部访问数据,虽然之前在写链表时支持头部访问,现在我将头部访问禁止,只允许尾部添加append,尾部删除,这样这个链表就是一个栈。 -
所以顺序表和链表解决的是数据怎么存放的问题,在顺序表和链表中封装了一系列方法,但是把头部方法禁止后,只允许从尾部访问数据,即只允许一端,这样顺序表或者链表都变为栈,
所以栈和顺序表即链表的区别在于,顺序表和链表描述的是怎么存放数据,栈描述的是如何访问数据(方法). -
什么时候用到栈?
比如一个数学表达式 1*(2+3) 可以用栈的方式去解析
栈的实现
用顺序表来写
在python中,列表就是顺序表,用list实现栈
-
栈的操作
往栈中添加元素叫做压栈/入栈,英文是push
出栈,弹出,pop
peek(窥视,偷看,探出),返回栈中元素,但是元素还在栈中
以上为要封装的栈的ADT类型 -
写一个栈的类
注意写的过程
先写出类名:
再将已经计划要写的类的方法给放进去,作为写代码的蓝本
将这些方法写规范
对于栈来说,要定义以上几种方法,使用:构造栈的对象,往里面加元素,
类的属性还没定义,类的对象是什么,在栈中如何存入元素,要生成栈的容器:将这个容器封装为私有东西,不希望操作栈的人直接操作内部的容器,开始时没有任何元素,所以容器为空
-
栈代码
class Stack(object):
"""栈"""
def __init__(self):
#self.__list #合理,但是这样写,就可以用这个命令直接拿到我的全部列表
return self.__list = []
def is_empty(self):
"""判断是否为空"""
return self.__list == [] # self.__list == [],[]为空值,判断左右两边是否相等,返回结果为布尔值。这行代码简写了
def push(self, item):
"""添加一个新的元素item到栈顶"""
self.__list.append(item)
def pop(self):
"""弹出栈顶元素"""
return self.__list.pop() #不写return,单纯self.__list.pop()的返回值是none
#同上面对比,选择在尾部不选择在头部添加弹出的原因,顺序表在尾部操作的时间复杂度是O(1),操作头部的时间复杂度是O(n)。
#用单链表存,就要从头部操作元素
# 所以根据存储容器不同,时间复杂度不同
#def push(self, item):
# self.__list.insert(0,item)
#def pop(self, item):
# self.__list.pop(0)
def peek(self):
"""返回栈顶元素"""
if self.__list:
return self.__list[-1] #列表为空,不支持访问操作,要设置判断条件
else:
return None
def size(self):
"""返回栈的大小"""
return len(self.__list)
if __name__ == "__main__":
s = Stack()
s.push(1)
s.push(2)
s.push(3)
s.push(4)
print(s.pop())
print(s.pop())
print(s.pop())
print(s.pop())
之所以简单,是对已有存储结构的二次开发
运行结果如下:压进去是1,2,3,4,pop出来是4,3,2,1
队列
队列的定义
顺序表和链表都可以通过禁用某些方法来实现队列的操作。
所以顺序表和链表解决存储,在具体使用时要关注容器的具体操作特性,是FIFO还是LIFO
队列的实现
实现代码的思路和栈一样
class Queue(object):
"""队列"""
def __init__(self):
self.__list = []
def is_empty(self):
return self.__list == []
def enqueue(self, item):
"""进队列"""
self.__list.insert(0,item) #在list头部添加元素
#self.__list.append(item) #在list尾部添加元素
def dequeue(self):
"""出队列"""
return self.__list.pop() #从list尾部弹出
return self.__list.pop(0)#从list头部弹出
# 选用那个,要看经常用的是进队还是出队,再考虑不同操作的时间复杂度
def size(self):
"""返回大小"""
return len(self.__list)
if __name__ == "__main__"
s = Queue()
s.enqueue(1)
s.enqueue(2)
s.enqueue(3)
s.enqueue(4)
print(s.dequeue())
print(s.dequeue())
print(s.dequeue())
print(s.dequeue())
队列的扩展—双端队列
双端队列实现
class Deque(object):
"""双端队列"""
def __init__(self):
self.__list = []
def is_empty(self):
"""判断队列是否为空"""
return self.__list == []
def enqueue_front(self, item):
"""在队头添加元素"""
self.__list.insert(0,item)
def enqueue_rear(self, item):
"""在队尾添加元素"""
self.__list.append(item)
def dequeue_front(self):
"""从队头取出元素"""
return self.__list.pop(0)
def dequeue_rear(self):
"""从队尾取出元素"""
return self.__list.pop()
def size(self):
"""返回队列大小"""
return len(self.__list)