Python——数据结构(栈,队列,链表)
数据结构
程序=数据结构+算法
数据结构就是设计数据以何种方式组织并存储在计算机中。列表、集合与字典等都是一种数据结构。
小Tips:列表中的元素是怎样存储的,操作的时间复杂度是多少?
Python将数存放在一个内存单元中,在列表中的元素指向那个内存单元。所以列表中的元素在内存单元中可能在一起,也可能不在一起。 [].insert()和[].remove()的时间复杂度为o(n) [].append()和[].pop()的时间复杂度为o(1)
栈
栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。
栈的特点:后进先出
栈的基本操作:
- 进栈(压栈):push
- 出栈:pop
- 取栈顶(看看栈顶是谁):gettop
Python中实现栈:
进栈函数:append 出栈函数:pop 查看栈顶函数:li[-1]
小Tips:
递归出错的原因是:爆栈
示例:给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。
思路:检查字符串是否包含"{","[","("如果包含则将其入栈,遇到"}","]",")"时检查当前栈顶,相同则将其出栈,否则返回False
1 def check_kuohao(str): 2 stack = [] 3 for i in str: 4 if i in {'{','[','('}: 5 stack.append(i) 6 elif i == '}': 7 if len(stack) > 0 and stack[-1] == '{': 8 stack.pop() 9 else: 10 return False 11 elif i == ']': 12 if len(stack) > 0 and stack[-1] == '[': 13 stack.pop() 14 else: 15 return False 16 elif i == ')': 17 if len(stack) > 0 and stack[-1] == '(': 18 stack.pop() 19 else: 20 return False 21 22 if len(stack) == 0: 23 return True 24 else: 25 return False 26 27 print(check_kuohao('([)]'))
示例:迷宫问题(深度优先,一条路走到黑)
maze = [ [1,1,1,1,1,1,1,1,1,1], [1,0,0,1,0,0,0,1,0,1], [1,0,0,1,0,0,0,1,0,1], [1,0,0,0,0,1,1,0,0,1], [1,0,1,1,1,0,0,0,0,1], [1,0,0,0,1,0,0,0,0,1], [1,0,1,0,0,0,1,0,0,1], [1,0,1,1,1,0,1,1,0,1], [1,1,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1] ] def mazepath(x1,y1,x2,y2): dirs = [ # lambda 参数1,参数2:(返回值格式) lambda x, y: (x + 1, y), # 向下走一格 lambda x, y: (x - 1, y), # 向上走一格 lambda x, y: (x, y - 1), # 向左走一格 lambda x, y: (x, y + 1) # 向右走一格 ] stack = [] # 初始化一个栈 stack.append((x1,y1)) # 将起始坐标推入栈中 while len(stack) > 0: curNode = stack[-1] # 查看栈顶元素 if curNode[0] == x2 and curNode[1] == y2: # 到达终点 for i in stack: print(i) return True else: for d in dirs: nextNode = d(curNode[0],curNode[1]) # 将()中的元素做相应的操作,还没推入栈中 if maze[nextNode[0]][nextNode[1]] == 0: # 这条路可以走 stack.append(nextNode) # 将这个新位置入栈 maze[nextNode[0]][nextNode[1]] = -1 # 将新位置标记为已经走过,防止死循环 break else: # 当前位置上下左右都不能走,后退一步 stack.pop() return False # 栈为空,无路可走 print(mazepath(1,1,8,8))
队列
队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。
进行插入的一端称为队尾(rear),插入动作称为进队或入队,进行删除的一端称为队头(front),删除动作称为出队
队列的性质:先进先出
双向队列:队列的两端都允许进行进队和出队操作。
队列的实现原理:
初步设想:列表+两个下标指针
创建一个列表和两个变量,front变量指向队首,rear变量指向队尾。初始时,front和rear都为0。
进队操作:元素写到li[rear]的位置,rear自增1。
出队操作:返回li[front]的元素,front自减1。
这样队列会不够用,所以我可以把它的首尾连接起来。
环形队列:
实现方式:求余数运算
队首指针前进1:front = (front + 1) % MaxSize
队尾指针前进1:rear = (rear + 1) % MaxSize
队空条件:rear == front
队满条件:(rear + 1) % MaxSize == front
Python实现队列:
from collections import deque # 双向队列 创建队列:queue = deque(li) 进队:append 出队:popleft 双向队列队首进队:appendleft 双向队列队尾进队:pop
示例:迷宫问题(广度优先)
思路:从一个节点开始,寻找所有下面能继续走的点。继续寻找,直到找到出口。
1 from collections import deque 2 3 mg = [ 4 [1,1,1,1,1,1,1,1,1,1], 5 [1,0,0,1,0,0,0,1,0,1], 6 [1,0,0,1,0,0,0,1,0,1], 7 [1,0,0,0,0,1,1,0,0,1], 8 [1,0,1,1,1,0,0,0,0,1], 9 [1,0,0,0,1,0,0,0,0,1], 10 [1,0,1,0,0,0,1,0,0,1], 11 [1,0,1,1,1,0,1,1,0,1], 12 [1,1,0,0,0,0,0,1,0,1], 13 [1,1,1,1,1,1,1,1,1,1] 14 ] 15 16 dirs = [lambda x, y: (x + 1, y), 17 lambda x, y: (x - 1, y), 18 lambda x, y: (x, y - 1), 19 lambda x, y: (x, y + 1)] 20 21 def print_p(path): 22 curNode = path[-1] 23 realpath = [] 24 print('迷宫路径为:') 25 while curNode[2] != -1: 26 realpath.append(curNode[0:2]) 27 curNode = path[curNode[2]] 28 realpath.append(curNode[0:2]) 29 realpath.reverse() 30 print(realpath) 31 32 def mgpath(x1, y1, x2, y2): 33 queue = deque() 34 path = [] 35 queue.append((x1, y1, -1)) 36 while len(queue) > 0: 37 curNode = queue.popleft() 38 path.append(curNode) 39 if curNode[0] == x2 and curNode[1] == y2: 40 #到达终点 41 print_p(path) 42 return True 43 for dir in dirs: 44 nextNode = dir(curNode[0], curNode[1]) 45 if mg[nextNode[0]][nextNode[1]] == 0: # 找到下一个方块 46 queue.append((*nextNode, len(path) - 1)) 47 mg[nextNode[0]][nextNode[1]] = -1 # 标记为已经走过 48 return False 49 50 51 mgpath(1,1,8,8)
链表
链表中每一个元素都是一个对象,每个对象称为一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的相互连接,最终串联成一个链表。
定义节点:
class Node(object): def __init__(self, item): self.item = item # 当前节点的值 self.next = None # 下一节点的值 a = Node(1) b = Node(2) c = Node(3) a.next = b b.next = c
链表节点的插入和删除
插入: p.next = curNode.next curNode.next = p 删除: p = curNode.next curNode.next = curNode.next.next del p
建立链表:
头插法
def createLinkListF(li): l = Node() for num in li: s = Node(num) s.next = l.next l.next = s return l
尾插法
def createLinkListR(li): l = Node() r = l #r指向尾节点 for num in li: s = Node(num) r.next = s r = s
双链表
双链表中每个节点有两个指针:一个指向后面节点、一个指向前面节点。
节点定义:
class Node(object): def __init__(self, item=None): self.item = item self.next = None self.prior = None
插入和删除
插入: p.next = curNode.next curNode.next.prior = p p.prior = curNode curNode.next = p 删除: p = curNode.next curNode.next = p.next p.next.prior = curNode del p
建立双链表
def createLinkListR(li): l = Node() r = l for num in li: s = Node(num) r.next = s s.prior = r r = s return l, r