一、题目

给你一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。

进阶:你可以在 O(n) 的时间解决这个问题吗?

点击查看原题

二、思路

由于异或运算特性是0^1=1,求两两元素异或值最大,则尽可能结果高位都为1(两元素高位尽可能都一个为1另一个为0)。

1)暴力法

每个两个数都尝试一遍,使用两层for循环,找出最大值,时间复杂度为O(n).

2)字典树

对暴力法进行优化,即减少一层for循环。将每个元素的二进制看作字符串,先使用字典树对所有的元素进行记忆化(从二进制高位作为根节点进行记忆,保证每个不同的元素存在于不同的叶子节点)。然后遍历一遍,找出两两异或值最大的结果。

3)哈希表

这个和上面的两个方法不太一样,使用一个Set类型进行记录最高位到第i位的值,由于需要尽可能让高位的异或结果为1,使用一个变量ans进行记录最高位到第i位的最大异或值,然后不断进行查询。(很抽象,手推代码自己体会)

三、代码

字典树

  1. class Solution {
  2. public int findMaximumXOR(int[] nums) {
  3. Trie root = new Trie();
  4. for (int num : nums) { // 先存入字典树
  5. insert(root, num);
  6. }
  7. int ans = 0;
  8. for (int num : nums) { // 对每个元素都找能匹配的最大
  9. ans = Math.max(ans, check(root, num));
  10. }
  11. return ans;
  12. }
  13. // 插入字典树
  14. public void insert(Trie root, int num) {
  15. for (int i = 30; i >= 0; i--) {
  16. int bitValue = (num >> i) & 1;
  17. if (bitValue == 1) {
  18. if (root.right == null) {
  19. root.right = new Trie();
  20. }
  21. root = root.right;
  22. } else {
  23. if (root.left == null) {
  24. root.left = new Trie();
  25. }
  26. root = root.left;
  27. }
  28. }
  29. }
  30. // 找到与num值异或最大的结果
  31. public int check(Trie root, int num) {
  32. int ans = 0;
  33. for (int i = 30; i >= 0; i--) {
  34. int numBitValue = (num >> i) & 1;
  35. if (numBitValue == 1) {
  36. // 当num的第i+1位为1时,尽可能找同样位为0的数
  37. if (root.left != null) {
  38. root = root.left;
  39. ans = (ans << 1) + 1;
  40. } else { // 找不到i+1位为0的数时,就往另一分支继续查询
  41. root = root.right;
  42. ans = (ans << 1);
  43. }
  44. } else {
  45. if (root.right != null) {
  46. root = root.right;
  47. ans = (ans << 1) + 1;
  48. } else {
  49. root = root.left;
  50. ans = (ans << 1);
  51. }
  52. }
  53. }
  54. return ans;
  55. }
  56. }
  57. class Trie {
  58. public Trie left; //0
  59. public Trie right; //1
  60. }

时间复杂度为O(nlogC),其中C为元素的二进制位数,空间复杂度位O(nlogC)。

哈希表

  1. class Solution {
  2. public int findMaximumXOR(int[] nums) {
  3. int ans = 0;
  4. for (int i = 30; i >= 0; i--) {
  5. Set<Integer> memo = new HashSet(); // 每循环一次初始化一次
  6. for (int num : nums) {
  7. memo.add(num >> i); // 记录每个元素最高位到i + 1位
  8. }
  9. int ansNext = (ans << 1) + 1; // 假设i+1位能异或出1
  10. boolean findFlag = false;
  11. for (int num : nums) {
  12. // 由于ansNext是由两个元素异或而成的,假设两个元素之一是num高位部分,那么ansNext ^ (num >> i)就是另一个元素的高位部分
  13. if (memo.contains(ansNext ^ (num >> i))) {
  14. findFlag = true;
  15. break;
  16. }
  17. }
  18. // 上面这个循环找到元素就假设成立,找不到就假设失败
  19. if (findFlag) {
  20. ans = ansNext;
  21. } else {
  22. ans = ansNext - 1;
  23. }
  24. }
  25. return ans;
  26. }
  27. }

时间复杂度为O(nlogC),其中C为元素的二进制位数,空间复杂度位O(n)。