数组中异或最大的两个元素

发布于 2025-01-06 00:17:37 字数 107 浏览 0 评论 0原文

给定一个整数数组,您必须找到异或最大的两个元素。

有一种简单的方法——只需选择每个元素并与其他元素进行异或,然后比较结果即可找到该对。

除此之外,还有什么高效的算法吗?

Given an array of integers ,You have to find two elements whose XOR is maximum.

There is naive approach --just by picking each element and xoring with other elements and then comparing the results to find the pair.

Other than this ,Is there any efficient algorithm?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

感性 2025-01-13 00:17:38

这可以使用 TrieO(NlogN) 时间复杂度解决。

  • 构造一个特里树。对于每个整数键,trie 的每个节点将保存从最高有效位开始的每一位(0 或 1)。
  • 现在对于 arr[0, 1, ..... N] 的每个 arr[i] 元素
    • 执行查询以检索 arr[i] 可能的最大异或值。我们知道不同类型的位(0 ^ 11 ^ 0)的异或总是1。因此,在查询每个位时,尝试遍历持有相反位的节点。这将使特定位 1 导致异或值最大化。如果不存在位相反的节点,才遍历同位节点。
    • 查询后,将arr[i]插入trie中。
    • 对于每个元素,跟踪可能的最大异或值。
    • 在遍历每个节点的过程中,构建要最大化异或的另一个密钥。

对于 N 元素,我们需要为每个元素进行一次查询 (O(logN)) 和一次插入 (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;
}

This can be solved in O(NlogN) time complexity using Trie.

  • Construct a trie. For each integer key, each node of the trie will hold every bit(0 or 1) starting from most significant bit.
  • Now for each arr[i] element of arr[0, 1, ..... N]
    • Perform query to retrieve the maximum xor value possible for arr[i]. We know xor of different type of bits(0 ^ 1 or 1 ^ 0) is always 1. So during query for each bit, try to traverse node holding opposite bit. This will make that particular bit 1 result in maximizing xor value. If there is no node with opposite bit, only then traverse the same bit node.
    • After query, insert arr[i] into trie.
    • For each element, keep track the maximum Xor value possible.
    • During walking through each node, build the other key for which the Xor is being maximized.

For N elements, we need one query(O(logN)) and one insertion(O(logN)) for each element. So the overall time complexity is O(NlogN).

You can find nice pictorial explanation on how it works in this thread.

Here is C++ implementation of the above algorithm:

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;
}
破晓 2025-01-13 00:17:38

忽略符号位,其中一个值必须是具有最高有效位设置的值之一。 除非所有值都设置了该位,在这种情况下,您将转到未在所有值中设置的下一个最高有效位。因此,您可以通过查看 HSB 来减少第一个值的可能性。例如,如果可能性是

0x100000
0x100ABC
0x001ABC
0x000ABC

最大对的第一个值必须是 0x100000 或 0x10ABCD。

@内部服务器错误我认为最小不一定是正确的。我没有减少第二个值的好主意。只是可能的第一个值列表中的任何值。在我的示例中,为 0x001ABC 或 0x000ABC。

Ignoring the sign bit, one of the values must be one of the values with the highest significant bit set. Unless all the values have that bit set, in which case you go to the next highest significant bit that isn't set in all the values. So you could pare down the possibilities for the 1st value by looking at the HSB. For example, if the possibilities are

0x100000
0x100ABC
0x001ABC
0x000ABC

The 1st value of the max pair must be either 0x100000 or 0x10ABCD.

@internal Server Error I don't think smallest is necessarily correct. I don't have a great idea for paring down the 2nd value. Just any value that isn't in the list of possible 1st values. In my example, 0x001ABC or 0x000ABC.

美人如玉 2025-01-13 00:17:38

一个非常有趣的问题!
这是我的想法:

  • 首先使用二叉树从所有数字构建一棵二叉树
    表示并首先将它们排序到树中最高有效位
    (添加前导零以匹配最长的数字)。完成每条路径后
    从根到任意叶子都代表原来的一个数字
    放。
  • 设 a 和 b 为指向树节点的指针,并在根处初始化它们。
  • 现在将 a 和 b 沿树向下移动,尝试在每一步使用相反的边,即,如果 a 向下移动 0 边,则 b 向下移动 1 边,除非不可能。

如果 a 和 b 到达叶子,则 应该指向具有“很少”相同位的两个数字。

我刚刚提出这个算法,不知道它是否正确或如何证明它。然而它应该在 O(n) 运行时间内。

