7.Python数据结构与算法分析课后习题(第二版)__chapter7
Python数据结构与算法分析第二版课后习题
·
chapter7_answer
- 一、讨论题
- 二、编程练习
-
- 1.修改深度优先搜索函数,以进行拓扑排序。
- 2.修改深度优先搜索函数,以计算强连通分量。
- 3.修改深度优先搜索函数,以计算强连通分量。
- 4.使用广度优先搜索实现一个算法,用以计算从每一个顶点到其余所有顶点的最短路径。这被称为所有对最短路径。
- 5.使用广度优先搜索修改第4章中的迷宫程序,从而找到走出迷宫的最短路径。
- 6.写一个程序来解决这样一个问题:有2个坛子,其中一个的容量是4加仑,另一个的是3加仑。坛子上都没有刻度线。可以用水泵将它们装满水。如何使4加仑的坛子最后装满2加仑的水?
- 7.扩展练习6中的程序,将坛子的容量和较大的坛子中最后的水量作为参数。
- 8.写一个程序来解决这样一个问题:3只羚羊和3只狮子准备乘船过河,河边有一艘能容纳2只动物的小船。但是,如果两侧河岸上的狮子数量大于领养数量,羚羊就会被吃掉。找到运送办法,使得所有动物都能安全渡河。
- 三、总结
一、讨论题
略
二、编程练习
1.修改深度优先搜索函数,以进行拓扑排序。
2.修改深度优先搜索函数,以计算强连通分量。
3.修改深度优先搜索函数,以计算强连通分量。
Graph.py:
class Vertex:
def __init__(self, key):
self.id = key
self.connectedTo = {}
self.distance = 0
self.color = 'white'
self.pred = None
self.discovery = 0
self.finish = 0
def addNeighbor(self, nbr, weight=0):
self.connectedTo[nbr] = weight
def __str__(self):
return f'<Vertex id:{self.id} time:({self.discovery}/{self.finish}) ' \
f'color:{self.color} predecessor:{self.pred.id if self.pred else None} ' \
f'connect_to:{[v.id for v in self.connectedTo]}>'
# return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo])
def __lt__(self, other):
return self.id < other.id
def __eq__(self, other):
return self.id == other.id
def __hash__(self):
return super().__hash__()
def getConnections(self):
return self.connectedTo.keys()
def getId(self):
return self.id
def getWeight(self, nbr):
return self.connectedTo[nbr]
def getDistance(self):
return self.distance
def setDistance(self, distance):
if distance > 0:
self.distance = distance
def getColor(self):
return self.color
def setColor(self, color):
self.color = color
def getPred(self):
if self.pred:
return self.pred
else:
return None
def setPred(self, pred):
self.pred = pred
def setDiscovery(self, value):
self.discovery = value
def setFinish(self, value):
self.finish = value
class Graph:
def __init__(self):
self.vertList = {}
self.numVertices = 0
def addVertex(self, key):
self.numVertices += 1
newVertex = Vertex(key)
self.vertList[key] = newVertex
return newVertex
def getVertex1(self, n):
if n in self.vertList:
return self.vertList[n]
else:
return None
def getVertex(self, key):
vertex = None
if key in self.vertList:
vertex = self.vertList[key]
return vertex
def get_vertex(self, key) -> Vertex:
vertex = None
if key in self.vertList:
vertex = self.vertList[key]
return vertex
def __contains__(self, item):
return item in self.vertList
def addEdge(self, f, t, cost=0):
if f not in self.vertList:
nv = self.addVertex(f)
if t not in self.vertList:
nv = self.addVertex(t)
self.vertList[f].addNeighbor(self.vertList[t], cost)
def getVertices(self):
return self.vertList.keys()
def __iter__(self):
return iter(self.vertList.values())
def get_all_vertex(self):
return list(self.vertList.keys())
if __name__ == '__main__':
g = Graph()
for i in range(6):
g.addVertex(i)
print(g.vertList)
g.addEdge(0, 1, 5)
g.addEdge(0, 5, 2)
g.addEdge(1, 2, 4)
g.addEdge(2, 3, 9)
g.addEdge(3, 4, 7)
g.addEdge(3, 5, 3)
g.addEdge(4, 0, 1)
g.addEdge(5, 4, 8)
g.addEdge(5, 2, 1)
for v in g:
for w in v.getConnections():
print('( %s, %s)' % (v.getId(), w.getId()))
from chapter7.Graph import Graph
class DFSGraph(Graph):
def __init__(self):
super(DFSGraph, self).__init__()
self.time = 0
self.__sort_list = []
self.__scc_list = []
def _initial_visit(self):
self.time = 0
self.__sort_list = []
self.__scc_list = []
for vertex in self:
vertex.setColor('white')
vertex.setPred(None)
def _dfs(self, vertex):
self.time += 1
vertex.setColor('gray')
vertex.setDiscovery(self.time)
for neighbor in vertex.getConnections():
if neighbor.getColor() == 'white':
neighbor.setPred(vertex)
self._dfs(neighbor)
self.time += 1
vertex.setColor('black')
vertex.setFinish(self.time)
self.__sort_list.append(vertex)
self.__scc_list[-1].append(vertex)
def _transpose(self):
gt = DFSGraph()
for vertex in self:
for neighbor in vertex.getConnections():
gt.addEdge(neighbor.id, vertex.id)
return gt
def dfs(self, vertex_list=None):
self._initial_visit()
if vertex_list is None:
vertex_list = self
for vertex in vertex_list:
if vertex.getColor() == 'white':
self.__scc_list.append(list())
self._dfs(vertex)
def topo_sort(self):
self.dfs()
self.__sort_list.reverse()
return self.__sort_list
def scc(self):
topo = self.topo_sort()
gt = self._transpose()
for vid, vertex in enumerate(topo):
topo[vid] = gt.getVertex(vertex.id)
gt.dfs(topo)
scc_mp = {}
for scc in gt.__scc_list:
value = scc[::-1]
key = ''.join(str(v.id) for v in value)
scc_mp[key] = value
return scc_mp
def topo_sort(g):
g.dfs()
ans = []
for vertex in g:
ans.append(vertex)
ans.sort(key=lambda v: v.finish, reverse=True)
return ans
def show_scc(g):
scc_mp = g.scc()
for key, scc in scc_mp.items():
print(f'scc_id: {key}')
print(f'scc: [{" -> ".join(str(v.id) for v in scc)}]')
print('*' * 30)
if __name__ == '__main__':
g = DFSGraph()
# tasks = ['3/4杯牛奶', '一个鸡蛋', '一勺油', '一杯松饼粉', '加热平底锅',
# '倒入1/4杯', '出现气泡时翻面', '加热枫糖浆', '开始享用']
# g.addEdge(tasks[0], tasks[3])
# g.addEdge(tasks[4], tasks[5])
# g.addEdge(tasks[1], tasks[3])
# g.addEdge(tasks[2], tasks[3])
# g.addEdge(tasks[3], tasks[5])
# g.addEdge(tasks[3], tasks[7])
# g.addEdge(tasks[5], tasks[6])
# g.addEdge(tasks[6], tasks[8])
# g.addEdge(tasks[7], tasks[8])
# task_list = topo_sort(g)
# print(' -> '.join(str(t.id) for t in task_list))
#
# # DFSGraph内置拓扑排序[O(n)]
# print(' -> '.join(str(v.id) for v in g.topo_sort()))
g.addEdge('A', 'B')
g.addEdge('B', 'C')
g.addEdge('B', 'E')
g.addEdge('C', 'C')
g.addEdge('C', 'F')
g.addEdge('D', 'B')
g.addEdge('D', 'G')
g.addEdge('E', 'A')
g.addEdge('E', 'D')
g.addEdge('F', 'H')
g.addEdge('G', 'E')
g.addEdge('H', 'I')
g.addEdge('I', 'F')
show_scc(g)
4.使用广度优先搜索实现一个算法,用以计算从每一个顶点到其余所有顶点的最短路径。这被称为所有对最短路径。
from chapter7.Graph import Vertex, Graph
from chapter3.queue import Queue
def bfs(g, start, end):
start = g.getVertex(start)
end = g.getVertex(end)
if start is None or end is None:
raise ValueError(f'{start}或{end}不在单词表中')
start.setDistance(0)
start.setPred(None)
vertQueue = Queue()
vertQueue.enqueue(start)
while (vertQueue.size() > 0):
currentVert = vertQueue.dequeue()
for nbr in currentVert.getConnections():
if (nbr.getColor() == 'white'):
nbr.setColor('gray')
nbr.setDistance(currentVert.getDistance() + 1)
nbr.setPred(currentVert)
vertQueue.enqueue(nbr)
currentVert.setColor('black')
return currentVert.distance
def traverse(g, y):
x = g.getVertex(y)
# x = y
while (x.getPred()):
print(x.getId())
x = x.getPred()
print(x.getId())
def traversal(g: Graph, end) -> list:
res = []
vertex = g.getVertex(end)
while vertex:
res.append(vertex.id)
vertex = vertex.pred
return res
if __name__ == '__main__':
g = Graph()
g.addEdge('A', 'B')
g.addEdge('A', 'D')
g.addEdge('B', 'C')
g.addEdge('B', 'D')
g.addEdge('D', 'E')
g.addEdge('E', 'B')
g.addEdge('E', 'F')
for start_vertex in g:
for end_vertex in g:
if start_vertex.id != end_vertex.id:
distance = bfs(g, start_vertex.id, end_vertex.id)
print(f'the distance from {start_vertex.id} to {end_vertex.id} is: ')
trace = traversal(g, end_vertex.id)
if trace[-1] == start_vertex.id:
print(' -> '.join(trace[::-1]))
else:
print('None')
# start, end = 'C', 'E'
# distance = bfs(g, start, end)
# print(f'the distance from {start} to {end} is: {distance}')
# trace = traversal(g, end)
# if len(trace) > 1:
# print(' -> '.join(trace[::-1]))
# else:
# print('None')
# traverse(g, end)
# print('->'.join(trace[::-1]))
5.使用广度优先搜索修改第4章中的迷宫程序,从而找到走出迷宫的最短路径。
BFS迷宫(不会改……直接在网上找的,嗯感觉结果不太对……)
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 27 13:48:14 2021
@author: Administrator
"""
###无递归、类求迷宫最短路径算法
# import random
pre_route = list() # 宽度搜索得到的节点
q = list() # 队列结构控制循环次数
xx = [0, 1, 0, -1] # 右移、下移、左移、上移
yy = [1, 0, -1, 0]
visited = list() # 记录节点是否已遍历
father = list() # 每一个pre_route节点的父节点
route = list()
def bfs(l, x, y, m, n):
visited = [[0 for i in range(len(l[0]))] for j in range(len(l))]
visited[x][y] = 1 # 入口节点设置为已遍历
q.append([x, y])
while q: # 队列为空则结束循环
now = q[0]
q.pop(0) # 移除队列头结点
for i in range(4):
point = [now[0] + xx[i], now[1] + yy[i]] # 当前节点
if point[0] < 0 or point[1] < 0 or point[0] >= len(l) or point[1] >= len(l[0]) or visited[point[0]][
point[1]] == 1 or l[point[0]][point[1]] == '1':
continue
father.append(now)
visited[point[0]][point[1]] = 1
q.append(point)
pre_route.append(point)
if point[0] == m and point[1] == n:
print("success")
return 1
print("false")
return 0
def get_route(father, pre_route): # 输出最短迷宫路径
route = [pre_route[-1], father[-1]]
for i in range(len(pre_route) - 1, -1, -1):
if pre_route[i] == route[-1]:
route.append(father[i])
route.reverse()
print("迷宫最短路径为:\n", route)
print("步长:", len(route) - 1)
return route
def prn_map(route, l, m, n): # 打印包含路径的迷宫图
for i in range(len(l)):
l[i] = list(l[i])
for i in range(len(route)):
l[route[i][0]][route[i][1]] = '2'
for i in range(len(l)):
for j in range(len(l[0])):
if l[i][j] == '1':
print(' ', end='')
elif l[i][j] == '0':
print('██', end='')
else:
print('░░', end='')
if i == m and j == n:
print('☀', end='')
print()
l = ['01010101001011001001010110010110100100001000101010',
'00001000100000101010010000100000001001100110100101',
'01111011010010001000001101001011100011000000010000',
'01000000001010100011010000101000001010101011001011',
'00011111000000101000010010100010100000101100000000',
'11001000110101000010101100011010011010101011110111',
'00011011010101001001001010000001000101001110000000',
'10100000101000100110101010111110011000010000111010',
'00111000001010100001100010000001000101001100001001',
'11000110100001110010001001010101010101010001101000',
'00010000100100000101001010101110100010101010000101',
'11100100101001001000010000010101010100100100010100',
'00000010000000101011001111010001100000101010100011',
'10101010011100001000011000010110011110110100001000',
'10101010100001101010100101000010100000111011101001',
'10000000101100010000101100101101001011100000000100',
'10101001000000010100100001000100000100011110101001',
'00101001010101101001010100011010101101110000110101',
'11001010000100001100000010100101000001000111000010',
'00001000110000110101101000000100101001001000011101',
'10100101000101000000001110110010110101101010100001',
'00101000010000110101010000100010001001000100010101',
'10100001000110010001000010101001010101011111010010',
'00000100101000000110010100101001000001000000000010',
'11010000001001110111001001000011101001011011101000',
'00000110100010001000100000001000011101000000110011',
'10101000101000100010001111100010101001010000001000',
'10000010100101001010110000000100101010001011101000',
'00111100001000010000000110111000000001000000001011',
'10000001100111010111010001000110111010101101111000']
if __name__ == '__main__':
x = 0;
y = 0
m = 25;
n = 40
if bfs(l, x, y, m, n) == 1:
route = get_route(father, pre_route)
prn_map(route, l, m, n)
6.写一个程序来解决这样一个问题:有2个坛子,其中一个的容量是4加仑,另一个的是3加仑。坛子上都没有刻度线。可以用水泵将它们装满水。如何使4加仑的坛子最后装满2加仑的水?
7.扩展练习6中的程序,将坛子的容量和较大的坛子中最后的水量作为参数。
水壶问题:源代码有注释
import queue
import numpy as np
class Kettle:
def __init__(self, w1, w2, result):
self.w1 = w1
self.w2 = w2
self.result = result
self.x = 0
self.y = 0
self.state_space = np.zeros((self.w1 + 1, self.w2 + 1))
self.q_temp = queue.Queue()
self.final = []
self.final_only_data = [[0, 0]]
def BFS(self):
self.q_temp.put([0, 0])
self.final.append([[0, 0], 'none', [0]])
self.state_space[0][0] = 1
while len(self.q_temp.queue) != 0:
a, b = self.q_temp.get()
if a == self.result or b == self.result:
continue
pre = [a, b]
self.x, self.y = a, b
self.Pour_X()
self.update(pre, a, b, 'Pour_X')
self.Pour_Y()
self.update(pre, a, b, 'Pour_Y')
self.Empty_X()
self.update(pre, a, b, 'Empty_X')
self.Empty_Y()
self.update(pre, a, b, 'Empty_Y')
self.Pour_X_To_Y()
self.update(pre, a, b, 'Pour_X_To_Y')
self.Pour_Y_To_X()
self.update(pre, a, b, 'Pour_Y_To_X')
return self.final, self.final_only_data
def update(self, pre, a, b, name):
if self.state_space[self.x][self.y] == 0:
self.state_space[self.x][self.y] = 1
self.q_temp.put([self.x, self.y])
self.final_only_data.append([self.x, self.y])
self.final.append([[self.x, self.y], name, pre])
self.x, self.y = a, b
def Pour_X(self):
self.x = self.w1
return True
def Pour_Y(self):
self.y = self.w2
return True
def Empty_X(self):
self.x = 0
return True
def Empty_Y(self):
self.y = 0
return True
def Pour_X_To_Y(self):
if (self.x + self.y) <= self.w2:
self.y += self.x
self.x = 0
else:
self.x = self.x - (self.w2 - self.y)
self.y = self.w2
return True
def Pour_Y_To_X(self):
if (self.x + self.y) <= self.w1:
self.x += self.y
self.y = 0
else:
self.y = self.y - (self.w1 - self.x)
self.x = self.w1
return True
if __name__ == '__main__':
kettle = Kettle(3, 4, 2)
final_list, tmp_list = kettle.BFS()
result = []
for i in range(len(final_list)):
if i == 0:
continue
data = final_list[i]
print('每行的数据为:', data)
pre = data[2]
print('当前的父节点为:', pre)
pre_index = tmp_list.index(pre)
print('当前父节点在列表中的位置为:', pre_index)
result = final_list[pre_index][-1]
print('当前父节点的路径为:', result)
a = result.copy()
a.append(i)
final_list[i][-1] = a
print('*********************************************')
def output(order):
print('x' + '\t' + 'y' + '\t' + 'method')
for i in order:
print(*final_list[i][0], sep='\t', end='\t')
print(final_list[i][1])
con = 0
for i in range(len(tmp_list)):
if 2 in tmp_list[i]:
print(f'-------------方法{con}--------------')
order = final_list[i][-1]
output(order)
con += 1
8.写一个程序来解决这样一个问题:3只羚羊和3只狮子准备乘船过河,河边有一艘能容纳2只动物的小船。但是,如果两侧河岸上的狮子数量大于领养数量,羚羊就会被吃掉。找到运送办法,使得所有动物都能安全渡河。
from chapter7.Graph import Graph
from chapter3.queue import Queue
def solution():
'''
3只羚羊和3只狮子过河问题:
1艘船只能容纳2只动物
当河岸上狮子数大于羚羊数,羚羊就会被吃掉
找到运输方法,让所有动物都过河
'''
# 定义合法的运输操作(i, j) , 例如(1, 0)表示运送羚羊1只,狮子0只
opt = [(1, 0), (0, 1), (1, 1), (2, 0), (0, 2)]
# 定义状态state(m, n, k), m表示羚羊数,n表示狮子数,k表示船在此岸还是彼岸
# stateA 表示A岸(此岸)的状态;stateB 表示B岸(彼岸)的状态;
# 初始状态
stateA = (3, 3, 1)
stateB = (0, 0, 0)
# BFS搜索
mygraph = Graph()
myqueue = Queue()
myqueue.enqueue((stateA, stateB))
sequence = [] # 剪枝记录(最后发现,有效状态只有15种)
sequence.append((stateA))
while True:
stateA, stateB = myqueue.dequeue()
if stateA == (0, 0, 0):
break
for o in opt:
# 一次从某岸到另一岸的运输
if stateA[2] == 1:
stateA_ = (stateA[0] - o[0], stateA[1] - o[1], stateA[2] - 1)
stateB_ = (stateB[0] + o[0], stateB[1] + o[1], stateB[2] + 1)
else:
stateB_ = (stateB[0] - o[0], stateB[1] - o[1], stateB[2] - 1)
stateA_ = (stateA[0] + o[0], stateA[1] + o[1], stateA[2] + 1)
# 运输后
if stateA_[0] and stateA_[0] < stateA_[1]: # 此岸在有羊的情况下,如果狼大于羊,则吃掉
continue
elif stateB_[0] and stateB_[0] < stateB_[1]: # 彼岸在有羊的情况下,如果狼大于羊,则吃掉
continue
elif stateA_[0] < 0 or stateA_[0] > 3 or stateA_[1] < 0 or stateA_[1] > 3: # 边界
continue
else:
# 剪枝
if stateA_ in sequence:
continue
else:
sequence.append(stateA_)
myqueue.enqueue((stateA_, stateB_))
mygraph.addEdge(stateA, stateA_, o)
return mygraph, sequence
if __name__ == '__main__':
g, sq = solution()
# 建立父子关系
for v_n in sq:
v = g.getVertex(v_n)
for nbr in v.getConnections():
if nbr.getColor() == 'white':
nbr.setPred(v)
nbr.setColor('gray')
v.setColor('black')
target = g.getVertex(sq[-1])
# 回溯,显示决策路径
i = 0
while target.getPred():
predv = target.getPred()
print(target.id, '<--', predv.getWeight(target), '--', predv.id)
target = predv
i += 1
三、总结
主要学习了
1.用python表示图这种抽象数据类型,
2.BFS找到无权重的最短路径:词梯问题,
3.Dijkstra算法求解带权重的最短路径,
4.使用强连通分量来简化图,
5.拓扑排序:任务有先后,
6.利用Prim最小生成树广播消息。

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)