整数の配列が与えられた場合、XORが最大である2つの要素を見つける必要があります。
素朴なアプローチがあります-各要素を選択して他の要素とxoringし、結果を比較してペアを見つけるだけです。
これ以外に、効率的なアルゴリズムはありますか?
これにはO(n lg U)アルゴリズムがあると思います。ここで、Uは最大数です。このアイデアはuser949300に似ていますが、もう少し詳細があります。
直感は次のとおりです。 2つの数値をXOR演算して最大値を取得するには、可能な限り高い位置に1を配置し、この位置に1が配置されているペアのうち、次の位置に1とペアを配置する必要があります可能な最高位など.
そのため、アルゴリズムは次のようになります。番号のどこかで最上位の1ビットを見つけることから始めます(n(番号)ごとにO(lg U)の作業を行うことで、O(n lg U)の時間内にこれを行うことができます)。ここで、配列を2つの部分に分割します。1つはそのビットに1があり、グループはそのビットに0があります。最適なソリューションは、最初のスポットの1の数字とそのスポットの0の数字を結合する必要があります。これにより、可能な限り1ビット高くなります。他のペアリングには0があります。
ここで、再帰的に、最も高い1を持つ1と0のグループから番号のペアを見つけたいと思います。これを行うには、これら2つのグループのうち、それらを4つのグループに分割します。
11と00のグループ、または10と01のグループに番号がある場合、それらのXORが理想的です(11から始まる)。したがって、これらのグループのペアのいずれかが '空の場合、これらのグループから理想的な解を再帰的に計算し、それらのサブ問題の解の最大値を返します。 XOR 1で始まる数値と0で始まる数値の場合、次の2番目のビットがキャンセルされるため、3番目のビットだけを見る必要があります。
これにより、MSBによって分割された2つのグループの数値から始めて、次の再帰アルゴリズムが得られます。
アルゴリズムを開始するには、実際には、最初のグループの番号を2つのグループ(MSB 1の番号とMSB 0の番号)に分割します。次に、2つの番号グループで上記のアルゴリズムの再帰呼び出しを実行します。
例として、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
要素が1つだけのバケットになったので、101と010(111を与える)と100と011(111を与える)のペアをチェックするだけです。どちらのオプションもここで機能するため、最適な答えは7です。
このアルゴリズムの実行時間について考えてみましょう。数値にはO(log U)ビットしかないため、最大再帰深度はO(lg U)であることに注意してください。ツリーの各レベルで、各番号は1回の再帰呼び出しで表示されます。各再帰呼び出しは、ビットごとに配布する必要があるため、0グループと1グループの合計数に比例して機能します。したがって、再帰ツリーにはO(log U)レベルがあり、各レベルはO(n) workを実行し、O(n log U)の合計作業量を与えます。
お役に立てれば!これはすごい問題でした!
これは、 Trie を使用してO(NlogN)
時間の複雑さで解決できます。
arr[i]
_ の各_arr[0, 1, ..... N]
_要素についてarr[i]
_に可能な最大xor値を取得するクエリを実行します。異なるタイプのビット(_0 ^ 1
_または_1 ^ 0
_)のxorは常に_1
_です。したがって、各ビットのクエリ中に、反対のビットを保持しているノードをトラバースしてみてください。これにより、特定のビット_1
_がxor値を最大化します。反対ビットのノードがない場合は、同じビットノードを横断するだけです。arr[i]
_をトライに挿入します。N
要素の場合、各要素に対して1つのquery(O(logN)
)と1つの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;
}
_
符号ビットを無視すると、値の1つは最上位ビットが設定された値の1つでなければなりません。 すべての値にそのビットが設定されていない限り。この場合、すべての値に設定されていない次の最上位ビットに移動します。したがって、HSBを調べることにより、最初の値の可能性を削減できます。たとえば、可能性がある場合
0x100000
0x100ABC
0x001ABC
0x000ABC
最大ペアの最初の値は0x100000または0x10ABCDでなければなりません。
@internal Server Error最小のものが必ずしも正しいとは思わない。 2番目の値を削減するための素晴らしいアイデアはありません。可能な最初の値のリストにあるではないの値。私の例では、0x001ABCまたは0x000ABCです。
非常に興味深い問題です!私のアイデアは次のとおりです。
Aとbがリーフに到達した場合、「ごく少数」の同一ビットを持つ2つの数値を指す必要があります。
私はこのアルゴリズムを作成したばかりで、そのアルゴリズムが正しいのか、それをどのように証明するのかわかりません。ただし、O(n)実行時間である必要があります。
引数として整数の2つのリストAとBをとる再帰関数を作成します。戻り値として、Aから1つ、Bから1つの2つの整数を返します。これにより、2つのうちXOR。が最大になります。すべての整数が0の場合、(0,0)関数は何らかの処理を行い、それ自体を2回再帰的に呼び出しますが、より小さい整数を使用します。再帰呼び出しの1つでは、リストAから整数を取得して1からビットkを提供することを検討します。リストBから1をビットkに提供します。
詳細を記入する時間がありませんが、答えを見るにはこれで十分でしょうか?また、実行時間がN ^ 2よりも良いかどうかはわかりませんが、おそらくそうです。