Два элемента в массиве, чье значение является максимальной
учитывая массив целых чисел, вы должны найти два элемента, XOR которых является максимальным.
существует наивный подход-просто выбирая каждый элемент и xoring с другими элементами, а затем сравнивая результаты, чтобы найти пару.
кроме этого ,есть ли эффективный алгоритм?
6 ответов
Я думаю, что у меня есть алгоритм O(N lg U) для этого, где U-наибольшее число. Идея похожа на user949300, но с немного более подробно.
интуиция заключается в следующем. Когда вы фиксируете два числа вместе, чтобы получить максимальное значение, вы хотите иметь 1 в максимально возможной позиции, а затем из пар, которые имеют 1 в этой позиции, вы хотите сопряжение с 1 в следующей возможной наивысшей позиции и т. д.
такой алгоритм следует. Начните с поиска самого высокого 1 бита в любом месте чисел(вы можете сделать это за время O(N lg U), выполнив работу O (lg U) для каждого из n чисел). Теперь разделите массив на две части - одно из чисел, имеющих 1 в этом бите, и группу с 0 в этом бите. Любое оптимальное решение должно сочетать число с 1 в первом месте с числом с 0 в этом месте, так как это поставило бы 1 бит как можно выше. Любое другое сопряжение имеет 0.
теперь, рекурсивно мы хотим найти спаривание чисел из группы 1 и 0, которая имеет наибольшее 1 в них. Для этого из этих двух групп разделите их на четыре группы:--5-->
- числа, начинающиеся с 11
- числа, начиная с 10
- номера, начинающиеся с 01
- номера, начинающиеся с 00
если есть какие-либо числа в группе 11 и 00 или в группах 10 и 01, их XOR будет идеальным (начиная с 11). Следовательно, если любая из этих пар групп не пуста, рекурсивно вычислите идеальное решение из этих групп, а затем верните максимум этих решений подзадачи. В противном случае, если обе группы пусты, это означает, что все числа должны иметь одну и ту же цифру во второй позиции. Следовательно, оптимальный XOR числа, начинающегося с 1, и числа, начинающегося с 0, в конечном итоге будет иметь следующий второй бит, поэтому мы должны просто посмотреть на третий немного.
это дает следующий рекурсивный алгоритм, который, начиная с двух групп чисел, разбитых на их MSB, дает ответ:
- данная группа 1 и группа 0 и битовый индекс i:
- если битовый индекс равен количеству битов, верните XOR (уникального) числа в группе 1 и (уникального) числа в группе 0.
- строительство группы 11, 10, 01 и 00 из этих групп.
- если группа 11 и группа 00 непустая, рекурсивно найти максимальный XOR этих двух групп, начиная с бита i + 1.
- если группа 10 и группа 01 непусты, рекурсивно найдите максимальный XOR этих двух групп, начиная с бита i + 1.
- если любая из вышеуказанных пар была возможна, то верните максимальную пару, найденную рекурсией.
- в противном случае все числа должны иметь один и тот же бит в позиции i, поэтому верните максимальную пару, найденную, посмотрев бит i + 1 на групп 1 и 0.
чтобы начать алгоритм, вы можете просто разделить числа из начальной группы на две группы - числа с MSB 1 и числа с MSB 0. Затем вы запускаете рекурсивный вызов вышеуказанного алгоритма с двумя группами чисел.
в качестве примера рассмотрим цифры 5 1 4 3 0 2. У них есть представления
101 001 100 011 000 010
мы начнем с разделения их на 1 группу и 0 группа:
101 100
001 011 000 010
теперь мы применяем этот алгоритм. Мы разбить на группы 11, 10, 01, и 00:
11:
10: 101 100
01: 011 010
00: 000 001
теперь мы не можем пару каких 11 элементов с 00 элементы, поэтому мы просто рекурсия на 10 и 01 групп. Это означает, что мы строим группы 100, 101, 010 и 011:
101: 101
100: 100
011: 011
010: 010
теперь, когда мы дошли до ведер с одним элементом в них, мы можем просто проверить пары 101 и 010 (что дает 111) и 100 и 011 (что дает 111). Любой вариант работает здесь, поэтому мы получаем, что оптимальный ответ-7.
давайте подумаем о времени работы данного алгоритма. Обратите внимание, что максимальная глубина рекурсии O(lg U), так как в числах есть только o(log U) бит. На каждом уровне дерева каждое число появляется ровно в одном рекурсивном вызове, и каждый из рекурсивных вызовов работает пропорционально общему числу чисел в группах 0 и 1, потому что нам нужно распределить их по битам. Следовательно, есть O (log U) уровни в дереве рекурсии, и каждый уровень выполняет O(n) работу, давая общую работу O (N log U).
надеюсь, что это помогает! Это была потрясающая проблема!
игнорируя бит знака, одно из значений должно быть одним из значений с наибольшим значащий бит. Если все значения не имеют этот бит set в этом случае вы переходите на следующий высокий, что не во всех значениях. Таким образом, вы можете сократить возможности для 1-го значения, посмотрев на HSB. Например, если возможности
0x100000
0x100ABC
0x001ABC
0x000ABC
1-е значение максимальной пары должно быть 0x100000 или 0x10ABCD.
@внутренняя ошибка сервера я не думаю, что маленький верна. У меня нет отличной идеи для сокращения 2-го значения. Просто любое значение не в списке возможных значений 1st. В моем примере 0x001ABC или 0x000ABC.
это можно решить в O(NlogN)
сложность времени с помощью Trie.
- построить trie. Для каждого целочисленного ключа каждый узел trie будет содержать каждый бит (0 или 1), начиная с самого значительного бита.
- теперь для каждого
arr[i]
элементarr[0, 1, ..... N]
- выполнить запрос, чтобы получить максимальное значение xor для
arr[i]
. Мы знаем xor различных типов битов (0 ^ 1
или1 ^ 0
) всегда1
. Так во время запроса каждый бит, попробуйте пересечь узел, удерживающий противоположный бит. Это сделает этот конкретный бит1
результат в максимизации значения xor. Если нет узла с противоположным битом, только тогда пересеките тот же битовый узел. - после запроса вставить
arr[i]
в trie. - для каждого элемента следите за максимально возможным значением Xor.
- во время обхода каждого узла создайте другой ключ, для которого XOR максимизируется.
- выполнить запрос, чтобы получить максимальное значение xor для
на 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;
}
очень интересная проблема! Вот моя идея:
- сначала создайте двоичное дерево из всех чисел, используя двоичный представление и сортировка их в дерево самый значительный бит сначала (добавьте ведущие нули, чтобы соответствовать самому длинному числу). Когда каждый путь от корня до любого листа представляет одно число от оригинала набор.
- пусть A и b-указатели на узел дерева и инициализируют их в корне.
- Теперь переместите a и b вниз по дереву, попытка использовать противоположные ребра на каждом шаге, т. е. если a движется вниз по 0-краю, b движется вниз по 1-краю, если это невозможно.
Если a и b достигают листа, он должен указывать на два числа с" очень немногими " одинаковыми битами.
Я только что придумал этот алгоритм и не знаю, правильно ли это или как это доказать. Однако это должно быть во время o(n).
сделать рекурсивную функцию, которая принимает два списка целых чисел, A и B, в качестве своих аргументов. В качестве возвращаемого значения он возвращает два целых числа, одно из A и одно из B, которые максимизируют XOR двух. Если все числа равны 0, возвращает (0,0). В противном случае функция выполняет некоторую обработку и вызывает себя рекурсивно дважды, но с меньшими целыми числами. В одном из рекурсивных вызовов он рассматривает взятие целого числа из списка A для предоставления 1 в бит k, а в другом вызове он рассматривает взятие целое число из списка B для предоставления от 1 до бита k.
У меня нет времени сейчас, чтобы заполнить детали, но, может быть, этого будет достаточно, чтобы увидеть ответ? Кроме того, я не уверен, что время выполнения будет лучше, чем N^2, но это, вероятно, будет.
мы можем найти максимальное число в o (n) времени, а затем цикл через массив, выполняющий xor с каждым элементом. Предполагая, что стоимость операции xor равна O(1), мы можем найти max xor двух чисел в o (n) времени.