算法:Trie字典树

本文介绍了字典树(Trie)数据结构及其优势,如空间换时间提高查询效率,常用于搜索引擎的词频统计。文章详细解析了LeetCode上的三道相关题目:208.实现Trie、212.单词搜索II和79.单词搜索,分别展示了Java和Python的实现,并结合深度优先搜索(DFS)解决实际问题。

字典树及经典题目

一、字典树Trie

(空间换时间)

  • 优点:最大限度的减少无谓的字符串比较,查询效率比哈希表高;
  • 使用场景:统计和排序大量的字符串。经常被搜索引擎用于文本词频统计;
  • 特性:
    • 节点不存完整单词;
    • 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串;
    • 不同路径代表的字符串都不相同;
    • 节点上添加数值,可以代表统计频次,对用户进行推荐;

二、经典题目

2.1 208. 实现 Trie (前缀树)

Java实现:

class Trie {

    private Trie[] t;
    private static final int R = 26;
    private boolean end;
    public Trie() {
        t = new Trie[R];
    }
    public void insert(String word) {
        Trie x = this;
        for (int i=0; i<word.length(); i++) {
            if (!x.containsTrie(word.charAt(i))) {
                x.putNode(word.charAt(i), new Trie());
            }
            x = x.getNode(word.charAt(i));
        }
        x.setEnd();
    }
    
    public boolean search(String word) {
        Trie p = searchPrefix(word);
        return p!=null && p.isEnd();
    }
    
    public boolean startsWith(String prefix) {
        Trie p = searchPrefix(prefix);
        return p!=null;
    }

    private Trie searchPrefix(String prefix) {
        Trie x = this;
        for (int i=0; i<prefix.length(); i++) {
            if (x.containsTrie(prefix.charAt(i))) {
                x = x.getNode(prefix.charAt(i));
            }else {
                return null;
            }
        }
        return x;
    }

    private boolean containsTrie(char c) {
        return getNode(c)!=null;
    }

    private Trie getNode(char c) {
        return t[c-'a'];
    }

    private void putNode(char c, Trie trie) {
        t[c-'a'] = trie;
    }

    private boolean isEnd() {
        return end;
    }

    private void setEnd() {
        this.end = true;
    }
}

python的实现

class Trie:

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


    def insert(self, word: str) -> None:
        m = self.root
        for w in word:
            m = m.setdefault(w, {})
        m[self.end_of_word] = {}


    def search(self, word: str) -> bool:
        m = self.root
        for w in word:
            if w in m:
                m = m[w]
            else:
                return False
        return self.end_of_word in m


    def startsWith(self, prefix: str) -> bool:
        m = self.root
        for w in prefix:
            if w in m:
                m = m[w]
            else:
                return False
        return True

2.2 212. 单词搜索 II

private int[] dx = {1, -1, 0, 0};
    private int[] dy = {0, 0, 1, -1};
    public List<String> findWords(char[][] board, String[] words) {
        Trie trie = new Trie();
        for (String word: words) {
            trie.insert(word);
        }

        Set<String> resList = new HashSet<>();
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<board.length; i++) {
            for (int j=0; j<board[i].length; j++) {
                sb.append(board[i][j]);
                char c = board[i][j];
                board[i][j] = '#';
                dfs(board, i, j, trie, sb, resList);
                sb.deleteCharAt(sb.length()-1);
                board[i][j] = c;
            }
        }
        return new ArrayList<>(resList);
    }

    private void dfs(char[][] board, int i, int j, Trie trie, StringBuilder sb, Set<String> resList) {
        if (!trie.findPrefix(sb.toString())) {
            return;
        }
        if (trie.findWord(sb.toString())) {
            resList.add(sb.toString());
        }
        for (int k=0; k<dx.length; k++) {
            int x = i+dx[k], y = j + dy[k];
            if (x<0 || x>=board.length || y<0 || y>=board[0].length || board[x][y]=='#') {
                continue;
            }
            sb.append(board[x][y]);
            char c = board[x][y];
            board[x][y] = '#';
            dfs(board, x, y, trie, sb, resList);
            sb.deleteCharAt(sb.length()-1);
            board[x][y] = c;
        }

    }

    private class Trie{
        private Trie[] tries;
        private static final int R = 26;
        private boolean end;
        public Trie() {
            tries = new Trie[R];
        }

        public void insert(String word) {
            Trie x = this;
            for (int i=0; i<word.length(); i++) {
                if (!x.containsTrie(word.charAt(i))) {
                    x.setTrie(word.charAt(i), new Trie());
                }
                x = x.getTrie(word.charAt(i));
            }
            x.setEnd();
        }

        public boolean findPrefix(String prefix) {
            return searchPrefix(prefix)!=null;
        }

        public boolean findWord(String word) {
            Trie t = searchPrefix(word);
            return t!=null && t.isEnd();
        }

        private Trie searchPrefix(String prefix) {
            Trie x = this;
            for (int i=0; i<prefix.length(); i++) {
                if (x.containsTrie(prefix.charAt(i))) {
                    x = x.getTrie(prefix.charAt(i));
                }else {
                    return null;
                }
            }
            return x;
        }

        private boolean isEnd() {
            return end;
        }

        private void setEnd() {
            end = true;
        }

        private void setTrie(char c, Trie node) {
            tries[c-'a'] = node;
        } 

        private boolean containsTrie(char c) {
            return getTrie(c)!=null;
        }

        private Trie getTrie(char c) {
            return tries[c-'a'];
        }
    }

2.3 79. 单词搜索 , DFS

    private int[] dx = {0, 0, 1, -1};
    private int[] dy = {1, -1, 0, 0};
    public boolean exist(char[][] board, String word) {
        // https://leetcode-cn.com/problems/word-search/
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<board.length; i++) {
            for (int j=0; j<board[0].length; j++) {
                sb.append(board[i][j]);
                char c = board[i][j];
                board[i][j] = '#';
                boolean res = dfs(i, j, board, sb, word);
                board[i][j] = c;
                sb.deleteCharAt(sb.length()-1);
                if (res) {
                    return res;
                }
            }
        }
        return false;
    }

    private boolean dfs(int i, int j, char[][] board, StringBuilder sb, String word) {
        if (!word.startsWith(sb.toString())) {
            return false;
        }
        if (word.equals(sb.toString())) {
            return true;
        }

        for (int k=0; k<dx.length; k++) {
            int x = i + dx[k], y = j + dy[k];
            if (x<0 || x>=board.length || y<0 || y>=board[0].length || board[x][y]=='#') {
                continue;
            }
            sb.append(board[x][y]);
            char c = board[x][y];
            board[x][y] = '#';
            boolean res = dfs(x, y, board, sb, word);
            board[x][y] = c;
            sb.deleteCharAt(sb.length()-1);
            if (res) {
                return res;
            }
        }
        return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值