目录
数据结构介绍
列表
栈
栈的基本操作:
栈的实现(使用一般列表结构即可实现):
栈的应用——括号匹配问题
队列
队列的实现方式——环形队列
队列的实现方式——双向队列
队列内置模块
栈和队列应用——迷宫问题
栈——深度优先搜索
队列——广度优先搜索
数据结构介绍
介绍:
- 数据结构是值相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。
- 简单说:数据结构就是设计数据以何种方式组织并存储在计算机中。
- 比如:列表、集合与字典等都是一种数据结构。
- 程序 = 数据结构 + 算法
分类:
- 数据结构按照其逻辑结构可分为线性结构、树结构、图结构。
- 线性结构:数据结构中存在一对一的相互关系
- 树结构:数据结构中存在一对多的相互关系
- 图结构:数据结构中存在多对多的相互关系
列表
列表与其他编程语言的数组有区别:1、数组元素类型相同;2、数组长度固定。
列表:是一种基本数据类型。
操作:
- 查找:O(1)
- 插入:O(n)
- 删除:O(n)
栈
- 栈:是一个数据集合,可以理解为只能在一端进行插入或者删除操作的列表。
- 特点:后进先出LIFO
- 概念:栈顶、栈底
栈的基本操作:
- 进栈(压栈):push
- 出栈:pop
- 取栈顶:gettop
栈的实现(使用一般列表结构即可实现):
- 进栈:li.append
- 出栈:li.pop
- 取栈顶:li[-1]
class Stack:def __init__(self):self.stack = []# 进栈def push(self, element):self.stack.append(element)# 出栈def pop(self):return self.stack.pop()# 取栈顶def get_top(self):if len(self.stack) > 0:return self.stack[-1]else:return None# 举例
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())
栈的应用——括号匹配问题
给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。
例如:()()[]{} 匹配、([{()}]) 匹配、[]( 不匹配、[(]) 不匹配
class Stack:def __init__(self):self.stack = []# 进栈def push(self, element):self.stack.append(element)# 出栈def pop(self):return self.stack.pop()# 取栈顶def get_top(self):if len(self.stack) > 0:return self.stack[-1]else:return None# 判断栈是否为空def is_empty(self):return len(self.stack) == 0def brace_match(s):stack = Stack()match = {'}':'{', ']':'[', ')':'('}for ch in s:if ch in {'(', '[', '{'}:stack.push(ch)else:if stack.is_empty():return Falseelif stack.get_top() == match[ch]:stack.pop()else: # stack.get_top() != match[ch]return Falseif stack.is_empty():return Trueelse:return Falseprint(brace_match("{(){}}{{}{}}"))
队列
- 队列:队列是一个数据集合,仅允许在列表一端进行插入,另一端进行删除。
- 进行插入的一端称为队尾(rear),插入动作称为进队或入队。
- 进行删除的一端称为队头(front),删除动作称为出队。
- 队列的性质:先进先出FIFO
队列的实现方式——环形队列
环形队列:当队尾指针front = Maxsize - 1时,再前进一个位置就自动到0
- 队首指针前进1:front = (front + 1) % MaxSize
- 队尾指针前进1:rear = (rear + 1) % MaxSize
- 队空条件:rear == front
- 队满条件:(rear + 1) % MaxSize == front
class Queue:def __init__(self, size = 100): # 固定队列的长度(做环形队列)self.queue = [0 for _ in range(size)]self.size = sizeself.rear = 0 # 队尾指针(入队)self.front = 0 # 队首指针(出队)# 入队def push(self, element):if not self.is_filled():self.rear = (self.rear + 1) % self.sizeself.queue[self.rear] = elementelse:raise IndexError("Queue is filled.") # 抛出错误,队满# 出队def pop(self):if not self.is_empty():self.front = (self.front + 1) % self.sizereturn self.queue[self.front]else:raise IndexError("Queue is empty.") # 抛出错误,队空# 判断队空def is_empty(self):return self.rear == self.front# 判断队满def is_filled(self):return (self.rear + 1) % self.size == self.frontq = Queue(6)
for i in range(5):q.push(i)
print(q.is_filled())
print(q.pop())
q.push(4)
队列的实现方式——双向队列
双向队列:两端都支持进队和出队操作
双向队列基本操作:
- 队首进队
- 队首出队
- 队尾进队
- 队尾出队
队列内置模块
使用方法:from collections impport deque
创建队列:queue = deque()
进队:append()
出队:popleft()
双向队列队首进队:appendleft()
双向队列队尾出队:pop()
# 双向队列
from collections import deque# 创建队列
q = deque([1, 2, 3, 4, 5], 5) # 里面的第一个参数为列表,为队列初始化;第二个参数为int,是队列的长度
# 当队列满了之后继续入队,队首位置会向后一位,q.append(1) # 队尾入队
q.popleft() # 队首出队# 用于双向队列
q.appendleft() # 队首入队
q.pop() # 队尾出队
栈和队列应用——迷宫问题
问题介绍:给一个二维列表,表示迷宫(0表示通道,1表示围墙);给出算法,求一条走出迷宫的路径。
栈——深度优先搜索
- 回溯法
- 思路:从一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。
- 使用栈存储当前路径
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, 1, 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]
]# 封装一个列表,表示以什么顺序来探路
dirs = [lambda x,y:(x-1, y),lambda x,y:(x+1, y),lambda x,y:(x, y+1),lambda x,y:(x, y-1),
]def maze_path(x1, y1, x2, y2): # (x1, y1)起点,(x2, y2)终点stack = [] # 建立栈stack.append((x1, y1)) # 先将起点压入栈while(len(stack)>0): # 栈不空就循环,如果回溯到起点之后就代表没有路curNode = stack[-1] # 当前位置if curNode[0] == x2 and curNode[1] == y2: # 判断当前点是否为终点# 走到终点for p in stack:print(p)return True# 上下左右四个方向搜索:上(x-1, y), 下(x+1, y), 左(x, y+1), 右(x, y-1)for dir in dirs:nextNode = dir(curNode[0], curNode[1])# 如果下一个节点可以走if maze[nextNode[0]][nextNode[1]] == 0:stack.append(nextNode) # 如果可以走,就将该点压入栈maze[nextNode[0]][nextNode[1]] = 2 # 然后将该点标记成2,2表示路以及走过break # 找到一个就break,往下走else: # 找不到就回退maze[nextNode[0]][nextNode[1]] = 2stack.pop() # 回退else: # 栈空,未找到路print("没有路")return Falsemaze_path(1, 1, 8, 8)
队列——广度优先搜索
可以计算最短路径
- 思路:从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口。
- 使用队列存储当前正在考虑的节点,使用列表存储走过的路以及其上个节点下标。
from collections import dequemaze = [[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, 1, 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],
]# 封装一个列表,表示上下左右四个坐标
dirs = [lambda x, y: (x + 1, y),lambda x, y: (x - 1, y),lambda x, y: (x, y - 1),lambda x, y: (x, y + 1),
]# 用于输出路径
def print_r(path):curNode = path[-1]realpath = []while curNode[2] != -1:realpath.append(curNode[0:2])curNode = path[curNode[2]]realpath.append(curNode[0:2]) # 最后将起点放进去realpath.reverse()for node in realpath:print(node)# 广度优先搜索
def maze_path_queue(x1, y1, x2, y2):queue = deque() # 创建队列queue.append((x1, y1, -1)) # 先将起点存储path = []while len(queue) > 0: # 队空表示无路curNode = queue.pop()path.append(curNode)if curNode[0] == x2 and curNode[1] == y2:# 终点print_r(path)return Truefor dir in dirs:nextNode = dir(curNode[0], curNode[1])if maze[nextNode[0]][nextNode[1]] == 0:queue.append((nextNode[0], nextNode[1], len(path)-1)) # 后续节点进队,记录从哪个节点过来maze[nextNode[0]][nextNode[1]] = 2 # 标记为已经走过else:print("没有路")return Falsemaze_path_queue(1, 1, 8, 8)
代码自己手动敲一遍理解更深哦!