原题链接:421.数组中两个数的最大异或值

题目描述:

image.png


看懂题解:

暴力的匹配没有什么好说的,题目中提到进阶为O(n),那么肯定需要在遍历到每个数 num[i] 的时候就找到和他异或值最大的数 num[j] ,并看是否能更新最大的异或值 x
对于每个数 num[i] ,我们选择都对他的二进制表示建一棵字典树,即下面代码中的 add 函数,当前位为0的向左建树,当前位为1的向右建树。
然后找出 num[i] 对应的最大异或值,即 check 函数:(执行前,字典树中已经包含 num[0 ~ i-1] ),取出 num[i] 的从高到低每一位,在字典树中看这一位是否有相反位存在,也就是尽可能让 x 当前位为1。
代码如下:

  1. class Solution {
  2. // 字典树的根节点
  3. Trie root = new Trie();
  4. // 数据范围2^31 最高位的二进制位编号为 30
  5. static final int HIGH_BIT = 30;
  6. public int findMaximumXOR(int[] nums) {
  7. int n = nums.length;
  8. int x = 0;
  9. for (int i = 1; i < n; ++i) {
  10. // 将 nums[i-1] 放入字典树,此时 nums[0 .. i-1] 都在字典树中
  11. add(nums[i - 1]);
  12. // 将 nums[i] 看作 ai,找出最大的 x 更新答案
  13. x = Math.max(x, check(nums[i]));
  14. }
  15. return x;
  16. }
  17. public void add(int num) {
  18. Trie cur = root;
  19. for (int k = HIGH_BIT; k >= 0; --k) {
  20. int bit = (num >> k) & 1;
  21. if (bit == 0) { //当前位为0则向左建树
  22. if (cur.left == null) {
  23. cur.left = new Trie();
  24. }
  25. cur = cur.left;
  26. }
  27. else { //为1则向右
  28. if (cur.right == null) {
  29. cur.right = new Trie();
  30. }
  31. cur = cur.right;
  32. }
  33. }
  34. }
  35. public int check(int num) {
  36. Trie cur = root;
  37. int x = 0; //直接计算出最大异或值
  38. for (int k = HIGH_BIT; k >= 0; --k) {
  39. int bit = (num >> k) & 1;
  40. if (bit == 0) {
  41. // 当前位为0,需要有数这一位是1,x的这一位才能为1
  42. // a_i 的第 k 个二进制位为 0,应当往表示 1 的子节点 right 走
  43. if (cur.right != null) {
  44. cur = cur.right;
  45. x = x * 2 + 1;
  46. } else { // 没有那也没办法
  47. cur = cur.left;
  48. x = x * 2;
  49. }
  50. } else {
  51. // a_i 的第 k 个二进制位为 1,应当往表示 0 的子节点 left 走
  52. if (cur.left != null) {
  53. cur = cur.left;
  54. x = x * 2 + 1;
  55. } else {
  56. cur = cur.right;
  57. x = x * 2;
  58. }
  59. }
  60. }
  61. return x;
  62. }
  63. }
  64. class Trie {
  65. // 左子树指向表示 0 的子节点
  66. Trie left = null;
  67. // 右子树指向表示 1 的子节点
  68. Trie right = null;
  69. }