目录
025实现二叉搜索树
挑战内容
026实现二叉树的深度优先遍历
挑战内容
027实现二叉树的广度优先遍历
挑战内容
028计算二叉树的高度
挑战内容
029实现高度最小的二叉搜索树
挑战内容
030为二叉树的每个层级创建一个列表
挑战内容
031检查二叉树是否平衡
挑战内容
032检查二叉树是否是搜索树
挑战内容
033查找二叉搜索树的中序后继结点
挑战内容
034查找二叉搜索树的第二大结点
挑战内容
035寻找两个结点的最近共同祖先
挑战内容
037实现最小堆
挑战内容
038实现查找树
挑战内容
039实现图
挑战内容
040实现图的深度优先遍历
挑战内容
041实现图的广度优先遍历
挑战内容
042检查图中两个顶点之间是否存在路径
挑战内容
043在加权图中找到最短路径
挑战内容
044在未加权图中找到最短路径
挑战内容
内容编译自 Donne Martin 的开源项目
实现二叉搜索树的插入方法。二叉搜索树如果不是一棵空树,则是具有下列以下性质的二叉树:
本次挑战中,你需要在 bst.py
文件中补充类 Node
和类 Bst
的空缺部分。
Node
类是定义的树结点。
Node
中的 __init__
方法用于初始化树结点,树结点包含数据元素 data
,指向左子结点的指针 left
,指向右子结点的指针 right
,以及指向父结点的指针 parent
。
Bst
类是定义的二叉搜索树。
Bst
中的 __init__
方法用于初始化二叉搜索树,参数 root
表示根结点。
Bst
中的 insert
方法用于进行插入操作,参数 data
用于指定插入的数据元素,需要返回插入的树结点。
代码:递归
class Node(object)://树节点,包括数据,左节点,右节点,父节点
def __init__(self, data):
self.data=data
self.left=None
self.right=None
self.parent=None
def __repr__(self)://返回打印方法
return str(self.data)
class Bst(object):
def __init__(self, root=None):
self.root=root
def insert(self, data)://插入函数,一般需要递归,但是题目中的insert()参数量不足(少一个节点变量),且涉及返回值,递归功能放进_insert()中,
if data is None://数据空类型
raise TypeError('Wrong')
if self.root is None://根节点为空
self.root=Node(data)
return self.root
else://进入大小比较选择·
return self._insert(self.root,data)
def _insert(self,node,data):
if node is None://注:此处无用,因为node如果为空,可直接放入数据节点,不会进入此方法
return Node(data)
if data<=node.data:
if node.left is None://为空,插入节点,指定父节点,返回插入节点
node.left=Node(data)
node.left.parent=node
return node.left
else://不为空,递归,知道遇到左右为空的节点(能填充子树且满足条件的节点)
return self._insert(node.left,data)
else:
if node.right is None:
node.right=Node(data)
node.right.parent=node
return node.right
else:
return self._insert(node.right,data)
实现二叉树的深度优先遍历,深度优先遍历方法包括中序遍历,前序遍历,后序遍历三种。深度优先遍历的介绍如下:
本次挑战中,你需要在 tree_dfs.py
文件中补充类 BstDfs
的空缺部分。
BstDfs
类继承“实现二叉搜索树”挑战中的 Bst
类。BstDfs
中的 in_order_traversal
,pre_order_traversal
, post_order_traversal
方法分别用于进行中序遍历操作,前序遍历操作,后序遍历操作。node
表示结点,在进行遍历操作时使用根结点;参数 visit_func
是一个获取结点数据,并将结点数据添加到数组的函数。其中 visit_func
函数无需自行定义,它的参数为 node
,无返回值。代码:
from bst import Bst
class BstDfs(Bst)://递归再递归
def in_order_traversal(self, node, visit_func):
if node is not None:
self.in_order_traversal(node.left,visit_func)
visit_func(node)
self.in_order_traversal(node.right,visit_func)
def pre_order_traversal(self, node, visit_func):
if node is not None:
visit_func(node)
self.pre_order_traversal(node.left,visit_func)
self.pre_order_traversal(node.right,visit_func)
def post_order_traversal(self,node, visit_func):
if node is not None:
self.post_order_traversal(node.left,visit_func)
self.post_order_traversal(node.right,visit_func)
visit_func(node)
实现二叉树的广度优先遍历。广度优先遍历的介绍如下:
本次挑战中,你需要在 tree_bfs.py
文件中补充类 BstDfs
的空缺部分。
BstDfs
类继承“实现二叉搜索树”挑战中的 Bst
类。BstDfs
中的 bfs
方法用于进行层次遍历操作。bfs
函数的参数 visit_func
是一个获取结点数据,并将结点数据添加到数组的函数。其中 visit_func
函数无需自行定义,它的参数为 node
,无返回值。代码:deque()双向队列,专业广度遍历
from bst import Bst
from collections import deque
class BstBfs(Bst):
def bfs(self, visit_func):
if self.root is None:
raise TypeError('Wrong')
queue=deque()
queue.append(self.root)//加入队列
while queue:
node=queue.popleft()//左弹出,并把弹出节点遍历,加入其左右节点
visit_func(node)
if node.left is not None:
queue.append(node.left)//加入队列
if node.right is not None:
queue.append(node.right)
实现一个算法计算二叉树的高度。树高度的介绍如下:
本次挑战中,你需要在 tree_height.py
文件中补充类 BstHeight
的空缺部分。
BstHeight
类继承“实现二叉搜索树”挑战中的 Bst
类。BstHeight
中的 height
方法用于计算树的高度。height
函数的参数 node
表示结点,在计算高度时使用根结点。height
函数需要返回一个表示树高度的数字。代码:递归,有则加一,无则为零
from bst import Bst
class BstHeight(Bst):
def height(self, node):
if node is None://空节点记0
return 0
return 1+max(self.height(node.left),self.height(node.right))//反复递归,每层加一,直到为空,取最大的高度
使用排序后的数组构建高度最小的二叉搜索树。
[1, 2, 3, 4, 5, 6, 7,]
,构建的二叉搜索树高度为 3
。本次挑战中,你需要在 min_bst.py
文件中补充类 MinBst
的空缺部分。
MinBst
中的 create_min_bst
方法用于构建二叉搜索树。create_min_bst
函数的参数 array
用于指定传入的数组,数组的元素按从小到大顺序排列。create_min_bst
函数需要返回根结点。代码:递归,反复找中点
from bst import Node
class MinBst(object):
def create_min_bst(self, array):
if array is None:
return
return self.mid(array,0,len(array)-1)
def mid(self,array,start,end):
if end
为二叉搜索树的每个层级创建一个列表。介绍如下:
7 -> 9 -> 5 -> 2 -> 6 -> 11
的二叉搜索树,结果为 [[7], [5, 9], [2, 6, 11]]
。本次挑战中,你需要在 level_lists.py
文件中补充类 BstLevelLists
的空缺部分。
BstLevelLists
类继承“实现二叉搜索树”挑战中的 Bst
类。BstLevelLists
中的 create_level_lists
方法用于为二叉搜索树的每个层级创建一个列表。create_level_lists
函数没有参数,需要返回一个列表。代码:deque()方法的另一种实现方式
from bst import Bst
class BstLevelLists(Bst):
def create_level_lists(self):
if self.root is None:
return
results=[]
col=[]
temp=[]
col.append(self.root)
while col:
results.append(col)
temp=list(col)
col=[]//清空本层,进入下一层
for parent in temp:
if parent.left is not None:
col.append(parent.left)
if parent.right is not None:
col.append(parent.right)
return results
实现一个算法检查一个二叉树是否是平衡二叉树。平衡二叉树的介绍如下:
本次挑战中,你需要在 balance_tree.py
文件中补充类 BstBalance
的空缺部分。
BstBalance
类继承“实现二叉搜索树”挑战中的 Bst
类。BstBalance
中的 check_balance
方法用于检查一个二叉树是否是平衡树。check_balance
函数没有参数,需要返回一个布尔值,即 True
或者 False
。raise
语句显示 TypeError
。代码:运用递归,不是平衡二叉树返回-1(用一个另类值作为标志符号),是平衡二叉树返回其深度(高度)。
from bst import Bst
class BstBalance(Bst):
def check_balance(self)://调用方法,返回最终结果
if self.root is None:
raise TypeError('wrong')
height=self.check(self.root)
return height!=-1//除非否定符号‘-1’,否则正常高度都是自然数
def check(self,node):
if node is None://没有该节点返回高度0
return 0
left_height=self.check(node.left)
if left_height==-1://如果等于-1,意味着左边不是平衡二叉树(diff判断是产生-1返回值的源头)
return -1
right_height=self.check(node.right)
if right_height==-1://如果等于-1,意味着右边不是平衡二叉树(diff判断是产生-1返回值的源头)
return -1
diff=abs(left_height-right_height)
if diff>1://源自定义的判断,-1仅代表一个否定符号
return -1
return 1+max(left_height,right_height)//正经的平衡二叉树,返回其高度,根节点加上子树最大高度
实现一个算法检查一个二叉树是否是二叉搜索树。
本次挑战中,你需要在 tree_validate.py
文件中补充类 BstValidate
的空缺部分。
BstValidate
类继承“实现二叉搜索树”挑战中的 Bst
类。BstValidate
中的 validate
方法用于检查一个二叉树是否是二叉搜索树。validate
函数没有参数,需要返回一个布尔值,即 True
或者 False
。raise
语句显示 TypeError
。代码:递归,检验左右节点数据是否满足要求。注意:二叉搜索树是没有重复数据的
from bst import Bst
import sys
class BstValidate(Bst):
def validate(self):
if self.root is None:
raise TypeError('d')
return self._check(self.root)
def _check(self,node,minimum=-sys.maxsize,maximum=sys.maxsize):
if node is None://结束时,节点为空,符合,运用于第三四个if判断
return True
if node.datamaximum://数据超出范围,不符合
return False
if not self._check(node.left,minimum,node.data)://左节点检测,上限为父节点数据
return False
if not self._check(node.right,node.data,maximum)://右节点检测,下限为父节点数据
return False
return True//叶子结点,数据不超过范围,符合
实现一个算法查找二叉搜索树某节点的中序后继结点。某节点的中序后继结点指的是,对二叉搜索树进行中序遍历时,此节点的后面一个结点。
本次挑战中,你需要在 tree_next.py
文件中补充类 BstSuccessor
的空缺部分。
BstSuccessor
中的 get_next
方法用于查找二叉搜索树某节点的中序后继结点。get_next
函数的参数 node
用于指定要查找的结点。get_next
函数需要返回后继结点的数据元素。None
。raise
语句显示 TypeError
。代码:中序后继节点分两类:向上找父节点,向下找子节点
from bst import Node
class BstSuccessor(object):
def get_next(self, node):
if node is None:
raise TypeError
if node.right is None:
return self.nextancestor(node)
else:
return self.nextchild(node.right)
def nextchild(self,node):
if node.left is None:
return node.data
else:
return self.nextchild(node.left)
def nextancestor(self,node):
if node.parent is not None:
if node.parent.data
实现一个算法查找二叉搜索树中结点数据第二大的结点。
本次挑战中,你需要在 second_largest.py
文件中补充类 Solution
的空缺部分。
Solution
类继承“实现二叉搜索树”挑战中的 Bst
类。Solution
中的 find_second_largest
方法用于查找二叉搜索树中结点数据第二大的结点。find_second_largest
函数的没有参数,需要返回一个结点。raise
语句显示 TypeError
。raise
语句显示 ValueError
。代码:第二大节点有三种状况:1.左右节点都有。2.只有右节点。3.只有左节点。1和2先找到最右侧最底端的树,最大值是右侧叶子结点,第二大是紧挨的父节点;3的最大节点是根节点(最上面的),第二大节点是左子树最大的节点。
from bst import Bst
class Solution(Bst):
def find_second_largest(self)://输出函数
if self.root is None:
raise TypeError
if self.root.right is None and self.root.left is None:
raise ValueError
return self.find(self.root)
def find(self,node)://主要功能函数
if node.right is not None://存在右节点。如果它不是叶子结点,对其递归寻找;如果是叶子节点,返回node,结束递归
if node.right.left is not None or node.right.right is not None:
return self.find(node.right)
else:
return node
else://不存在右节点,返回左子树最大值
return self.findmost(node.left)
def findmost(self,node)://辅助函数,为左子树寻最大值
if node.right is not None:
return self.findmost(node.right)
else:
return node
实现一个算法查找二叉树中两个结点的最近共同祖先。最近共同祖先的介绍如下:
T
时,对于任意两个结点 u
、 v
,找到一个离根最远的结点 x
,使得 x
同时是 u
和 v
的祖先,x
便是 u
、 v
的最近公共祖先。本次挑战中,你需要在 tree_lca.py
文件中补充类 BinaryTree
的空缺部分。
BinaryTree
中的 lca
方法用于查找二叉树中两个结点的最近共同祖先。lca
函数的参数 root
指根节点,node1
和 node2
用于指定要查找的两个结点。lca
函数需要返回一个结点。None
。None
。代码:复杂递归
class Node(object):
def __init__(self, key, left=None, right=None):
self.key = key
self.left = left
self.right = right
def __repr__(self):
return str(self.key)
class BinaryTree(object):
def lca(self, root, node1, node2)://主函数,非空判断+节点存在检测+寻找最近共祖
if None in (root,node1,node2):
return None
if self.check(root,node1) or self.check(root,node2):
return None
return self.find(root,node1,node2)
def check(self,root,node)://节点存在检测:方法是左右节点向下递归直到root代表的节点就是node节点,如果不是则递归到root代表的节点为空。递归过程中只要检测到存在就赋值False。不存在输出True,存在输出False
if root is None:
return True
if root is node:
return False
left=self.check(root.left,node)
right=self.check(root.right,node)
return left and right
def find(self,root,node1,node2)://最近共祖寻找:方法是左右节点向下递归:root代表的节点为空,返回空;root一旦是node1或者node2,返回该节点;左右子树分别向下递归寻找,都不为空则两节点分布在该数两侧,返回该树根节点为最近共祖,否则选择为空的那一侧的返回点
if root is None:
return None
if root is node1 or root is node2:
return root
left_node=self.find(root.left,node1,node2)
right_node=self.find(root.right,node1,node2)
if left_node is not None and right_node is not None:
return root
else:
return left_node if left_node is not None else right_node
实现最小堆插入,查看最小元素,删除最小元素的方法。最小堆排序的介绍如下:
h
的二叉树,除第 h
层外,其它各层 (1~h-1
) 的结点数都达到最大个数,第 h
层所有的结点都连续集中在最左边。本次挑战中,你需要在 min_heap.py
文件中补充类 MinHeap
的空缺部分。
MinHeap
类是定义的最小堆。MinHeap
中的 __init__
方法用于初始化。MinHeap
中的 __len__
方法用于返回最小堆元素个数。MinHeap
中的 extract_min
方法用于删除最小堆中的最小元素,在删除最小元素后,需要进行向下调整排序使得它也是最小堆。它没有参数,需要返回删除的元素值。如果最小堆为空,则返回 None
。MinHeap
中的 peek_min
方法用于查看最小堆中的最小元素。它没有参数,需要返回最小的元素值。如果最小堆为空,则返回 None
。MinHeap
中的 insert
方法用于进行插入操作,在插入元素后,需要向上调整排序使得它也是最小堆。参数 data
用于指定插入的数据元素,它没有返回值。如果传入的 data
为 None
,需要使用 raise
语句显示 TypeError
。MinHeap
中的 _bubble_up
为类的私有方法,它用于在插入时进行向上调整排序。参数 index
用于指定数组的索引,它没有返回值。MinHeap
中的 _bubble_down
为类的私有方法,它用于在删除时进行向下调整排序。参数 index
用于指定数组的索引,它没有返回值。代码:向上调整只需要比较父节点,向下调整需要和最小的子节点比较
class MinHeap(object):
def __init__(self):
self.array = []
def __len__(self):
return len(self.array)
def extract_min(self):
if not self.array:
return None
if len(self.array)==1:
return self.array.pop(0)
minimum=self.array[0]
#移动最后一个元素到根节点
self.array[0]=self.array.pop(-1)
self._bubble_down(index=0)
return minimum
def peek_min(self):
return self.array[0] if self.array else None
def insert(self, data):
if data is None:
raise TypeError
self.array.append(data)
self._bubble_up(index=len(self.array)-1)
def _bubble_up(self, index):
if index==0:
return
index_parent=(index-1)//2
if self.array[index]self.array[min_index]:
self.array[index],self.array[min_index]=self.array[min_index],self.array[index]
self._bubble_down(min_index)
def _find_smaller_child(self,index)://辅助寻找最小子节点
left_index=2*index+1
right_index=2*index+2
#没有右孩子
if right_index>=len(self.array):
#更没有左孩子
if left_index>=len(self.array):
return -1
else:
return left_index
else:
return left_index if self.array[left_index]
实现查找树查找,插入,删除,列出元素的方法。查找树的介绍如下:
本次挑战中,你需要在 trie.py
文件中补充类 Node
和类 Trie
的空缺部分。
Node
类是定义的查找树结点。
Node
中的 __init__
方法用于初始化查找树结点。结点包含数据元素 data
,指向父结点的指针 parent
,表示此结点是否为字符串结尾的布尔值 terminates
,以及此结点的子结点 children
。其中子结点 children
为字典形式,字典的键为该子结点的字符,值为该子结点的结点。
Trie
类是定义的查找树。
Trie
中的 __init__
方法用于初始化,包含根结点 root
。
Trie
中的 find
方法用于进行查找操作。它的参数 word
用于指定要查找的字符串,需要返回此字符串结尾的结点。如果找不到此字符串,需要返回 None
。如果查找的字符串为 None
,需要使用 raise
语句显示 TypeError
。
Trie
中的 insert
方法用于进行插入操作。它的参数 word
用于指定要插入的字符串,没有返回值。如果插入的字符串为 None
,需要使用 raise
语句显示 TypeError
。
Trie
中的 remove
方法用于进行删除操作。它的参数 word
用于指定要删除的字符串,没有返回值。如果要删除的字符串为 None
,需要使用 raise
语句显示 TypeError
。如果要删除的字符串不存在,需要使用 raise
语句显示 KeyError
。
Trie
中的 list_words
方法用于进行列出查找树里的所有字符串,它没有参数,需要返回包含字符串的数组。
代码:学习列出所有字符串的方法
from collections import OrderedDict
class Node(object):
def __init__(self, data, parent=None, terminates=False):
self.data=data
self.terminates=False
self.parent=parent
self.children={}
class Trie(object):
def __init__(self):
self.root=Node('')
def find(self, word):
if word is None:
raise TypeError
node=self.root
for char in word:
if char in node.children:
node=node.children[char]
else:
return None
return node if node.terminates else None
def insert(self, word):
if word is None:
raise TypeError
node=self.root
parent=None
for char in word:
if char in node.children:
node=node.children[char]
else:
node.children[char]=Node(char,parent=node)
node=node.children[char]
node.terminates=True
def remove(self, word):
if word is None:
raise TypeError
node=self.find(word)
if node is None:
raise KeyError
node.terminates=False
parent=node.parent
while parent is not None:
if node.children:
return
del node
node=parent
parent=parent.parent
def list_words(self)://主方法,负责调用返回
result=[]
curr_word=''
self._list_words(self.root,curr_word,result)
return result
def _list_words(self,node,curr_word,result)://功能函数
if node is None:
return
for data,child in node.children.items()://数据和节点同时遍历
if child.terminates://到达末尾,加上遍历得到的word
result.append(curr_word+data)
self._list_words(child,curr_word+data,result)
实现图插入顶点,插入有向加权边,插入无向加权边的方法。图的介绍如下:
本次挑战中,你需要在 graph.py
文件中补充类 Node
和类 Trie
的空缺部分。
Node
类是定义的顶点。
Node
中的 __init__
方法用于初始化顶点。顶点包含数据元素 key
,表示状态的布尔值 visit_state
,表示入度的数值 incoming_edges
,表示顶点的字典 adj_nodes
,表示边的字典 adj_weights
。其中 adj_nodes
的键为数据元素,值为顶点。而 adj_weights
的键为数据元素,值为权重。
Node
中的 __repr__
方法用于打印顶点的数据元素。
Node
中的 __lt__
方法用于比较顶点的数据元素。
Node
中的 add_neighbor
方法用于增加顶点间的连接。参数 neighbor
用于指定相连的顶点,参数 weight
用于指定边的权重。如果 neighbor
或者 weight
为 None
,需要使用 raise
语句显示 TypeError
。
Node
中的 remove_neighbor
方法用于删除顶点间的连接。参数 neighbor
用于指定相连的顶点。如果 neighbor
为 None
,需要使用 raise
语句显示 TypeError
。如果 neighbor
不是相连的顶点,需要使用 raise
语句显示 KeyError
。
Trie
类是定义的图。
Trie
中的 __init__
方法用于初始化,包含顶点 nodes
。其中 nodes
为字典格式,字典的键为数据元素,字典的值为顶点。
Trie
中的 add_node
方法用于增加顶点。参数 key
用于指定顶点的数据元素。如果 key
为 None
,需要使用 raise
语句显示 TypeError
。
Trie
中的 add_edge
方法用于增加有向边。参数 source
用于指定起始顶点的数据元素,参数 dest
用于指定终点的数据元素,参数 weight
用于指定边的权重。如果 source
或者 dest
为 None
,需要使用 raise
语句显示 TypeError
。如果 source
或者 dest
不在已有的顶点中,需要使用数据元素创建结点后再增加边。
Trie
中的 add_undirected_edge
方法用于增加无向边,增加无向边可认为是增加双向的有向边。参数 source
和 dest
分别用于指定两个顶点的数据元素,参数 weight
用于指定边的权重。如果 source
或者 dest
为 None
,需要使用 raise
语句显示 TypeError
。如果 source
或者 dest
不在已有的结点中,需要使用数据元素创建结点后再增加边。
代码:综合性的实现
from enum import Enum
class State(Enum):
unvisited = 0
visiting = 1
visited = 2
class Node:
def __init__(self, key):
self.key = key
self.visit_state = State.unvisited
self.incoming_edges = 0
self.adj_nodes = {} # Key = key, val = Node
self.adj_weights = {} # Key = key, val = weight
def __repr__(self):
return str(self.key)
def __lt__(self, other):
return self.key < other.key
def add_neighbor(self, neighbor, weight=0):
if neighbor is None or weight is None:
raise TypeError('neighbor or weight cannot be None')
neighbor.incoming_edges += 1
self.adj_weights[neighbor.key] = weight
self.adj_nodes[neighbor.key] = neighbor
def remove_neighbor(self, neighbor):
if neighbor is None:
raise TypeError('neighbor cannot be None')
if neighbor.key not in self.adj_nodes:
raise KeyError('neighbor not found')
neighbor.incoming_edges -= 1
del self.adj_weights[neighbor.key]
del self.adj_nodes[neighbor.key]
class Graph:
def __init__(self):
self.nodes = {} # Key = key, val = Node
def add_node(self, key):
if key is None:
raise TypeError('key cannot be None')
if key not in self.nodes:
self.nodes[key] = Node(key)
return self.nodes[key]
def add_edge(self, source, dest, weight=0):
if source is None or dest is None:
raise KeyError('Invalid key')
if source not in self.nodes:
self.add_node(source)
if dest not in self.nodes:
self.add_node(dest)
self.nodes[source].add_neighbor(self.nodes[dest], weight)
def add_undirected_edge(self, source, dest, weight=0):
if source is None or dest is None:
raise TypeError('key cannot be None')
self.add_edge(source, dest, weight)
self.add_edge(dest, source, weight)
实现图的深度优先遍历方法。图的深度优先遍历介绍如下:
本次挑战中,你需要在 graph_dfs.py
文件中补充类 GraphDfs
的空缺部分。
GraphDfs
类继承“实现图”挑战中的 Graph
类。GraphDfs
中的 dfs
方法用于进行深度优先遍历操作。dfs
函数的参数 root
表示起始顶点,参数 visit_func
是一个获取顶点数据,并将顶点数据添加到数组的函数。其中 visit_func
函数无需自行定义,它的参数为 node
,无返回值。代码:像树的深度遍历一样,对子节点遍历时进行自调用递归
from graph import Graph,State
class GraphDfs(Graph):
def dfs(self, root, visit_func):
if root is None:
return
visit_func(root)
root.visit_state=State.visited
for node in root.adj_nodes.values():
if node.visit_state==State.unvisited:
self.dfs(node,visit_func)
实现图的广度优先遍历方法。图的广度优先遍历介绍如下:
本次挑战中,你需要在 graph_bfs.py
文件中补充类 GraphBfs
的空缺部分。
GraphBfs
类继承“实现图”挑战中的 Graph
类。GraphBfs
中的 bfs
方法用于进行广度优先遍历操作。dfs
函数的参数 root
表示起始顶点,参数 visit_func
是一个获取顶点数据,并将顶点数据添加到数组的函数。其中 visit_func
函数无需自行定义,它的参数为 node
,无返回值。代码:类似树的广度优先遍历,遍历,使用队列一层层插入弹出
from graph import Graph, State
from collections import deque
class GraphBfs(Graph):
def bfs(self, root, visit_func):
if root is None:
return
queue = deque()
queue.append(root)
root.visit_state = State.visited
while queue:
node = queue.popleft()
visit_func(node)
for adjacent_node in node.adj_nodes.values():
if adjacent_node.visit_state == State.unvisited:
queue.append(adjacent_node)
adjacent_node.visit_state = State.visited
实现一个算法来检查图中两个顶点之间是否存在路径。
本次挑战中,你需要在 graph_path.py
文件中补充类 GraphPathExists
的空缺部分。
GraphPathExists
类继承“实现图”挑战中的 Graph
类。GraphPathExists
中的 path_exists
方法用于检查图中两个顶点之间是否存在路径。path_exists
函数的参数 start
和 end
分别用于指定起始顶点和结束顶点。path_exists
函数的需要返回一个布尔值,即 True
或者 False
。start
和 end
为相同顶点时,需要返回 True
。start
和 end
中有 None
时,需要返回 False
。代码:本类路径问题运用广度优先遍历进行搜索
from graph import Graph, State
from collections import deque
class GraphPathExists(Graph):
def path_exists(self, start, end):
if start is None or end is None:
return False
if start is end:
return True
queue = deque()
queue.append(start)
start.visit_state = State.visited
while queue:
node = queue.popleft()
if node is end:
return True
for adj_node in node.adj_nodes.values():
if adj_node.visit_state == State.unvisited:
queue.append(adj_node)
adj_node.visit_state = State.visited
return False
实现一个算法来查找有向加权图中两个顶点之间的最短路径。
本次挑战中,你需要在 shortest_path.py
文件中补充类 ShortestPath
的空缺部分。
ShortestPath
中的 __init__
方法用于初始化,需要包含属性 graph
和 path_weight
。另外的属性可根据需要进行添加。
初始化的参数为 graph
,是一个有向加权图。
属性 graph
是传入的有向加权图。当传入的 graph
为 None
时,需要使用 raise
语句显示 TypeError
。
属性 path_weight
为字典形式,键为顶点,值为初始顶点到该顶点的最短路径的权值和,其中初始顶点的值为 0。
ShortestPath
中的 shortest_path
方法用于查找有向加权图中两个顶点之间的最短路径。
shortest_path
函数的参数 start_node_key
和 end_node_key
分别用于指定起始顶点的数据元素和结束顶点的数据元素。
shortest_path
函数的需要返回一个数组,数组包含路径的数据元素,路径需要包含起始顶点和结束顶点。
如果 start_node_key
或者 end_node_key
为 None
,需要使用 raise
语句显示 TypeError
。
如果 start_node_key
或者 end_node_key
不在已有的结点中,需要使用 raise
语句显示 ValueError
。
代码:比较复杂,没看懂
import sys
class ShortestPath(object):
def __init__(self, graph):
if graph is None:
raise TypeError('graph cannot be None')
self.graph = graph
self.previous = {} # Key: node key, val: prev node key, shortest path
self.path_weight = {} # Key: node key, val: weight, shortest path
self.remaining = PriorityQueue() # Queue of node key, path weight
for key in self.graph.nodes.keys():
# Set each node's previous node key to None
# Set each node's shortest path weight to infinity
# Add each node's shortest path weight to the priority queue
self.previous[key] = None
self.path_weight[key] = sys.maxsize
self.remaining.insert(
PriorityQueueNode(key, self.path_weight[key]))
def find_shortest_path(self, start_node_key, end_node_key):
if start_node_key is None or end_node_key is None:
raise TypeError('Input node keys cannot be None')
if (start_node_key not in self.graph.nodes or
end_node_key not in self.graph.nodes):
raise ValueError('Invalid start or end node key')
# Set the start node's shortest path weight to 0
# and update the value in the priority queue
self.path_weight[start_node_key] = 0
self.remaining.decrease_key(start_node_key, 0)
while self.remaining:
# Extract the min node (node with minimum path weight)
# from the priority queue
min_node_key = self.remaining.extract_min().obj
min_node = self.graph.nodes[min_node_key]
# Loop through each adjacent node in the min node
for adj_key in min_node.adj_nodes.keys():
# Node's path:
# Adjacent node's edge weight + the min node's
# shortest path weight
new_weight = (min_node.adj_weights[adj_key] +
self.path_weight[min_node_key])
# Only update if the newly calculated path is
# less than the existing node's shortest path
if self.path_weight[adj_key] > new_weight:
# Set the node's previous node key leading to the shortest path
# Update the adjacent node's shortest path and
# update the value in the priority queue
self.previous[adj_key] = min_node_key
self.path_weight[adj_key] = new_weight
self.remaining.decrease_key(adj_key, new_weight)
# Walk backwards to determine the shortest path:
# Start at the end node, walk the previous dict to get to the start node
result = []
current_node_key = end_node_key
while current_node_key is not None:
result.append(current_node_key)
current_node_key = self.previous[current_node_key]
# Reverse the list
return result[::-1]
# 挑战24的参考答案
class PriorityQueueNode(object):
def __init__(self, obj, key):
self.obj = obj
self.key = key
def __repr__(self):
return str(self.obj) + ': ' + str(self.key)
class PriorityQueue(object):
def __init__(self):
self.array = []
def __len__(self):
return len(self.array)
def insert(self, node):
self.array.append(node)
return self.array[-1]
def extract_min(self):
if not self.array:
return None
minimum = sys.maxsize
for index, node in enumerate(self.array):
if node.key < minimum:
minimum = node.key
minimum_index = index
return self.array.pop(minimum_index)
def decrease_key(self, obj, new_key):
for node in self.array:
if node.obj is obj:
node.key = new_key
return node
实现一个算法来查找未加权图中两个顶点之间的最短路径。
本次挑战中,你需要在 sp_unweighted.py
文件中补充类 GraphShortestPath
的空缺部分。
GraphShortestPath
类继承“实现图”挑战中的 Graph
类。GraphShortestPath
中的 shortest_path
方法用于查找有向未加权图中两个顶点之间的最短路径。shortest_path
函数的参数 source_key
和 dest_key
分别用于指定起始顶点的数据元素和结束顶点的数据元素。shortest_path
函数的需要返回一个数组,数组包含路径的数据元素,路径需要包含起始顶点和结束顶点。 如果起始顶点和结束顶点为相同顶点,数组只需要包含一个元素。start_node_key
或者 end_node_key
为 None
,则返回 None
。start_node_key
和 end_node_key
之间没有路径,也返回 None
。代码:
from graph import Graph, State
from collections import deque
class GraphShortestPath(Graph):
def shortest_path(self, source_key, dest_key):
if source_key is None or dest_key is None:
return None
if source_key is dest_key:
return [source_key]
prev_node_keys = self._shortest_path(source_key, dest_key)
if prev_node_keys is None:
return None
else:
path_ids = [dest_key]
prev_node_key = prev_node_keys[dest_key]
while prev_node_key is not None:
path_ids.append(prev_node_key)
prev_node_key = prev_node_keys[prev_node_key]
return path_ids[::-1]
def _shortest_path(self, source_key, dest_key):
queue = deque()
queue.append(self.nodes[source_key])
prev_node_keys = {source_key: None}
self.nodes[source_key].visit_state = State.visited
while queue:
node = queue.popleft()
if node.key is dest_key:
return prev_node_keys
# prev_node = node
for adj_node in node.adj_nodes.values():
if adj_node.visit_state == State.unvisited:
queue.append(adj_node)
prev_node_keys[adj_node.key] = node.key
adj_node.visit_state = State.visited
return None