Go语言中如何实现可区分位置的二维字母网格(类似二维字典/矩阵)?
嘿Jason,这个问题在单词搜索这类网格遍历算法里太常见了!我给你几个在Go语言里的实用实现方案,都能完美解决你区分相同字母不同位置的需求:
你可以直接定义一个包含坐标和字母的结构体,把二维数组的元素换成这个结构体,这样每个元素自带唯一的位置标识,完全不用担心相同字母混淆:
// 定义每个网格单元的结构体 type Cell struct { X, Y int Char rune // 如果是纯ASCII字母,用byte也可以 } // 初始化你的4×4网格 func initBoard() [4][4]Cell { return [4][4]Cell{ {{0, 0, 'a'}, {1, 0, 'h'}, {2, 0, 't'}, {3, 0, 'l'}}, {{0, 1, 's'}, {1, 1, 'n'}, {2, 1, 'o'}, {3, 1, 'a'}}, {{0, 2, 'e'}, {1, 2, 'f'}, {2, 2, 's'}, {3, 2, 't'}}, {{0, 3, 'w'}, {1, 3, 'a'}, {2, 3, 's'}, {3, 3, 'e'}}, } }
使用的时候,你可以通过board[y][x].Char获取字母,通过board[y][x].X和board[y][x].Y拿到唯一坐标。遍历过程中,只需要用一个二维布尔数组或者map记录已经访问过的坐标,就能避免重复使用同一位置的字母。
如果不想额外定义结构体,这个方案是网格遍历场景下的最优解——用原始的二维字母数组存储字符,再搭配一个二维布尔数组标记是否访问过该位置。坐标本身就是唯一标识,相同字母只要位置不同就不会冲突:
// 初始化原始字母网格 var board = [4][4]rune{ {'a', 'h', 't', 'l'}, {'s', 'n', 'o', 'a'}, {'e', 'f', 's', 't'}, {'w', 'a', 's', 'e'}, } // 在遍历前初始化访问标记数组 func initVisited() [][]bool { visited := make([][]bool, 4) for i := range visited { visited[i] = make([]bool, 4) } return visited }
遍历的时候,比如你要访问board[y][x],先检查visited[y][x]是否为false:如果是,就可以使用这个字母,然后把visited[y][x]设为true;回溯的时候再改回false,这样就完美实现了“不重复用同一位置”的要求。这个方案代码最少,运行效率也最高,是大多数单词搜索算法的标准实现方式。
如果你确实想实现类似“二维字典”的结构,可以用Go的Map,把包含X、Y的结构体作为键,字母作为值。因为Go中可比较的结构体可以直接作为Map的键,所以这个方案完全可行:
// 定义坐标结构体 type Coord struct { X, Y int } // 初始化坐标-字母映射的Map var boardMap = map[Coord]rune{ {0, 0}: 'a', {1, 0}: 'h', {2, 0}: 't', {3, 0}: 'l', {0, 1}: 's', {1, 1}: 'n', {2, 1}: 'o', {3, 1}: 'a', {0, 2}: 'e', {1, 2}: 'f', {2, 2}: 's', {3, 2}: 't', {0, 3}: 'w', {1, 3}: 'a', {2, 3}: 's', {3, 3}: 'e', }
标记已访问的话,可以用map[Coord]bool来记录用过的坐标。不过这个方案的缺点是遍历网格不如二维数组方便,需要迭代Map的所有键,所以更适合随机访问特定坐标的场景,而非连续遍历。
整体来说,我最推荐方案2——它兼顾了代码简洁性和遍历效率,完全适配你后续的单词拼接算法需求。如果你的业务逻辑需要更清晰的实体封装,方案1也是非常好的选择。
内容的提问来源于stack exchange,提问作者Jason

