数据结构 栈的应用 队列 链表 双向链表 哈希表
栈 stack
栈的出队序列
-
n个有序元素的出栈序列个数:
-
卡特兰数
# ABC # CBA # ABC # ACB # BAC # BCA # CAB # ABCDE # ACBDE AEDCB ADBCE ABDCE
栈处理括号匹配 (brace match)
"{{[]}({}){[][]}{[]}([({})]([{[]}]))}"
def brace_match(s):
stack = []
brace_dict = {"(": ")", "[": "]", "{": "}"}
for ch in s:
# 添加左括号
if ch in brace_dict.keys():
stack.append(ch)
# 匹配右括号
elif ch in brace_dict.values():
# 判断栈的最后一个 是否跟 当前的反括号 匹配
if brace_dict[stack[-1]] == ch:
stack.pop()
else:
return False
if stack:
return False
else:
return True
print(brace_match(s))
栈的应用 迷宫问题
- 给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。
- 思路:
- 在一个迷宫节点(x,y)上,可以进行四个方向的探查:maze[x-1][y], maze[x+1][y], maze[x][y-1], maze[x][y+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, 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]
]
DES ( 使用栈解决 )
# 起点 (1, 1) 终点 (8, 8)
dirs = [
# x 为纵向坐标 y 横向坐标
lambda x, y: (x - 1, y), # 上
lambda x, y: (x, y + 1), # 右
lambda x, y: (x + 1, y), # 下
lambda x, y: (x, y - 1), # 左
]
def solve_maze_DFS(maze, x, y, x1, y1):
stack = [] # 定义栈
stack.append((x, y))
maze[x][y] = 2 # 标记走过的路
while stack:
cur_node = stack[-1]
if cur_node == (x1, y1):
print(maze)
return True
for dir in dirs:
next_x, next_y = dir(*cur_node)
if maze[next_x][next_y] == 0:
stack.append((next_x, next_y))
maze[next_x][next_y] = 2
break
else:
err_x, err_y = stack.pop()
maze[err_x][err_y] = 3
return False
# solve_maze_DFS(maze, 1, 1, 8, 8)
# 找到的路径 2 走错的 路径 3
o = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 1, 3, 3, 3, 1, 3, 1],
[1, 3, 2, 1, 3, 3, 3, 1, 3, 1],
[1, 2, 2, 3, 3, 1, 1, 2, 2, 1],
[1, 2, 1, 1, 1, 2, 2, 2, 2, 1],
[1, 2, 2, 2, 1, 2, 0, 0, 2, 1],
[1, 0, 1, 2, 2, 2, 1, 0, 2, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 2, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
BFS ( 使用队列解决 )
def solve_maze_BfS(maze, x, y, x1, y1):
d = deque()
path = []
# 将起点加入到队列中
d.append((x, y, -1))
maze[x][y] = 2
while d:
cur_code = d.popleft()
path.append(cur_code)
if cur_code[:2] == (x1, y1):
p = len(path) - 1
over_path = []
# p = cur_code[-1]
# over_path = [cur_code[:2]]
while p >= 0:
over_path.append(path[p][:2])
p = path[p][-1]
over_path.reverse()
print(over_path)
return True
for dir in dirs:
next_x, next_y = dir(cur_code[0], cur_code[1])
if maze[next_x][next_y] == 0:
d.append((next_x, next_y, len(path) - 1))
maze[next_x][next_y] = 2
return False
solve_maze_BfS(maze, 1, 1, 8, 8)
队列:
-
先进先出
from collections import deque # 生产者消费者模型的 队列 q = deque() q.append(1) q.append(2) print(q.popleft()) # 1 print(q.popleft()) # 2 print(q.popleft()) # 报错
-
使用队列实现 Linux 的 head 和 tail 命令
from collections import deque print(list(deque(open('abc.txt', 'r', encoding='utf-8'), 5))) q = deque([1,2,3,4,5], 3) print(list(q))
如何用两个栈实现一个队列?
#1 2 3 4 5
# Stack 1:
# Stack 2: 4 3 2 1
# 进队: 进栈1; 出队:出栈2;如果栈2空,就将栈1一次出栈并进到栈2,在从栈2出栈一次
class Queue(object):
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, item):
self.stack1.append(item)
def pop(self):
if self.stack2:
return self.stack2.pop()
elif self.stack1:
while len(self.stack1):
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
else:
raise ConnectionError("empty queue")
q = Queue()
q.push(1)
q.push(2)
print(q.pop())
q.push(13)
print(q.pop())
print(q.pop())
print(q.pop())
链表:
- 创建链表的方式:
- 头插法
- 尾插法
class LinkList(object):
def __init__(self, data=None):
self.data = data
# 元素的 指向
self.next = None
# a = LinkList('11')
# b = LinkList('33')
# c = LinkList('33')
# b.next = a
# c.next = b
def create_link(li):
""" 头插法 每一次都从头部开始往后插 """
head = LinkList() # 创建链表的头部
for val in li: #
v = LinkList(val) # 将列表的值 创建为链表的值
v.next = head.next # 创建的量表对象的 下一个节点指向 链表的头部的下一个指向内容 1 > 3 > 2
head.next = v # 重新定义头部的 下一个指向
return head
def create_link_tail(li):
""" 尾插法 """
head = LinkList() # 创建链表 头部
tail = head # 定义初始尾部
for var in li:
v = LinkList(var)
tail.next = v # 定义链表最后的 下一个节点的指向为新创建的 对象
tail = v # 重新定义 尾部
return head
# 正向遍历
def look_link_head(head):
h = head.next # 头部的下一个内容
while h: # 如果下一个内容有值 就继续
yield h.data # 返回 下一个内容的 值
h = h.next # 重新定义下一个内容
head = create_link_tail([1, 2, 3, 4, 5, 6, 7])
for i in look_link_head(head):
print(i)
双向链表
- 双链表中每个节点有两个指针:一个指向后面节点、一个指向前面节点
"""
双向链表 创建
"""
class Node(object):
def __init__(self, data=None):
self.data = data
self.next = None
self.prior = None
def create_link_list(li):
head = Node() # 链表的 头
tail = head # 下一个要插入的 内容
for val in li:
v = Node(val)
tail.next = v
v.prior = tail
tail = v
return head, tail
# 正向遍历
def look_link_head(head):
h = head.next # 头部的下一个内容
while h: # 如果下一个内容有值 就继续
yield h.data # 返回 下一个内容的 值
h = h.next # 重新定义下一个内容
# 反向遍历
def look_link_tail(tail):
t = tail
while t.prior: # 不打印 头部最后的 None
yield t.data
t = t.prior
head, tail = create_link_list([1, 2, 3, 4, 5, 6, 7])
for i in look_link_head(head):
print(i)
for i in look_link_tail(tail):
print(i)
列表与链表 复杂度
-
时间复杂度
- 按元素值查找 O(n) O(n)
- 按下标查找 O(1) O(n)
- 在某元素后插入 O(n) O(1)
- 删除某元素 O(n) O(1)
-
链表在插入和删除的操作上明显快于顺序表
-
链表的内存可以更灵活的分配
- 试利用链表重新实现栈和队列
-
链表这种链式存储的数据结构对树和图的结构有很大的启发性
哈希表:
- 哈希表一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:
- ninsert(key, value):插入键值对(key,value
- nget(key):如果存在键为key的键值对则返回其value,否则返回空
- ndelete(key):删除键为key的键值对
解决哈希冲突——开放寻址法
- 开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
- 线性探查:如果位置i被占用,则探查i+1, i+2,……
- 二次探查:如果位置i被占用,则探查i+12,i-12,i+22,i-22,……
- 二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2,h3,……