A very interesting problem!
Here is my idea:

  • First build a binary tree from all the numbers by using the binary
    representation and sort them into the tree most significant bit first
    (add leading zeros to match the longest number). When done each path
    from the root to any leaf represents one number from the original
    set.
  • Let a and b be pointers to a tree node and initialize them at the root.
  • Now move a and b down the tree, trying to use opposite edges at each step, i.e. if a moves down a 0-edge, b moves down a 1-edge unless its not possible.

If a and b reach a leaf, the should point to two numbers with "very few" identical bits.

I just made this algorithm up and do not know if its correct or how to prove it. However it should be in O(n) running time.

深白境迁sunset 2025-01-13 00:17:38

创建一个递归函数,将两个整数列表 A 和 B 作为参数。作为其返回值,它返回两个整数,一个来自 A,一个来自 B,这使得两者的 XOR 最大化。如果所有整数均为 0,则返回 (0,0)。否则,该函数会进行一些处理并递归调用自身两次,但使用较小的整数。在其中一个递归调用中,它考虑从列表 A 中取出一个整数,为位 k 提供 1,而在另一个调用中,它考虑从列表 B 中取出一个整数,为位 k 提供 1。

我现在没有时间填写详细信息,但也许这足以看到答案?另外,我不确定运行时间是否会比 N^2 更好,但可能会。

Make a recursive function that takes two lists of integers, A and B, as its arguments. As its return value, it returns two integers, one from A and one from B, which maximize the XOR of the two. If all the integers are 0, return (0,0). Otherwise, the function does some processing and calls itself recursively twice, but with smaller integers. In one of the recursive calls, it considers taking an integer from list A to supply a 1 to bit k, and in the other call it considers taking an integer from list B to supply a 1 to bit k.

I don't have time now to fill in the details, but maybe this will be enough for to see the answer? Also, I'm not sure if the run time will be better than N^2, but it probably will be.

少年亿悲伤 2025-01-13 00:17:38

我们可以在 O(n) 时间内找到最大数,然后循环遍历数组,对每个元素进行异或。假设异或运算成本为 O(1),我们可以在 O(n) 时间内找到两个数字的最大异或。

We can find the maximum number in O(n) time then loop through the array doing xor with each element. Assuming xor operation cost is O(1) we can find max xor of two numbers in O(n) time.

剑心龙吟 2025-01-13 00:17:37

我想我有一个 O(n lg U) 算法,其中 U 是最大的数字。这个想法与user949300类似,但有更多细节。

直觉如下。当您将两个数字异或在一起时,为了获得最大值,您希望在尽可能最高的位置有一个 1,然后是具有 1 的配对在这个位置,您希望与下一个可能的最高位置处的 1 进行配对,依此类推。

因此算法如下。首先找到数字中任意位置的最高 1 位(您可以通过执行 O(lg U)O(n lg U) 时间内完成此操作code> 对每个 n 数字起作用)。现在,将数组分成两部分 - 一个是该位中具有 1 的数字,另一个是该位中具有 0 的数字。任何最佳解决方案都必须将第一个位置带有 1 的数字与该位置带有 0 的数字组合起来,因为这样会出现 1 > 尽可能高一点。任何其他配对都有 0

现在,我们希望递归地从 10 组中找到 1 最高的数字配对。为此,请将这两组中的它们分为四组:

  • 11 开头的数字
  • 10
  • 开头的数字 以 01
  • 开头的数字与 00

如果 1100 组或 1001 中有任何数字 组,他们的异或将是理想的(以 11 开头)。因此,如果这些组对中的任何一个不为空,则递归地计算这些组中的理想解决方案,然后返回这些子问题解决方案中的最大值。否则,如果两组都是空的,则意味着所有数字的第二个位置必须具有相同的数字。因此,以 1 开头的数字和以 0 开头的数字的最佳 XOR 最终会抵消下一个第二位,因此我们应该只看第三个位少量。

这给出了以下递归算法,该算法从按 MSB 划分的两组数字开始给出答案:

  • 给定组 1 和组 0 以及位索引 我:
    • 如果位索引等于位数,则返回 1 组中的(唯一)数字与 0 组中的(唯一)数字的 XOR > 小组。
    • 从这些组中构造组 11100100
    • 如果组11和组00非空,则从位i + 1开始递归查找这两个组的最大异或。< /里>
    • 如果组10和组01非空,则从位i + 1开始递归查找这两个组的最大异或。
    • 如果上述任一配对可行,则返回递归找到的最大配对。
    • 否则,所有数字在位置 i 中必须具有相同的位,因此返回通过查看组 上的位 i + 1 找到的最大对10

要开始算法,您实际上可以将初始组中的数字分为两组 - MSB 1 的数字和 MSB 0 的数字。然后,您使用两组数字对上述算法进行递归调用。

例如,考虑数字 5 1 4 3 0 2。这些具有表示形式,

101  001  100   011   000   010

