二叉树之下

BFS实战

2018-09-03  本文已影响6人  小碧小琳

一、BFS三个步骤:

二、BFS的三个小栗子

遍历树,词梯问题(Word Ladder),被包围的岛屿(Surrounded Islands)

2.1、树

对于树,不需要判重,代码简单。

def bfs_tree(node):
    if node is None:
        return None
    else:
        queue = []
        #用list实现队列,左边是尾,右边是头。
        # 用insert方法把后入的元素插入最左边,入队尾部,用pop的方法弹出最右边
        #保证了先进先出的顺序
        queue.insert(0,node)
        #队列不为空,就继续执行循环
        while queue:
            cur = queue.pop()
            print(cur.val)
            #这里我定义的node的next属性,如果没有子节点,那么next就是空列表。
            for next in cur.nexts:
                queue.insert(0,next)

2.2、Word Ladder

对于图,就需要用一个hashSet判重了。这里的hashSet可以是set,也可是dict,但是不能是list!

2.2.1、为什么BFS找到的路线就是最短的----用图说明

对于一个词梯问题,在我们得到词的 集合以后,需要构造一个图。然后对图进行搜索操作。
参考以前写过的文章理解动态规划、BFS和DFS

引入Python的graph类,vertex类。这个类都有其属性。
用两种方法构造词梯的邻接表

若是把上面的 图画出来,就是这样的:


用BFS进行搜索操作时,从fool到sage。首先,BFS会搜索与fool邻接的点[foul,foil,cool,pool],在第二步,从foul搜索到foil时,发现foil已经存在于visited集合中了,说明有更短的路线可以从fool到foil(只要1步)。
这就说明,BFS找到的路线,一定是最短的。

2.2.2、LeetCode中的127,词梯问题

因为LeetCode中没有graph与vertex类,所以构造不了图。那怎样体现词与词之间的连词呢?比如上图中怎么去搜索fool相邻的四个词呢?

这里用三步去搜索:
1、对于fool的每个字母,
2、都用26个英文字母去替换,
3、再判断替换一个字母以后的单词,是不是存在于总的单词集合中。

比如对于fool,先对第一个字母 f 进行替换,从a到z,aool不存在于总单词集合中,cool,pool存在于总单词集合中,若是这个单词也不存在于visited中,那么就是有效的。

代码实现:

class Solution:
    def ladderLength(self, beginWord, endWord, wordList):

        if len(wordList) == 0:
            return 0
        if endWord not in wordList:
            return 0
        wordSet = set(wordList)
        word_len = len(beginWord)
        len_dict = {}
        len_dict[beginWord] = 1
        queue = []
        queue.insert(0,beginWord)

        while queue:
            cur_word = queue.pop()
            for i in range(word_len):
                for j in range(26):
                    alp = chr(j + ord('a'))
                    new_word = cur_word[0:i] +alp + cur_word[i+1:]
                    #若是等于目标单词,那就返回当前点的深度加1
                    if new_word == endWord:
                        return len_dict[cur_word] + 1
                    if new_word in wordSet and new_word not in len_dict:
                        queue.insert(0,new_word)
                        len_dict[new_word] = len_dict[cur_word] + 1
        #循环结束,仍是没有返回值,说明没有到达目标单词的路径,于是返回0
        return 0

注意这里用的是wordSet是一个集合,如果把visited的单词放到一个列表list中,因为Python中list的in方法时间复杂度很高,就会超时。

2.3、二维数组问题——LeetCode、200.Surrounded Islands

在上一篇文章中也讲过,对于数组问题,因为能够确定图的形状, 所以不一定非要用一个Set来保存已经搜索过的点。可以用一些小技巧,来更快速的实现。

比如这一题,先搜索数组的四条边界,可以把已经搜索过,下次遇到不会再进行搜索的“O”,替换成别的字母“+”,这样下次就不会再进行搜索了。

利用BFS把四条边的“O”都变成“+”以后,再把数组里面的被X包围的O都替换成X,再把“+”换回“O”,就能解决问题了。

需要注意,在Python中,set类型中,是不允许添加进list类型的元素的。
比如,set.add([i,j])就会出错,而set.add((i,j))就是正确的。在添加坐标时,需要注意这一点。

代码实现:

def solve(board):
    """
    :type board: List[List[str]]
    :rtype: void Do not return anything, modify board in-place instead.
    """

    ####
    #这里需要注意的是,因为采用的方式,是如果遇到O,就替换成+,
    # 因此不会存在再次遇到相同的O被访问的情况,因此不用添加visited了。
    #根那个词梯问题类似,不需要额外的visited,重要的不是这个集合,而是判断是否重复的步骤
    ####

    if len(board) == 0:
        return

    m = len(board)
    n = len(board[0])

    #定义一个bfs函数,传入点的索引i,j,
    #1、把满足条件的是’O‘的点都变成’+‘/2、并且将location添加进queue中
    #locat 是一个(i,j)元组,用以放入visited中的,因为list不能放入set中,元组能。
    def bfs(i,j):
        queue = []
        #如果locat的点是O,就变成+,并将locat添加进queue中

        if board[i][j] == 'O':
            board[i][j] = '+'
            queue.insert(0,(i,j))
        #若是该点不是’O,那这个函数就结束了。
        while queue:
            new_locat = queue.pop()
            i = new_locat[0]
            j = new_locat[1]
            #并对该点周围四个location判断是否有效且是‘O’,如果是,就变为‘+’,并且添加到queue中
            for i_new,j_new in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
                if i_new >= 0 and i_new <= m-1 and j_new >= 0 and j_new <= n-1 and \
                        board[i_new][j_new] == 'O':
                    board[i_new][j_new] = '+'
                    queue.insert(0,(i_new,j_new))

    #对四条边界的每个点,进行上述的bfs操作
    for i in range(m):
        #第一列
        bfs(i,0)
        print(board)
        #最后一列
        bfs(i,n-1)

    for i in range(n):
        #第一行
        bfs(0,i)
        #最后一行
        bfs(m-1,i)

    #四条边为O的都已经替换为+了,只需要把里面的被X包围的O替换成X,
    #把外面的没被包围的+还原为O,即可满足题意了
    for i in range(m):
        for j in range(n):
            if board[i][j] == 'O':
                board[i][j] = 'X'
            if board[i][j] == '+':
                board[i][j] = 'O'

print(solve([["O","O"],["O","O"]]))
上一篇 下一篇

猜你喜欢

热点阅读