题目:
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.
思路:
1、暴力法:暴力法很显然,可以直接在O(n^2)的时间内进行两两计算,并且维持一个最大值。代码就不用说了,但是这个时间复杂度肯定过不了大数据。
2、递归法:对于输入的数组,我们将其中的元素根据最高位的值分为两类,最高位为0的为一类,最高位为1的为一类。如果两类都不为空,那么该问题的最终结果一定是从第一类中找一个数和从第二类中找一个数进行异或得到的结果。
那么此题就可以优化为:从两个数列中各取一个数,求两个数按位异或结果的最大值。那么我们可以根据次高位将数继续分成两类,此时我们有了四个类:arr00, arr01, arr10和arr11。那么最终的解一定在(arr00, arr11)和(arr10和arr01)中。如果存在空集,那么最终的解一定在(arr00, arr01),(arr11, arr10)中。
假设O(n)表示辅助函数helper的时间复杂度,其中n表示传入helper两个数列的总长度,那么我们有O(n) = n + 2 * O(n / 2),根据master定理,其时间复杂度是O(nlogn)。
3、前缀树法:其思路是利用数组中每个元素二进制的表示形式建一棵前缀树(注意根据题目输入的限制条件,该树的最大深度是33)。然后再对数组中的每一个元素,在前缀树中去搜索最大的异或值。在每次异或的时候,我们都沿着异或值最大的分支往下走,所以helper函数的时间复杂度是O(33) = O(1)。因此整个算法的时间复杂度就是O(33n) = O(n),符合follow up中对时间复杂度的要求。
代码:
1、暴力法:
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
if (nums.size() < 2) {
return 0;
}
int max_xor = INT_MIN, value = INT_MIN;
for (int i = 0; i < nums.size(); ++i) {
for (int j = i + 1; j < nums.size(); ++j) {
value = nums[i] ^ nums[j];
max_xor = max(max_xor, value);
}
}
return max_xor;
}
};
2、递归法:
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
int n = nums.size();
if (n == 0 || n == 1) {
return 0;
}
if (n == 2) {
return nums[0] ^ nums[1];
}
list<int> set0, set1;
int i, j;
int maxValue;
for (i = 30; i >= 0; --i) {
for (j = 0; j < n; j++) {
if ((nums[j] & (1 << i)) == 0) {
set0.push_back(nums[j]);
}
else {
set1.push_back(nums[j]);
}
}
if (set0.size() != 0 && set1.size() != 0) { // both set are not NULL
maxValue = pow(2, i);
break;
}
else {
set0.clear();
set1.clear();
}
}
if (i == -1) {
return 0;
}
maxValue += helper(set0, set1, i - 1);
return maxValue;
}
private:
int helper(list<int>& set0, list<int>& set1, int pos) {
int maxValue;
list<int> arr00, arr01, arr10, arr11;
int i;
list<int>::iterator it;
if (set0.size() == 0 || set1.size() == 0 || pos < 0) {
return 0;
}
for (it = set0.begin(); it != set0.end(); ++it) {
int value = *it;
if ((value & (1 << pos)) == 0) {
arr00.push_back(value);
}
else {
arr01.push_back(value);
}
}
for (it = set1.begin(); it != set1.end(); ++it) {
int value = *it;
if ((value & (1 << pos)) == 0) {
arr10.push_back(value);
}
else {
arr11.push_back(value);
}
}
if (arr00.size() == 0 && arr10.size() == 0) {
maxValue = helper(set0, set1, pos - 1);
}
else if (arr01.size() == 0 && arr11.size() == 0)
maxValue = helper(set0, set1, pos - 1);
else {
int maxValue1 = helper(arr00, arr11, pos - 1);
int maxValue2 = helper(arr01, arr10, pos - 1);
maxValue = pow(2, pos) + (maxValue1 > maxValue2 ? maxValue1 : maxValue2);
}
return maxValue;
}
};
3、前缀树法:
class Solution {
public:
int findMaximumXOR(vector<int>& nums) {
int res = 0;
TreeNode* root = buildTree(nums);
for (int i = 0; i < nums.size(); ++i) {
res = max(res, helper(root, nums[i]));
}
return res;
}
private:
class TreeNode {
public:
TreeNode* next[2];
TreeNode () {
next[0] = NULL;
next[1] = NULL;
};
};
TreeNode* buildTree(vector<int>& nums) {
TreeNode* root = new TreeNode(), *cur;
int n = nums.size();
for (int i = 0; i < n; i++) {
int num = nums[i];
cur = root;
for (int j = 31; j >= 0; j--) {
int index = ((num >> j) & 1);
if (cur->next[index] == NULL) {
cur->next[index] = new TreeNode();
}
cur = cur->next[index];
}
}
return root;
}
int helper(TreeNode* cur, int num) {
int res = 0;
for (int i = 31; i >= 0; --i) {
int index = ((num >> i) & 1) ? 0 : 1;
if (cur->next[index]) {
res <<= 1;
res |= 1;
cur = cur->next[index];
} else {
res <<= 1;
res |= 0;
cur = cur->next[index ? 0 : 1];
}
}
return res;
}
};