【剑指offer2】 chap10 前缀树

十、前缀树

1.知识梳理

  • 又称为字典树,树状结构存储字典,多叉树,每个节点表示字符串的一个字符

  • 字符串终止标志

  • 无特殊说明,只包含小写英文字母

2.题型总结

(1)前缀树模板

  • 三大基本操作:创建、插入、查找

剑指 Offer II 062. 实现前缀树

class Trie {    
  static class TrieNode {//        
    //孩子节点        
    TrieNode children[];        
    //结尾标志        
    boolean isWord;        
    public TrieNode() {            
      children = new TrieNode[26];        
    }    
  }    
  //根节点
  private TrieNode root;  
  
  public Trie() {        
    root = new TrieNode();    
  }    
  
  public void insert(String word) 
  {        
    TrieNode node = root;        
    for (char ch : word.toCharArray()) {            
      if (node.children[ch - 'a'] == null) {                
        node.children[ch - 'a'] = new TrieNode();            
      }            
      node = node.children[ch - 'a'];        
    }        node.isWord = true;    
  }    
  
  public boolean search(String word) {        
    TrieNode node = root;        
    for (char ch : word.toCharArray()) {            
      if (node.children[ch - 'a'] == null) {                
        return false;            
      }            
      node = node.children[ch - 'a'];        
    }        
    return node.isWord;    
  }    
  
  public boolean startsWith(String prefix) {        
    TrieNode node = root;        
    for (char ch : prefix.toCharArray()) {            
      if (node.children[ch - 'a'] == null) {                
        return false;            
      }            
      node = node.children[ch - 'a'];        
    }        
    return true;    
  }
}

剑指 Offer II 063. 替换单词

1、构建字典树

2、逐个转换 

class Solution {
    public String replaceWords(List<String> dictionary, String sentence) {

    }
}

(2)前缀树+DFS

剑指 Offer II 064. 神奇的字典

class MagicDictionary {
    static class TrieNode {
        TrieNode children[];
        boolean isWord;

        public TrieNode() {
            children = new TrieNode[26];
        }
    }

    TrieNode root;

    public MagicDictionary() {
        root = new TrieNode();
    }

    public void buildDict(String[] dictionary) {
        for (String word : dictionary) {
            TrieNode node = root;
            for (char ch : word.toCharArray()) {
                if (node.children[ch - 'a'] == null) {
                    node.children[ch - 'a'] = new TrieNode();
                }
                node = node.children[ch - 'a'];
            }
            node.isWord = true;
        }
    }

    public boolean search(String searchWord) {
        return dfs(root, searchWord, 0, 0);
    }

    private boolean dfs(TrieNode root, String searchWord, int i, int flag) {
        if (root == null) {
            return false;
        }
        if (root.isWord && i == searchWord.length() && flag == 1) {
            return true;
        }
        if (i < searchWord.length() && flag <= 1) {
            boolean found = false;
            for (int j = 0; j < 26 && !found; j++) {
                int nextFlag = (j == searchWord.charAt(i) - 'a') ? flag : flag + 1;
                found = dfs(root.children[j], searchWord, i + 1, nextFlag);
            }
            return found;
        }
        return false;
    }
}

/**
 * Your MagicDictionary object will be instantiated and called as such:
 * MagicDictionary obj = new MagicDictionary();
 * obj.buildDict(dictionary);
 * boolean param_2 = obj.search(searchWord);
 */

剑指 Offer II 065. 最短的单词编码

先构造字典树(倒序添加),然后dfs计算每条到达叶子节点路径长的总和

class Solution {

    //构造前缀树
    class TrieNode {
        public TrieNode[] children;

        public TrieNode() {
            children = new TrieNode[26];
        }
    }

    public int minimumLengthEncoding(String[] words) {
        TrieNode root = buildTrie(words);

        int[] total = {0};

        dfs(root, 1, total);
        return total[0];
    }

    private void dfs(TrieNode root, int length, int[] total) {
        boolean isLeaf = true;
        for (TrieNode child : root.children) {
            if (child != null) {
                isLeaf = false;
                dfs(child, length + 1, total);
            }
        }
        if (isLeaf) {
            total[0] += length;
        }
    }

    //构造前缀树,注意从每个单词的最后一个字符倒过来往前存储
    private TrieNode buildTrie(String[] words) {
        TrieNode root = new TrieNode();
        for (String word : words) {
            TrieNode curr = root;
            for (int i = word.length() - 1; i >= 0; i--) {
                char ch = word.charAt(i);
                if (curr.children[ch - 'a'] == null) {
                    curr.children[ch - 'a'] = new TrieNode();
                }
                curr = curr.children[ch - 'a'];
            }
        }
        return root;
    
    }
}

剑指 Offer II 066. 单词之和

插入操作构造字典树,然后找到前缀末尾,开始递归调用求和函数

class MapSum {
    class TrieNode {
        TrieNode children[];
        int value;

        public TrieNode() {
            children = new TrieNode[26];
        }
    }

    private TrieNode root;

    public MapSum() {
        root = new TrieNode();
    }

    public void insert(String key, int val) {
        TrieNode curr = root;
        for (char ch : key.toCharArray()) {
            if (curr.children[ch - 'a'] == null) {
                curr.children[ch - 'a'] = new TrieNode();
            }
            curr = curr.children[ch - 'a'];
        }
        curr.value = val;
    }

    public int sum(String prefix) {
        //先判断有没有prefix的
        TrieNode curr = root;

        for (char ch : prefix.toCharArray()) {
            if (curr.children[ch - 'a'] == null) {
                return 0;
            }
            curr = curr.children[ch - 'a'];
        }
        return getSum(curr);
    }

    private int getSum(TrieNode curr) {
        if (curr == null) return 0;
        int ans = curr.value;
        for (TrieNode child : curr.children) {
            ans += getSum(child);
        }
        return ans;
    }
}

/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum obj = new MapSum();
 * obj.insert(key,val);
 * int param_2 = obj.sum(prefix);
 */

 (3)前缀树+遍历

剑指 Offer II 067. 最大的异或

先构造字典树,然后遍历每个数,进行查找,优先选择与当前位相反的节点,最后选择最大值即可

 

class Solution {
    static class TrieNode {
        TrieNode children[];

        public TrieNode() {
            children = new TrieNode[2];
        }
    }

//    private TrieNode root;

    public int findMaximumXOR(int[] nums) {
        TrieNode root = buildTrie(nums);
        int max = 0;
        for (int num : nums) {
            TrieNode curr = root;
            int xor = 0;
            for (int i = 31; i >= 0; i--) {
                int bit = (num >> i) & 1;
                if (curr.children[1 - bit] == null) {
                    xor = xor << 1;
                    curr = curr.children[bit];
                } else {
                    xor = (xor << 1) + 1;
                    curr = curr.children[1 - bit];
                }
            }
            max = Math.max(max, xor);
        }
        return max;
    }

    private TrieNode buildTrie(int[] nums) {
        TrieNode root = new TrieNode();
        for (int num : nums) {
            TrieNode curr = root;
            for (int i = 31; i >= 0; i--) {
                int bit = (num >> i) & 1;
                if (curr.children[bit] == null) {
                    curr.children[bit] = new TrieNode();
                }
                curr = curr.children[bit];
            }
        }
        return root;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值