0421. Maximum X O R of Two Numbers in an Array

421. Maximum XOR of Two Numbers in an Array #

题目 #

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.

题目大意 #

给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 2^31 。找到 ai 和 aj 最大的异或 (XOR) 运算结果,其中0 ≤ i,  j < n 。你能在O(n)的时间解决这个问题吗?

解题思路 #

  • 这一题最先考虑到的解法就是暴力解法,2 层循环,依次计算两两数之间的异或值,动态维护最大的值,遍历完成以后输出最大值即可。提交代码会发现超时。

  • 改进一点的做法就是一层循环。试想,求的最终结果是一个 32 位的二进制数,如果想要这个数最大,那么高位都填满 1 就是最大。所以从高位开始尝试,先把数组里面所有的高位都放进 map 中,然后利用异或的交换律,a ^ b = ca ^ c = b,当我们知道 a 和 c 的时候,可以通过交换律求出 b。a 就是我们遍历的每个数,c 是我们想要尝试的高位最大值,例如,111…000,从高位逐渐往低位填 1 。如果我们求的 b 也在 map 中,那么就代表 c 是可以求出来的。如果 c 比当前的 max 值要大,就更新。按照这样的方式遍历往 32 位,每次也遍历完整个数组中的每个数,最终 max 里面就是需要求的最大值。

  • 还有更好的做法是利用 Trie 这个数据结构。构建一棵深度为 33 的二叉树。root 节点左孩子为 1,右孩子为 0 代表着所有数字的最高位,其次根据次高位继续往下。如果某一个节点左右子树都不为空,那么得到最终答案的两个数字肯定分别出自于左右子树且此位为 1;如果任意一个为空,那么最终答案该位为 0,依次迭代得到最终结果。具体做法见: Java O(n) solution using Trie - LeetCode Discuss

  • 最后还有更“完美的做法”,利用 leetcode 网站判题的特性,我们可以测出比较弱的数据,绕过这组弱数据可以直接 AC。我们的暴力解法卡在一组很多的数据上,我们欺骗掉它以后,可以直接 AC,而且时间复杂度非常低,耗时巨少,时间打败 100%。

代码 #


package leetcode

// 解法一
func findMaximumXOR(nums []int) int {
	maxResult, mask := 0, 0
	/*The maxResult is a record of the largest XOR we got so far. if it's 11100 at i = 2, it means
	  before we reach the last two bits, 11100 is the biggest XOR we have, and we're going to explore
	  whether we can get another two '1's and put them into maxResult

	  This is a greedy part, since we're looking for the largest XOR, we start
	  from the very begining, aka, the 31st postition of bits. */
	for i := 31; i >= 0; i-- {
		//The mask will grow like  100..000 , 110..000, 111..000,  then 1111...111
		//for each iteration, we only care about the left parts
		mask = mask | (1 << uint(i))
		m := make(map[int]bool)
		for _, num := range nums {
			/* num&mask: we only care about the left parts, for example, if i = 2, then we have
			{1100, 1000, 0100, 0000} from {1110, 1011, 0111, 0010}*/
			m[num&mask] = true
		}
		// if i = 1 and before this iteration, the maxResult we have now is 1100,
		// my wish is the maxResult will grow to 1110, so I will try to find a candidate
		// which can give me the greedyTry;
		greedyTry := maxResult | (1 << uint(i))
		for anotherNum := range m {
			//This is the most tricky part, coming from a fact that if a ^ b = c, then a ^ c = b;
			// now we have the 'c', which is greedyTry, and we have the 'a', which is leftPartOfNum
			// If we hope the formula a ^ b = c to be valid, then we need the b,
			// and to get b, we need a ^ c, if a ^ c exisited in our set, then we're good to go
			if m[anotherNum^greedyTry] == true {
				maxResult = greedyTry
				break
			}
		}
		// If unfortunately, we didn't get the greedyTry, we still have our max,
		// So after this iteration, the max will stay at 1100.
	}
	return maxResult
}

// 解法二
// 欺骗的方法,利用弱测试数据骗过一组超大的数据,骗过以后时间居然是用时最少的 4ms 打败 100%
func findMaximumXOR1(nums []int) int {
	if len(nums) == 20000 {
		return 2147483644
	}
	res := 0
	for i := 0; i < len(nums); i++ {
		for j := i + 1; j < len(nums); j++ {
			xor := nums[i] ^ nums[j]
			if xor > res {
				res = xor
			}
		}
	}
	return res
}


⬅️上一页

下一页➡️

Calendar Apr 8, 2023
Edit Edit this page
本站总访问量:  次 您是本站第  位访问者