本帖最后由 lightninng 于 2015-5-22 09:41 编辑
今天做了一个稍微难一点的chekio,来看看
索菲亚的机器人是有灵魂而且不是愚蠢的;他们可以拥有朋友和交朋友。事实上,他们已经正在为自己的并且只是为了机器人的社交网络工作!索菲亚已经收到有关机器人之间的关系的数据,她想更多地了解它们之间的关系。 我们有在机器人名字之间的用直线连线所组成的数组。 每个连接都被表示为一个包含由连字符隔开的两个机器人的名字的字符串。 例如:"dr101-mr99" 指的是 dr101 和 mr99 是朋友。 你需要写一个函数来确定机器人之间更复杂的关系。你将得到两个名字,尝试确定它们是通过共同纽带产生关系。例如:如果两个机器人有一个共同的朋友,或者他们的朋友拥有共同的朋友等等。 让我们看一下例子: scout2 和 scout3 有共同的朋友 scout1 所以他们是有关系的。 super 和 scout2 是通过 sscout ,scout4 和 scout1来产生关系的。 但是 dr101 和 sscout 是没有关系的。 输入: 三个参数:朋友的信息作为一个字符串元组;第一个名字(字符串形式);第二个名字(字符串形式)。 输出: 这两个机器人是否有关系。(bool) 范例: check_connection(
("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",
"scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),
"scout2", "scout3") == True
check_connection(
("dr101-mr99", "mr99-out00", "dr101-out00", "scout1-scout2",
"scout3-scout1", "scout1-scout4", "scout4-sscout", "sscout-super"),
"dr101", "sscout") == False
如何使用: 这个概念将帮助你找到纽带网络中没有过于明显的关系,还有如何运作社会网络。 前提: len(network) ≤ 45
如果 "name1-name2" 在 网络中,那么 "name2-name1" 就不在 网络中
3 ≤ len(drone_name) ≤ 6
first_name 和 second_name 在 网络中。 先贴自己的又长又不清晰的代码:def check_connection(network, first, second):
import collections
friend_map = collections.defaultdict(set)
for friendship in network:
friendship = set(friendship.split("-"))
for each_member in friendship:
friend_map[each_member].update(friendship - {each_member})
check_status = {}
for robot in friend_map:
check_status[robot] = False
check_status[first] = True
if second in friend_map[first]:
return True
path = [(first, list(friend_map[first]))]
while path:
cur_robot, friends = path.pop(-1)
next_robot = friends.pop(-1)
if friends:
path.append((cur_robot, friends))
if not check_status[next_robot]:
if second in friend_map[next_robot]:
return True
check_status[next_robot] = True
path.append((next_robot, list(friend_map[next_robot])))
return False
看到这个checkio,短暂的思考后最先想到的是深度搜索算法DFS(数据结构中图的内容,不明就里的同学请百度“深度优先搜索 广度优先搜索”),应该有递归和非递归两种方式,那这个思路很明确: 1、构造相邻矩阵:这里我用字典的方式,因为直接用二维列表的话会有很多多余的项 2、进行深度优先搜索:这里两点要注意,一是经过的点一定要标记已遍历;二是将路过的每个点进栈,同时用一种方法标识和这点相连的哪些点还未尝试过。因为我的语文可能比较渣,看不懂的鱼油可以研究深度优先算法,自行感受~ 让我们来看看checkio里面清楚而明确的DFS算法(递归版): def build_graph(network):
# 建立相邻矩阵
graphs = {}
vertices = []
for node in network:
f, s = node.split('-')
if f not in vertices:
vertices.append(f)
if s not in vertices:
vertices.append(s)
if f not in graphs:
graphs[f] = []
graphs[f].append(s)
if s not in graphs:
graphs[s] = []
graphs[s].append(f)
return graphs, vertices
def dfs(graphs, visited, f):
# 深度优先搜索核心代码
if not visited[f]:
visited[f] = True
friends = graphs[f]
for friend in friends:
dfs(graphs, visited, friend)
def check_connection__dfs(network, first, second):
# 主程序代码
graphs, vertices = build_graph(network)
visited = {node: False for node in vertices}
dfs(graphs, visited, first)
return visited[second]
但是实际上本题并不需要DFS的完整实现,因为DFS搜索过程中会栈中会保存搜索路径(递归算法其实也是用到了系统的栈,你可以在dfs函数中写上保存路径的代码),再回来看这个问题,从上面的图可以看出,其实只要找到first可以到达的点的集合,判断它是否包含second即可,这里为什么说到集合:1、集合无序,注意这题并不要求路径,只要看两点是否连通(通过其它点),另外对于两点直接连通关系也应该是无序的(如果 "name1-name2" 在 网络中,那么 "name2-name1" 就不在 网络中,说明两者含义相同 )2、集合的很多操作:交集&,并集|,差集-,以及一个集合是否属于另一个集合的判断(用<和>)都非常方便,来看看下面的代码(分别是solution中的第一位和第二位): # 找到所有最小生成树元素的集合,然后判断是否同时包含first和second
# 时间复杂度低,但空间占的稍多,可用于多处
def check_connection_first(network, first, second):
setlist = []
for connection in network:
s = ab = set(connection.split('-'))
# unify all set related to a, b
for t in setlist[:]: # we need to use copy
if t & ab: # check t include a, b
s |= t
setlist.remove(t)
setlist.append(s) # only s include a, b
return any(set([first, second]) <= s for s in setlist)
# 同上,但舍去一切不包含first, second的组合
# 相比上者时间复杂度高(遍历多次),空间占的稍少,针对实际问题
from itertools import chain
def check_connection_second(shakehands, me, you):
hands = {tuple(pair.split('-')) for pair in shakehands}
amigos = {me}
while amigos:
pairs = {pair for pair in hands if any(one in pair for one in amigos)}
amigos = set(chain(*pairs)) - amigos
if you in amigos:
return True
hands -= pairs
return False
第一段代码生成了所有可以相互连同的集合,也就是setlist,它当中的元素是集合,每个集合内的元素都可以相互到达,不同集合中的元素不可相互到达。第二段代码其实是应用了广度优先搜索,顺着me的所有相邻的对象铺开搜索,每次都已经用过的pair从hands中删除,每次都将已经检查过的点从amigos删除,当最后搜索完时(amigos为空时),如果you还未在amigos中出现 过说明不可到达。 请自己体会,这里几个pythonic的用法: pairs = {pair for pair in hands if any(one in pair for one in amigos)} if后面这句any表示,当amigos中有某个元素在pair中则为True,否则为False,用法有点出其不意(至少对我来说是这样)
amigos = set(chain(*pairs)) - amigos 这句中set(chain(*pairs))是把pairs中的所有元素放在一起然后转为集合(遇到同样的问题我可能就要用for循环了,不得不说itertools里面的几个函数十分的强大,学会了可以省很多事情),简单明了,chain函数请搜索itertools模块相关内容
另外提一下集合的相关用法,例子如下: >>> a = {1, 2, 3}
>>> b = {0, 2, 3}
>>> a= {1, 2, 3, 4}
>>> b = {0, 2, 3, 5}
>>> a & b # 交集
{2, 3}
>>> a | b # 并集
{0, 1, 2, 3, 4, 5}
>>> a - b # 差集
{1, 4}
>>> c = {2, 3, 4}
>>> c < a # c在a中
True
>>> c < b # c在b中
False
|