栈
栈的类型定义
栈的基本操作
Push(&S,e)
- 初始条件:栈S已存在
- 操作结果:插入元素e为新的栈顶元素。
Pop(&S,&e)
- 初始条件:栈已存在且非空
- 操作结果:删除S的栈顶元素,并用e返回其值。
数组实现栈
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(x=element)
p.data = element
p.next = self.next
self.next = p
def pop(self):
"""弹栈(弹出元素)"""
if self.next != None:
p = self.next
self.next = p.next
return p.data
print("栈已经为空")
return None
def top(self):
"""返回栈顶元素"""
if self.next != None:
return self.next.data
print("栈已经为空")
return None
if __name__ == '__main__':
s = MyStack()
s.push(1)
print("栈顶元素为:" + str(s.top()))
print("栈大小为:" + str(s.size()))
s.pop()
print("弹栈成功")
s.pop()
两种方法的对比
- 采用数组实现栈的优点:一个元素值占用一个存储空间;它的缺点为:如果初始化申请的存储空间太大,会造成空间的浪费,如果申请的存储空间太小,后期会经常需要扩充存储空间,扩充存储空间是个费时的操作,这样会造成性能的下降
- 采用链表实现栈的优点是:使用灵活方便,只有在需要的时候才会申请空间,它的缺点为:除了要存储元素外,还需要额外的存储空间存储指针信息。
队列
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出的(First In First Out)的线性表,简称FIFO。允许插入的一端为队尾,允许删除的一端为队头。队列不允许在中间部位进行操作!假设队列是q=(a1,a2,……,an),那么a1就是队头元素,而an是队尾元素。这样我们就可以删除时,总是从a1开始,而插入时,总是在队列最后。这也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然排在队伍最后。
用数组实现队列
下面给出了一种最简单的实现方式,用front来记录队列首元素的位置,用rear来记录队列尾元素往后一个位置。入队列的时候只需要将待入队列放入下标为rear位置,然后同时执行rear+,那么出队列就是front+。
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
"""
用链表实现队列
采用链表实现队列的方法与实现栈的方法类似,分别用两个指针指向队列的首元素与尾元素,而用pHead来指向队列的首元素,用pEnd来指向队列的尾元素。
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的基础模块中是没有数组这种数据结构的,用了列表来代替。
关于数组的动态扩容以及增删改查
这个问题其实在python中是不用和C一样大费周章的,python提供的list和字典是可变类型,就像我上面提到的,本身就是一个过度分配的动态数组。所以它的增删改查,我大概画了一个流程图,可以完成基本的所有操作,而如果想要了解底层编译方式,就需要查看CPython解释器了解它的组成,我找到了一篇译文,可以参考如下链接,以及我画的思维导图: