Leetcode 208. 实现 Trie (前缀树)

208. 实现 Trie (前缀树)

问题描述

Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。
  • void insert(String word) 向前缀树中插入字符串 word
  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false
  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

示例:

输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]

解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // 返回 True
trie.search("app");     // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app");     // 返回 True

提示:

  • 1 <= word.length, prefix.length <= 2000
  • wordprefix 仅由小写英文字母组成
  • insertsearchstartsWith 调用次数 总计 不超过 3 * 104

解题思路与代码实现

主要思路:

根据前缀树的概念实现,查找和加入类似于二叉搜索树。

class Trie {
    // 根节点
    private Node root;

    public Trie() {
        root = new Node();
    }

    /**
     * 插入字符串
     * 非递归写法
     */
    public void insert(String word) {
        if (word != null && word.length() > 0) {
            Node cur = root;
            for (int i = 0; i < word.length(); i++) {
                char c = word.charAt(i);
                // 不包含该字符c
                if (!cur.childrenMap.containsKey(c)) {
                    Node node = new Node();
                    cur.childrenMap.put(c, node);

                }
                cur = cur.childrenMap.get(c);
            }
            if (!cur.isWord) { // 标记为单词
                cur.isWord = true;
            }
        }
    }

    /**
     * 查找单词
     */
    public boolean search(String word) {
        Node cur = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            // 在字典树中逐个字符向下查找
            if (cur.childrenMap.containsKey(c)) {
                cur = cur.childrenMap.get(c);
            } else {
                return false;
            }
        }
        return cur.isWord;
    }

    /**
     * 查找是否存在指定前缀
     */
    public boolean startsWith(String prefix) {
        Node cur = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            // 在字典树中逐个字符向下查找
            if (cur.childrenMap.containsKey(c)) {
                cur = cur.childrenMap.get(c);
            } else {
                return false;
            }
        }
        return true;

    }
}

class Node {
    public boolean isWord; // 是否为单词
    public HashMap<Character, Node> childrenMap; // key当前节点的字符,value到下一个节点的映射

    public Node() {
        childrenMap = new HashMap<>();
    }

}

踩坑点

212. 单词搜索 II

问题描述

给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words返回所有二维网格上的单词

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例 1:

img

输入:board = [["o","a","a","n"],["e","t","a","e"],["i","h","k","r"],["i","f","l","v"]], words = ["oath","pea","eat","rain"]
输出:["eat","oath"]

示例 2:

img

输入:board = [["a","b"],["c","d"]], words = ["abcb"]
输出:[]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 12
  • board[i][j] 是一个小写英文字母
  • 1 <= words.length <= 3 * 104
  • 1 <= words[i].length <= 10
  • words[i] 由小写英文字母组成
  • words 中的所有字符串互不相同

解题思路与代码实现

主要思路:

首先根据words构造字典树,然后通过DFS+回溯找到board矩阵中出现在字典树中的字符串,用HashSet收集避免重复

    class Solution {
        public static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        public HashSet<String> set = new HashSet<>();  // 记录board数组能够产生的所有字符串
        public Trie trie = new Trie(); // 构建字典树用于查找

        public List<String> findWords(char[][] board, String[] words) {
            // 构建字典树
            for (String word : words) {
                trie.insert(word);
            }
            // 遍历board数组
            for (int i = 0; i < board.length; i++) {
                for (int j = 0; j < board[0].length; j++) {
                    // 以board[i][j]为起点,搜索所有可能的字符串
                    boolean[][] visited = new boolean[board.length][board[0].length];   // 标记数组
                    dfs(board, new StringBuilder(), visited, i, j, trie.root);
                }
            }
            // 收集words数组中的字符串在字典树中的部分
            return new ArrayList<>(set);
        }

        /**
         * dfs找出以board[i][j]开头的字符串
         * 这种结构确保每次递归执行后都会回溯
         */
        public void dfs(char[][] board, StringBuilder builder, boolean[][] visited,
                         int x, int y, Node node) {
            // 越界或者已访问过的跳过
            if (x < 0 || x >= board.length ||
                    y < 0 || y >= board[0].length || visited[x][y]) {
                return;
            }
            visited[x][y] = true;   // 标记已访问
            builder.append(board[x][y]);
            Node childNode = node.childrenMap.get(board[x][y]);
            // 如果能向下查找ch字符
            if (childNode != null) {
                if (childNode.isWord) { // 如果是单词,加入到结果集中
                    set.add(builder.toString());
                }
                // 向四周搜索
                for (int[] dir : DIRECTIONS) {
                    dfs(board, builder, visited, x + dir[0], y + dir[1], childNode);   // 递归
                }
            }
            // 回溯
            builder.deleteCharAt(builder.length() - 1);
            visited[x][y] = false;
        }
    }

    class Trie {
        // 根节点
        public Node root;

        public Trie() {
            root = new Node();
        }

        /**
         * 插入字符串
         */
        public void insert(String word) {
            if (word != null && word.length() > 0) {
                Node cur = root;
                for (char c : word.toCharArray()) {
                    if (!cur.childrenMap.containsKey(c)) {
                        cur.childrenMap.put(c, new Node());
                    }
                    cur = cur.childrenMap.get(c);
                }
                cur.isWord = true; // 标记为单词
            }
        }

        /**
         * 查找单词
         */
        public boolean search(String word) {
            Node cur = root;
            for (char c : word.toCharArray()) {
                if (cur.childrenMap.containsKey(c)) {
                    cur = cur.childrenMap.get(c);
                } else {
                    return false;
                }
            }
            return cur.isWord;
        }
    }

    class Node {
        public boolean isWord; // 是否为单词
        public HashMap<Character, Node> childrenMap; // key当前节点的字符,value到下一个节点的映射

        public Node() {
            childrenMap = new HashMap<>();
        }
    }

踩坑点

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值