Ani*_*rya 44 arrays algorithm bit-manipulation xor
给定一个整数数组,您必须找到两个XOR最大的元素.
有天真的方法 - 只需挑选每个元素和xoring与其他元素,然后比较结果找到对.
除此之外,有没有有效的算法?
tem*_*def 42
我想我有一个O(n lg U)算法,其中U是最大的数字.这个想法类似于user949300,但有更多细节.
直觉如下.当您将两个数字一起进行异或运算时,要获得最大值,您希望在最高位置获得1,然后在此位置具有1的配对,您希望在下一个位置配对1可能的最高位置等
所以算法如下.首先在数字中的任何地方找到最高的1位(你可以在时间O(n lg U)中通过对每个n个数字执行O(lg U)工作来执行此操作).现在,将数组拆分为两部分 - 其中一个数字在该位中为1,而在该位中为0.任何最佳解决方案必须将数字与第一个点中的1和该点中带有0的数字组合,因为这将使1位尽可能高.任何其他配对都有0.
现在,递归地,我们想要找到1和0组中具有最高1的数字的配对.为此,在这两组中,将它们分成四组:
如果11和00组或10和01组中有任何数字,则它们的XOR将是理想的(从11开始).因此,如果这些组中的任何一组不为空,则递归地从这些组计算理想解,然后返回那些子问题解的最大值.否则,如果两个组都为空,则表示所有数字在其第二个位置必须具有相同的数字.因此,以1开头的数字和从0开始的数字的最佳XOR将最终使下一个第二位取消,所以我们应该只看第三位.
这给出了以下递归算法,从它们的MSB划分的两组数字开始,给出了答案:
要启动算法,您实际上只需将初始组中的数字划分为两组 - 具有MSB 1的数字和具有MSB 0的数字.然后使用两组数字激活对上述算法的递归调用.
例如,考虑数字5 1 4 3 0 2.这些都有表示
101 001 100 011 000 010
Run Code Online (Sandbox Code Playgroud)
我们首先将它们分成1组和0组:
101 100
001 011 000 010
Run Code Online (Sandbox Code Playgroud)
现在,我们应用上述算法.我们将其分为11,10,01和00组:
11:
10: 101 100
01: 011 010
00: 000 001
Run Code Online (Sandbox Code Playgroud)
现在,我们不能将任何11个元素与00元素配对,所以我们只对10和01组进行递归.这意味着我们构建了100,101,010和011组:
101: 101
100: 100
011: 011
010: 010
Run Code Online (Sandbox Code Playgroud)
现在我们只关注其中只有一个元素的桶,我们可以检查对101和010(给出111)和100和011(给出111).这两个选项都适用于此,因此我们得出最佳答案为7.
让我们考虑一下这个算法的运行时间.请注意,最大递归深度为O(lg U),因为数字中只有O(log U)位.在树中的每个级别,每个数字恰好出现在一个递归调用中,并且每个递归调用确实与0和1组中的数字总数成比例,因为我们需要按它们的位分配它们.因此,递归树中有O(log U)级别,并且每个级别都执行O(n)工作,总工作量为O(n log U).
希望这可以帮助!这是一个很棒的问题!
这可以O(NlogN)
使用Trie在时间复杂度上解决。
arr[i]
元素arr[0, 1, ..... N]
arr[i]
。我们知道不同类型位(0 ^ 1
或1 ^ 0
)的异或总是1
。所以在查询每一位时,尽量遍历持有相反位的节点。这将使该特定位1
导致 xor 值最大化。如果没有相反位的节点,则只遍历同位节点。arr[i]
到trie中。对于N
元素,我们需要为每个元素一个 query( O(logN)
) 和一个 insert( O(logN)
)。所以总的时间复杂度是O(NlogN)
。
您可以在此线程中找到有关它如何工作的精美图片说明。
这是上述算法的 C++ 实现:
const static int SIZE = 2;
const static int MSB = 30;
class trie {
private:
struct trieNode {
trieNode* children[SIZE];
trieNode() {
for(int i = 0; i < SIZE; ++i) {
children[i] = nullptr;
}
}
~trieNode() {
for(int i = 0; i < SIZE; ++i) {
delete children[i];
children[i] = nullptr;
}
}
};
trieNode* root;
public:
trie(): root(new trieNode()) {
}
~trie() {
delete root;
root = nullptr;
}
void insert(int key) {
trieNode* pCrawl = root;
for(int i = MSB; i >= 0; --i) {
bool bit = (bool)(key & (1 << i));
if(!pCrawl->children[bit]) {
pCrawl->children[bit] = new trieNode();
}
pCrawl = pCrawl->children[bit];
}
}
int query(int key, int& otherKey) {
int Xor = 0;
trieNode *pCrawl = root;
for(int i = MSB; i >= 0; --i) {
bool bit = (bool)(key & (1 << i));
if(pCrawl->children[!bit]) {
pCrawl = pCrawl->children[!bit];
Xor |= (1 << i);
if(!bit) {
otherKey |= (1 << i);
} else {
otherKey &= ~(1 << i);
}
} else {
if(bit) {
otherKey |= (1 << i);
} else {
otherKey &= ~(1 << i);
}
pCrawl = pCrawl->children[bit];
}
}
return Xor;
}
};
pair<int, int> findMaximumXorElements(vector<int>& arr) {
int n = arr.size();
int maxXor = 0;
pair<int, int> result;
if(n < 2) return result;
trie* Trie = new trie();
Trie->insert(0); // insert 0 initially otherwise first query won't find node to traverse
for(int i = 0; i < n; i++) {
int elem = 0;
int curr = Trie->query(arr[i], elem);
if(curr > maxXor) {
maxXor = curr;
result = {arr[i], elem};
}
Trie->insert(arr[i]);
}
delete Trie;
return result;
}
Run Code Online (Sandbox Code Playgroud)
忽略符号位,其中一个值必须是设置了最高有效位的值之一。 除非所有值都设置了该位,否则您将转到未在所有值中设置的下一个最高有效位。因此,您可以通过查看 HSB 来减少第一个值的可能性。例如,如果可能性是
0x100000
0x100ABC
0x001ABC
0x000ABC
Run Code Online (Sandbox Code Playgroud)
最大对的第一个值必须是 0x100000 或 0x10ABCD。
@内部服务器错误我认为最小的不一定正确。我没有减少第二个值的好主意。只是不在可能的第一个值列表中的任何值。在我的示例中,0x001ABC 或 0x000ABC。