1、栈的操作
栈是一种线性数据结构,用先进后出或者是后进先出的方式存储数据,栈中数据的插入删除操作都是在栈顶端进行,常见栈的函数操作包括
- empty() – 返回栈是否为空 – Time Complexity : O(1)
- size() – 返回栈的长度 – Time Complexity : O(1)
- top() – 查看栈顶元素 – Time Complexity : O(1)
- push(g) – 向栈顶添加元素 – Time Complexity : O(1)
- pop() – 删除栈顶元素 – Time Complexity : O(1)
python中栈可以用以下三种方法实现:
1)list
2)collections.deque
3)queue.LifoQueue
1.1使用列表实现栈
python的内置数据结构list可以用来实现栈,用append()向栈顶添加元素, pop() 可以以后进先出的顺序删除元素
但是列表本身有一些缺点,主要问题就是当列表不断扩大的时候会遇到速度瓶颈.列表是动态数组,因此往其中添加新元素而没有空间保存新的元素时,它会自动重新分配内存块,并将原来的内存中的值复制到新的内存块中.这就导致了一些append()操作会消耗更多的时间
>>> stack = []
>>> #append() fuction to push
... #element in list
...
>>> stack.append('hello')
>>> stack.append('world')
>>> stack.append('!')
>>> print('Initial stack')
Initial stack
>>> print(stack)
['hello', 'world', '!']
>>> #pop() function to pop element
... #from stack in LIFO order
...
>>> print('\nElement poped from stack')
Element poped from stack
>>> print(stack.pop())
!
>>> print(stack.pop())
world
>>> print(stack.pop())
hello
>>> print('\nStack after all elements are poped')
Stack after all elements are poped
>>> print(stack)
[]
1.2 使用collections.deque实现栈
python中栈也可以用deque类实现,当我们想要在实现在容器两端更快速地进行append和pop操作时,deque比列表更合适.deque可以提供O(1)时间的append和pop操作,而列表则需要O(n)时间.
>>> from collections import deque
>>> stack = deque()
>>> # append() fuction to push
... #element in list
...
>>> stack.append('hello')
>>> stack.append('world')
>>> stack.append('!')
>>> print('Initial stack')
Initial stack
>>> print(stack)
deque(['hello', 'world', '!'])
>>> #pop() function to pop element
... #from stack in LIFO order
...
>>> print('\nElement poped from stack')
Element poped from stack
>>> print(stack.pop())
!
>>> print(stack.pop())
world
>>> print(stack.pop())
hello
>>> print('\nStack after all elements are poped')
Stack after all elements are poped
>>> print(stack)deque([])
1.3 使用queue module实现栈
Queue模块有LIFO queue,也就是栈结构.用put()和get()操作从Queue中添加和获得数据
在这个模块中有多种函数可用:maxsize -队列中允许的项目数量。empty() -如果队列为空则返回True,否则返回False .full() -如果队列中有maxsize项目则返回True。如果队列初始化为maxsize=0(默认值),则full永远不会返回Trueget() -Remove并返回队列中的项。如果队列是空的,等待直到一个项目是可用的。get nowait()-如果一个项目是立即可用的,返回一个项目,否则引发QueueEmptyput(item)-将一个项目放入队列。如果队列已经满了,在添加项目之前等待,直到有空闲的槽可用。如果没有可用的空闲插槽,则引发QueueFull。
>>> from queue import LifoQueue
>>> stack = LifoQueue(maxsize = 3)
>>> print(stack.qsize())
0
>>> stack.put('hello')
>>> stack.put('world')
>>> stack.put('!')
>>> print('\nElement poped from stack')
Element poped from stack
>>> print(stack.get())
!
>>> print(stack.get())
world
>>> print(stack.get())
hello
>>> print('\nEmpty:', stack.empty())
Empty: True
使用单链表实现
# Python program to demonstrate
# stack implementation using a linked list.
# node class
class Node:
def __init__(self, value):
self.value = value
self.next = None
class Stack:
# Initializing a stack.
# Use a dummy node, which is
# easier for handling edge cases.
def __init__(self):
self.head = Node("head")
self.size = 0
# String representation of the stack
def __str__(self):
cur = self.head.next
out = ""
while cur:
out += str(cur.value) + "->"
cur = cur.next
return out[:-3]
# Get the current size of the stack
def getSize(self):
return self.size
# Check if the stack is empty
def isEmpty(self):
return self.size == 0
# Get the top item of the stack
def peek(self):
# Sanitary check to see if we
# are peeking an empty stack.
if self.isEmpty():
raise Exception("Peeking from an empty stack")
return self.head.next.value
# Push a value into the stack.
def push(self, value):
node = Node(value)
node.next = self.head.next
self.head.next = node
self.size += 1
# Remove a value from the stack and return.
def pop(self):
if self.isEmpty():
raise Exception("Popping from an empty stack")
remove = self.head.next
self.head.next = self.head.next.next
self.size -= 1
return remove.value
# Driver Code
if __name__ == "__main__":
stack = Stack()
for i in range(1, 11):
stack.push(i)
print(f"Stack: {stack}")
for _ in range(1, 6):
remove = stack.pop()
print(f"Pop: {remove}")
print(f"Stack: {stack}")
2、队列
与堆栈类似,队列是一种线性数据结构,以先进先出(FIFO)的方式存储项目。对于队列,最近添加的项目最先被删除。队列的一个很好的例子是资源的任何消费者队列,最先到达的消费者首先得到服务。
Enqueue:向队列中添加一个项目。如果队列已满,则称为溢出条件——时间复杂度:0(1)退出队列:从队列中移除一个项目。弹出项的顺序与推入项的顺序相同。如果队列为空,则称为Underflow条件-Time Complexity: 0(1)Front:从队列中获取最前面的项-Time Complexity: 0(1)Rear:从队列中获取最后一项-Time Complexity: 0(1)
# Python program to
# demonstrate queue implementation
# using list
# Initializing a queue
queue = []
# Adding elements to the queue
queue.append('a')
queue.append('b')
queue.append('c')
print("Initial queue")
print(queue)
# Removing elements from the queue
print("\nElements dequeued from queue")
print(queue.pop(0))
print(queue.pop(0))
print(queue.pop(0))
print("\nQueue after removing elements")
print(queue)
# Uncommenting print(queue.pop(0))
# will raise and IndexError
# as the queue is now empty
# Python program to # demonstrate queue implementation # using collections.dequeue from collections import deque # Initializing a queue q = deque() # Adding elements to a queue q.append('a') q.append('b') q.append('c') print("Initial queue") print(q) # Removing elements from a queue print("\nElements dequeued from the queue") print(q.popleft()) print(q.popleft()) print(q.popleft()) print("\nQueue after removing elements") print(q) # Uncommenting q.popleft() # will raise an IndexError # as queue is now empty
# Python program to # demonstrate implementation of # queue using queue module from queue import Queue # Initializing a queue q = Queue(maxsize = 3) # qsize() give the maxsize # of the Queue print(q.qsize()) # Adding of element to queue q.put('a') q.put('b') q.put('c') # Return Boolean for Full # Queue print("\nFull: ", q.full()) # Removing element from queue print("\nElements dequeued from the queue") print(q.get()) print(q.get()) print(q.get()) # Return Boolean for Empty # Queue print("\nEmpty: ", q.empty()) q.put(1) print("\nEmpty: ", q.empty()) print("Full: ", q.full()) # This would result into Infinite # Loop as the Queue is empty. # print(q.get())
1.3 例题解答《剑指Offer(第二版)》面试题09. 用两个栈实现队列
用于循环执行程序,即在某条件下,循环执行某段程序,以处理需要重复处理的相同任务
3、链表
链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,
且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作
链表的每个元素称为一个节点,每个节点都可以存储在内存中的不同的位置,为了表示每个元素与后继元素的逻辑关系,以便构成“一个节点链着一个节点”的链式存储结构,
除了存储元素本身的信息外,还要存储其直接后继信息,因此,每个节点都包含两个部分,第一部分称为链表的数据区域,用于存储元素本身的数据信息,这里用data表示,
它不局限于一个成员数据,也可是多个成员数据,第二部分是一个结构体指针,称为链表的指针域,用于存储其直接后继的节点信息,这里用next表示,
next的值实际上就是下一个节点的地址,当前节点为末节点时,next的值设为空指针
3.1反转链表
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head==None:
return head #如果当前的头节点为0,直接输出头
old = head.next #头节点的下一位也就是2
new = head #取出头节点作为尾巴
new.next = None #因为作为了尾巴,所以next为空
#循环的将身下的节点元素取出来链接在new的前面
while old != None:
flag = old
old = old.next
flag.next = new
new = flag
return new
3.2
3.3 复杂链表的复制
不适用额外空间,在原链表添加节点 时间O(N),空间0(1)
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if head == None:
return None
cur = head
while cur != None: # 添加新节点
new = Node(cur.val) #当前节点的值A赋给新的链表
new.next = cur.next #当期节点的下一个值B,作为赋值链表A的下一个值
cur.next = new #将当前节点的下一个作为new,也就是A-B,cur成为了A-A-B
cur = new.next #将B赋值给cur进行下一轮
cur = head
while cur: # 设置random
cur.next.random = cur.random.next if cur.random else None #判断是否有random
cur = cur.next.next
new_head = cur = head.next
while cur: # 去除新链表
cur.next = cur.next.next if cur.next else None
cur = cur.next
return new_head