数组arr中有2种数a和b出现了奇数次,其余数出现了偶数次,请你找到出现奇数次的ab,并打印他们

数组arr中有2种数a和b出现了奇数次,其余数出现了偶数次,请你找到出现奇数次的ab,并打印他们

提示:异或运算重要知识点系列文章
这道题是互联网大厂面试高频题

只要了解异或运算的根本知识点,解决这些问题也就不在话下。

(1)认识异或运算的本质与基本规律,0^x=x,x异或x=0,有交换律,结合律,与顺序无关
(2)数组arr中,有一个数k出现了奇数次,其他数出现偶数次,请找到并打印这个数k
(3)如何把一个数字x最右侧那个1拿出来,变成00…10…的格式
(4)如何统计一个数字x中1的个数


题目

数组arr中有2种数a和b出现了奇数次,其余数出现了偶数次,请你找到出现奇数次的ab,并打印他们


一、审题

示例:arr = 1 1 1 2 2 2 3 3 4 4 4 4
显然a=1,b=2,他们出现了3次
其余的都出现了偶数次
故打印1 2


二、解题

暴力解

显然,你可以暴力统计词频,还是老方法,o(n)复杂度,但是速度贼慢。

异或运算最优解

同样是o(n)复杂度,但是速度贼快
因为位运算比四则运算速度快多了。

之前我们见过这个题:
【上面开头系列文章中的】(2)数组arr中,有一个数k出现了奇数次,其他数出现偶数次,请找到并打印这个数k

那是1个数x,特别好找

现在有2个数a和b,就不那么容易了,咱们要分析他们的特点

要知道,如果a和b都出现了奇数次,在arr所有元素异或之后,肯定能剩下来,也就是
eor = a^b,它还不会为0
你比如上面的案例:arr = 1 1 1 2 2 2 3 3 4 4 4 4
在这里插入图片描述
好,我们观察一下,既然1^2它不是0,显然有特点的
最起码a的最右侧那个1,它这个位置是1,那b的这个位置绝对不能是1,否则就麻烦了,你想想。
在这里插入图片描述
也就是说:a后面那些都是0,最右侧那个1左边所有的位置,是0是1无所谓,比如t
b后面那些是y随意,与a最右侧那个1对应的位置,必须是0,其他随意x
这样子的话,a^b,它才能保证不是0!
在这里插入图片描述
恰好我们利用这个特性,从新去arr中把a捞出来
(1)刚刚我们不是已经找到eor=a^b了吗
(2)显然取eor最右侧那个1,rightOne=eor&(eor取反+1)【这个知识点x取反加1再与x拿到x最右侧那个1,在开头介绍的文章中有说过的哦】
(3)不妨设:令a=0,去arr中一次遍历,a=a^arr[i],一旦遇到a&rightOne != 0,则a找到了,a就是此时的arr[i]
啥意思?【好好理解这里!!!】
在a不断异或[i]的过程中,实际上,偶数次那些个数都废了,我们恰好能找到奇数次那个数,且最右侧是1那个数。
在这里插入图片描述
(4)此时a找到了,自然b也就就能搞定了,b=a^eor
为啥呢?eor=a^b
那你b自然等于a^eor=a异或a异或b=b【x异或x=0,x异或0=x】【这都是文章开头那些文章中讲的基础知识点】
现在,ab找到了吧!!!
是不是很妙!!
手撕代码:

 public static void printOdd2TimesNumReview(int[] arr){
        int N = arr.length;

        //利用异或特性--先取a^b
        int eor = 0;
        for (int i = 0; i < N; i++) {
            eor ^= arr[i];
        }
        //然后找eor最右侧那个1
        int rightOne = eor & ((~eor) + 1);
        //然后把a捞出来
        int a = 0;
        for (int i = 0; i < N; i++) {
            //先看arri是否最右侧1存在,再比较
            if ((rightOne & arr[i]) != 0) a ^= arr[i];
            //反正要把所有那个位置为1的数都拿来异或,自然留下了a,b在这个位置它不可能是1
        }

        //找到了a,再求b
        int b = a ^ eor;

        System.out.println("a:"+ a +" b:"+ b);
    }

    public static void test(){
        int[] arr = {1,1,1,2,2,2,3,3,4,4,4,4};
        printOdd2TimesNum(arr);
        printOdd2TimesNumReview(arr);
    }

    public static void main(String[] args) {
        test();
    }

思想明白了,代码非常简单!!!


总结

提示:重要经验:

1)异或的强大之处再次见识到了吧!!解决出现奇数次的问题
2)到此我们关于异或运算的知识点到一段路,后续有机会我们再来温习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值