题目
前缀树题目地址:https://leetcode.com/problems/implement-trie-prefix-tree/
题目地址:https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/
421. Maximum XOR of Two Numbers in an Array QuestionEditorial Solution My Submissions
Total Accepted: 854
Total Submissions: 3457
Difficulty: Medium
Contributors: shen5630
Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231.
Find the maximum result of ai XOR aj, where 0 ≤ i, j < n.
Could you do this in O(n) runtime?
Example:
Input: [3, 10, 5, 25, 2, 8]
Output: 28
Explanation: The maximum result is 5 ^ 25 = 28.
前缀树
前缀树的实现
普通的前缀树的实现,插入的一个字符串,如同百度搜索的那种感觉,体会一下。所以需要用一个Map来实现Prefix继续向下,当然也可以用26个字母的数组。其实也挺好的,只是比较占用内存。代码如下所示:
import java.util.HashMap;
class TrieNode {
boolean isWord;
HashMap<Character, TrieNode> children;
// Initialize your data structure here.
public TrieNode() {
children = new HashMap<>();
}
}
public class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
// Inserts a word into the trie.
public void insert(String word) {
char[] arrays = word.toCharArray();
TrieNode head = root;
for (int i = 0; i < arrays.length; i++) {
//没有则添加
if (!head.children.containsKey(arrays[i])) {
head.children.put(arrays[i], new TrieNode());
}
head = head.children.get(arrays[i]);//转到子节点
if (i == arrays.length - 1) {
head.isWord = true;
}
}
}
// Returns if the word is in the trie.
public boolean search(String word) {
// char[] arrays = word.toCharArray();
TrieNode head = root;
for (int i = 0; i < word.length(); i++) {
head = head.children.get(word.charAt(i));
if (head == null) return false;
}
return head.isWord;
}
// Returns if there is any word in the trie
// that starts with the given prefix.
public boolean startsWith(String prefix) {
// char[] arrays = prefix.toCharArray();
TrieNode head = root;
for (int i = 0; i < prefix.length(); i++) {
head = head.children.get(prefix.charAt(i));
if (head == null) return false;
}
return true;
}
}
// Your Trie object will be instantiated and called as such:
// Trie trie = new Trie();
// trie.insert("somestring");
// trie.search("key");
本题题解
- 假设有这样一个数组[5 1 4 3 0 2] 。那么它们的二进制表示为
101 001 100 011 000 010
- 要找到最大异或和,则尽量使两个数在同一bit位上差异巨大,所以用到分组的想法:最开始位为1的分为一组,为0的分为一组。这也是前缀树的思想。分组后如下所示
101 100
001 011 000 010
- 看起来还不错,但是依然无法看出来哪两个数异或的值是最大的,为此继续向下分组。这次按照 11,10,01,00分组:
11:
10: 101 100
01: 011 010
00: 000 001
101: 101
100: 100
011: 011
010: 010
101 ^ 010 =7
这种思想,加上前缀树,思考出这样的一种方式去求解问题:
将每个数加入前缀树中,每个节点只有0和1,两个方向。
对于某一个数value,找与其同等bit位的另一个分支,如果这样的分支存在,则将这个分支值加入到要求得异或数中,如果这样的分支不存在,将bit位上的值加入到异或数中。
例如 : value 的二进制表示为 :
0000 0000 1000 0010 0000 0110 0000 0101
在前缀树中能找到的与value异或最大的数xorValue可能是 :
0111 1111 0111 1101 1111 1001 1111 1010;
-由上一点得到的异或数为与value相异或的最大数xorValue了。然后
res = Math.max(res, xorValue ^ value);
Code
public class Solution {
/**
* 计算数组间两数异或的最大值
*
* @param nums
* @return
*/
public int findMaximumXOR(int[] nums) {
/*前缀树*/
int res = 0;
Node root = new Node();
for (int num : nums) {
buildTrie(root, num);
}
for (int value : nums) {
int xorValue = findMatchXor(root, value);
res = Math.max(res, xorValue ^ value);
}
return res;
}
/**
* 找到与num匹配的在前缀树中最大的数
*
* @param root
* @param num
* @return
*/
private int findMatchXor(Node root, int num) {
int res = 0;
Node head = root;
for (int i = 30; i >= 0; i--) {
int bit = num & (1 << i);
int flag = bit == 0 ? 0 : 1;
/*异或的最大值就是找到与num同等位相反的,这样才是最大值*/
/*如果不存在这样分支,则找与num同等位相同的*/
if (head.next[1 - flag] == null) {
head = head.next[flag];
/*加*/
res += flag << i;
} else {
head = head.next[1 - flag];
/*加*/
res += (1 - flag) << i;
}
}
return res;
}
/**
* 构建前缀树
*
* @param root
* @param x
*/
private void buildTrie(Node root, int x) {
/*建前缀树的过程,需要31位 不包括符号位*/
Node head = root;
for (int i = 30; i >= 0; i--) {
int bit = x & (1 << i);/*判断第 i bit位*/
int flag = bit == 0 ? 0 : 1;
/*前缀树的基本构建过程,如果没有则添加,有的继续向下*/
if (head.next[flag] == null) {
head.next[flag] = new Node();
}
head = head.next[flag];
}
}
private static class Node {
Node[] next;
public Node() {
/**0 和 1 */
next = new Node[2];
}
}
}