二进制字典树(01-Trie)

二进制字典树,也称为01-Trie,是一种专门用于存储数字二进制表示的字典树数据结构。它在解决异或相关问题、寻找最大/最小异或值以及位操作挑战方面表现出色,具有最优时间复杂度。

基本二进制字典树实现

二进制字典树从最高有效位开始逐位存储数字,实现高效的异或操作。

带异或操作的二进制字典树

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class BinaryTrie {
private:
    struct TrieNode {
        TrieNode* children[2];
        int count;
        
        TrieNode() : count(0) {
            children[0] = children[1] = nullptr;
        }
    };
    
    TrieNode* root;
    static const int MAXBITS = 30; // 对于32位整数
    
public:
    BinaryTrie() {
        root = new TrieNode();
    }
    
    void insert(int num) {
        TrieNode* curr = root;
        for (int i = MAXBITS; i >= 0; i--) {
            int bit = (num >> i) & 1;
            if (!curr->children[bit]) {
                curr->children[bit] = new TrieNode();
            }
            curr = curr->children[bit];
            curr->count++;
        }
    }
    
    void remove(int num) {
        TrieNode* curr = root;
        for (int i = MAXBITS; i >= 0; i--) {
            int bit = (num >> i) & 1;
            if (curr->children[bit]) {
                curr = curr->children[bit];
                curr->count--;
            }
        }
    }
    
    int findMaxXor(int num) {
        TrieNode* curr = root;
        int maxXor = 0;
        
        for (int i = MAXBITS; i >= 0; i--) {
            int bit = (num >> i) & 1;
            int oppositeBit = 1 - bit;
            
            // 尝试走相反位以获得最大异或
            if (curr->children[oppositeBit] && curr->children[oppositeBit]->count > 0) {
                maxXor |= (1 << i);
                curr = curr->children[oppositeBit];
            } else if (curr->children[bit] && curr->children[bit]->count > 0) {
                curr = curr->children[bit];
            } else {
                return -1; // 字典树为空
            }
        }
        
        return maxXor;
    }
    
    int findMinXor(int num) {
        TrieNode* curr = root;
        int minXor = 0;
        
        for (int i = MAXBITS; i >= 0; i--) {
            int bit = (num >> i) & 1;
            
            // 尝试走相同位以获得最小异或
            if (curr->children[bit] && curr->children[bit]->count > 0) {
                curr = curr->children[bit];
            } else if (curr->children[1-bit] && curr->children[1-bit]->count > 0) {
                minXor |= (1 << i);
                curr = curr->children[1-bit];
            } else {
                return -1; // 字典树为空
            }
        }
        
        return minXor;
    }
    
    bool search(int num) {
        TrieNode* curr = root;
        for (int i = MAXBITS; i >= 0; i--) {
            int bit = (num >> i) & 1;
            if (!curr->children[bit] || curr->children[bit]->count == 0) {
                return false;
            }
            curr = curr->children[bit];
        }
        return true;
    }
};

int main() {
    BinaryTrie trie;
    vector<int> nums = {3, 10, 5, 25, 2, 8};
    
    cout << "插入数字:";
    for (int num : nums) {
        cout << num << " ";
        trie.insert(num);
    }
    cout << endl;
    
    cout << "\n异或操作:" << endl;
    for (int num : {4, 6, 15}) {
        cout << "与 " << num << "的最大异或:" << trie.findMaxXor(num) << endl;
        cout << "与 " << num << "的最小异或:" << trie.findMinXor(num) << endl;
        cout << "搜索 " << num << ": " << (trie.search(num) ? "找到" : "未找到") << endl;
        cout << endl;
    }
    
    return 0;
}

二进制字典树从MSB到LSB逐位存储数字。对于最大异或,我们尽可能选择相反的位。对于最小异或,我们尽可能选择相同的位。

最大异或子数组问题

使用二进制字典树和前缀异或技术找到任意子数组的最大异或值。

最大异或子数组

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class XORSubarray {
public:
    // 简化版本,不跟踪范围
    int maxXorSubarraySimple(vector<int>& arr) {
        int n = arr.size();
        int maxXor = 0;
        int prefixXor = 0;
        
        BinaryTrie trie;
        trie.insert(0); // 插入0作为空前缀
        
        for (int i = 0; i < n; i++) {
            prefixXor ^= arr[i];
            maxXor = max(maxXor, trie.findMaxXor(prefixXor));
            trie.insert(prefixXor);
        }
        
        return maxXor;
    }
};

// 解决:给定数组,找到任意子集的最大异或
int maxSubsetXor(vector<int>& arr) {
    XORBasis basis;
    for (int num : arr) {
        basis.insert(num);
    }
    return basis.getMaxXor();
}

// 解决:检查目标是否可以通过异或某个子集形成
bool canFormTarget(vector<int>& arr, int target) {
    XORBasis basis;
    for (int num : arr) {
        basis.insert(num);
    }
    return basis.canForm(target);
}

int main() {
    vector<int> arr = {1, 2, 3, 4};
    XORSubarray solver;
    
    cout << "数组:";
    for (int x : arr) cout << x << " ";
    cout << endl;
    
    int simpleResult = solver.maxXorSubarraySimple(arr);
    cout << "简单方法结果:" << simpleResult << endl;
    
    // 用另一个数组测试
    vector<int> arr2 = {8, 1, 2, 12, 7, 6};
    cout << "\n数组2:";
    for (int x : arr2) cout << x << " ";
    cout << endl;
    
    int result2 = solver.maxXorSubarraySimple(arr2);
    cout << "最大异或子数组值:" << result2 << endl;
    
    return 0;
}

我们使用前缀异或性质:子数组[i,j]的异或 = prefixXor[j+1] ^ prefixXor[i]。对于每个位置,我们使用二进制字典树找到能给出最大异或的前缀。