深度优先与字典树

数据结构学习

Posted by Pacras on March 15, 2019

推荐一波 B站上的 算法与数据结构课程

深度优先算法

        深度优先算法广度优先算法在学习数据结构 图的遍历 中都已经接触过,其中深度有限算法更符合计算机的思维习惯,应用更加广泛。

实例

       比如 N皇后问题数独问题等。以 LeetCode 中 N皇后II 为例:如何将 n 个皇后放在 n 乘 n 的棋盘上,使得皇后之间不能相互攻击。给出所有可行的方案。        这怎么与深度优先有关呢?我们在某一行中放置皇后之后,便去研究下一行该怎么放,直至成功获取一个方案,或这一步不合理需要另作选择。这便是 深度优先 的应用。        假设我们在 (i, j) 位置放置了皇后,那么第 i 行,第j 列, 斜线 y-x = j-iy+x = j+i 也不能放了。我们可以在 DFS() 函数中传递已经占用的列、斜线等信息,行数则不需要,因为我们是一行一行地放,皇后们自然不会在同一行。        代码如下所示,用 cur_state 表示占用的列数,row 为当前行数,xy_subxy_sum 是斜线方程:

def DFS(self, cur_state, row, n, xy_sub, sy_sum):
    if row == self.n:
        self.res.append(cur_state)
    return 
    for col in range(n):
        if col in cur_state or col-row in xy_sub or col+row in xy_sum:
            continue
        self.DFS(cur_state+[col], row+1, n, xy_sub+[col-row], xy_sum+[col+row])

字典树

       字典树也叫前缀树,Trie树。实现:

def __init__(self):
   self.root = {}
   self.end_of_word = '#'

def insert(self, word):
   node = self.root
   for char in word:
       node = node.setdefault(char, {})
    node[self.end_of_word] = self.end_of_word

def search(self, word):
   node = self.root
   for char in word:
       if char not in word:
           return False
       node = node[char]
    return self.end_of_word in node

def startsWith(self, prefix):
   node = self.root
   for char in word:
       if char not in word:
           return False
       node = node[char]
   return self.end_of_word in node

       可以看出这是一个嵌套,比如有两个单词 eggeleven 在 Trie 中,它的结构应该是:`{ e: { g: { g: { #: { # } } }, l :{ e: { v: { e : { n: { # : { # } } } } } } } }。 `

示例 单词搜索II

        这道题目我们就可以先把单词列表放到 Trie 中:

trie = {}
for word in words:
    node = trie
    for char in word:
        node.setdefault(char, {})
    node[self.end_of_word] = self.end_of_word

       之后再用深度优先算法在二维网格中搜索:

def dfs(self, board, i, j, cur_word, cur_dict):
   if self.end_of_word in cur_dict:
        self.res.add(cur_word)
        return 
    temp = board[i][j]
    #设置标识,避免重复访问
    board[i][j] = '@'
    dx, dy = [-1, 1, 0, 0], [0, 0, -1, 1]
    for k in range(4):
        x, y = i + dx[k], j+ dy[k]
        if x in range(self.m) and y in range() and board[x][y] != '@' and board[x][y] in cur_dict:
        self.dfs(board, x, y, cur_word+board[x][y], cue_dict[board[x][y])
    board[x][y] = temp

       值得注意的是,在搜索过程中,需要标识访问过的位置,不然将会出现重复访问。比如判断 "aaa"是否在[["a", "a"]]中,很显然不在,但是如果没有标识的话就会返回在。

总结

       深度优先Trie树之前接触较少,现在学了之后有些难题也不那么吓人了。