我们首先将它们分为 1 组和 0 组:

101  100
001  011  000  010

现在,我们应用上述算法。我们将其分为 11100100 组:

11:
10:  101  100
01:  011  010
00:  000  001

现在,我们无法将任何组配对11 元素与 00 元素,因此我们只需递归 1001 组。这意味着我们构建 100101010011 组:

101: 101
100: 100
011: 011
010: 010

现在我们要对于只有一个元素的存储桶,我们只需检查 101010 (给出 111)和 100 对代码> 和011(给出111)。任一选项在这里都有效,因此我们得到最佳答案是7

让我们考虑一下这个算法的运行时间。请注意,最大递归深度为 O(lg U),因为数字中只有 O(log U) 位。在树的每一层,每个数字都出现在一次递归调用中,并且每个递归调用的工作量与 01 中的数字总数成正比。组,因为我们需要按位分配它们。因此,递归树中有 O(log U) 层,每个层的工作量为 O(n) ,总工作量为 O(n记录U)

希望这有帮助!这是一个很棒的问题!

I think I have a O(n lg U) algorithm for this, where U is the largest number. The idea is similar to user949300's, but with a bit more detail.

The intuition is as follows. When you're XORing two numbers together, to get the maximum value, you want to have a 1 at the highest possible position, and then of the pairings that have a 1 at this position, you want a pairing with a 1 at the next possible highest position, etc.

So the algorithm is as follows. Begin by finding the highest 1 bit anywhere in the numbers (you can do this in time O(n lg U) by doing O(lg U) work per each of the n numbers). Now, split the array into two pieces - one of the numbers that have a 1 in that bit and the group with 0 in that bit. Any optimal solution must combine a number with a 1 in the first spot with a number with a 0 in that spot, since that would put a 1 bit as high as possible. Any other pairing has a 0 there.

Now, recursively, we want to find the pairing of numbers from the 1 and 0 group that has the highest 1 in them. To do this, of these two groups, split them into four groups:

  • Numbers starting with 11
  • Numbers starting with 10
  • Numbers starting with 01
  • Numbers starting with 00

If there are any numbers in the 11 and 00 group or in the 10 and 01 groups, their XOR would be ideal (starting with 11). Consequently, if either of those pairs of groups isn't empty, recursively compute the ideal solution from those groups, then return the maximum of those subproblem solutions. Otherwise, if both groups are empty, this means that all the numbers must have the same digit in their second position. Consequently, the optimal XOR of a number starting with 1 and a number starting with 0 will end up having the next second bit cancel out, so we should just look at the third bit.

This gives the following recursive algorithm that, starting with the two groups of numbers partitioned by their MSB, gives the answer:

  • Given group 1 and group 0 and a bit index i:
    • If the bit index is equal to the number of bits, return the XOR of the (unique) number in the 1 group and the (unique) number in the 0 group.
    • Construct groups 11, 10, 01, and 00 from those groups.
    • If group 11 and group 00 are nonempty, recursively find the maximum XOR of those two groups starting at bit i + 1.
    • If group 10 and group 01 are nonempty, recursively find the maximum XOR of those two groups, starting at bit i + 1.
    • If either of the above pairings was possible, then return the maximum pair found by the recursion.
    • Otherwise, all of the numbers must have the same bit in position i, so return the maximum pair found by looking at bit i + 1 on groups 1 and 0.

To start off the algorithm, you can actually just partition the numbers from the initial group into two groups - numbers with MSB 1 and numbers with MSB 0. You then fire off a recursive call to the above algorithm with the two groups of numbers.

As an example, consider the numbers 5 1 4 3 0 2. These have representations

101  001  100   011   000   010

We begin by splitting them into the 1 group and the 0 group:

101  100
001  011  000  010

Now, we apply the above algorithm. We split this into groups 11, 10, 01, and 00:

11:
10:  101  100
01:  011  010
00:  000  001

Now, we can't pair any 11 elements with 00 elements, so we just recurse on the 10 and 01 groups. This means we construct the 100, 101, 010, and 011 groups:

101: 101
100: 100
011: 011
010: 010

Now that we're down to buckets with just one element in them, we can just check the pairs 101 and 010 (which gives 111) and 100 and 011 (which gives 111). Either option works here, so we get that the optimal answer is 7.

Let's think about the running time of this algorithm. Notice that the maximum recursion depth is O(lg U), since there are only O(log U) bits in the numbers. At each level in the tree, each number appears in exactly one recursive call, and each of the recursive calls does work proportional to the total number of numbers in the 0 and 1 groups, because we need to distribute them by their bits. Consequently, there are O(log U) levels in the recursion tree, and each level does O(n) work, giving a total work of O(n log U).

Hope this helps! This was an awesome problem!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文