[C++] AcWing 143. 最大异或对

文章介绍了如何利用Trie树数据结构解决给定一组整数的找最大异或值问题,通过逐位比较并存储二进制状态,避免暴力计算,实现高效的算法优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目


思路


给n个数(1 2 3),怎么找最大异或?

  •   什么是异或?

   1. 先把数转换成二进制(每个数都有31位)
      1: 000...0001
      2: 000...0010
      3: 000...0011
   2. 按位异或(相同为0,不同为1)
      1和2: 除了最后两位不一样,其他的都一样,所以结果为
            000...0011(同样也是用二进制表示的数,转换成十进制就是3),即1和2异或的结果为3
      同理,1^3=2,2^3=1

  •   暴力解法就是每两个数都拿出来算一遍然后和目前最大的比较,得出最终结果,但是暴力会超时。优化解法是Trie树
  •   Trie树解法: 

   1. 还是先进行insert(),但是和之前字符串插入字符不同的是,这里是以0,1的形式插入。先把1,2,3这三个数都插到Trie树上。
   Trie树:(省略一部分,只取最后四位)
                 root (空结点)
                /
               0 
              /
             0
            /  \
           0   1  
          /    /   \
         1   0   1
         1   2   3      
这一行代表十进制数字
   2. 然后看1(0001),第3位(位次顺序是3210)是u=0,i=3,就在root的子结点里找1(既然是最大异或,贪心思想,我们就尽量每一位都找不同的,这样就能保证找出来是最大的),发现没有1,那跟1成最大异或对的第三位只能是0了(p要指向子结点0)。继续看第2位是u=0,i=2,再在p指向的结点的儿子结点里找1,还是没有1,同理,p要指向子结点0。继续看第1位是u=0,i=1,同理可得,p要指向子结点1。继续看第0位u=1,同理可得,p要指向子结点0。
   3. 重复步骤2,最终得到的最大异或对是3,也就是1^2。

代码


#include<iostream>
using namespace std;
const int N=100010,M=31*N;
int a[N];
int son[M][2],idx;//son[M][2]为什么是2列?因为每个结点的分支只有0,1,最多两个分支。字符串是26列是因为每个结点的分支最多有26个(26个英文字母)
int n;
void insert(int x)
{
	int p=0;
	for(int i=30;i>=0;i--)
	{
		int u=x>>i&1;
		if(!son[p][u])
			son[p][u]=++idx;
		p=son[p][u];
	}
}
int query(int x)
{
	int p=0,ans=0;
	for(int i=30;i>=0;i--)
	{
		int u=x>>i&1;//看第i位是0还是1
		//关于">>":右移。
		//eg:x=11101(二进制)
		//x>>2,x右移两位,最后面两位就没了->(111)=3
		//x>>i就相当于把第i位单独取出来,x>>i&1就是看这一位是0还是1,0&1=0,1&1=1
		if(son[p][!u])//!u是因为要找异或,u=0,就看子结点有没有1;u=1,就看子结点有没有0
		{
			p=son[p][!u];//有符合要求的,p指向这个符合题意的结点,再找下一位的时候就从这个结点的子结点找
			ans+=1<<i;//二进制数左移i位,高位舍弃,低位补0 (10<<2:  1010 -> 101000)
		}
		else//没有符合要求的(要求即u=0,找1,u=1,找0)
			p=son[p][u];//u=0,没有1,符合题意的就是0;u=1,没有0,符合题意的就是1
	}
	return ans;
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];//输入n个数
	int ans=0;
	for(int i=0;i<n;i++)
	{
		insert(a[i]);//插
		ans=max(ans,query(a[i]));
	}
	cout<<ans<<endl;
	return 0;
}


题目链接


https://www.acwing.com/problem/content/145/

如有不足或错误,请批评指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值