惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
T
Threat Research - Cisco Blogs
H
Hacker News: Front Page
N
News and Events Feed by Topic
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
K
Kaspersky official blog
Forbes - Security
Forbes - Security
Webroot Blog
Webroot Blog
Schneier on Security
Schneier on Security
P
Privacy & Cybersecurity Law Blog
H
Heimdal Security Blog
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
V
Vulnerabilities – Threatpost
T
Tenable Blog
T
Tailwind CSS Blog
P
Privacy International News Feed
WordPress大学
WordPress大学
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
博客园 - Franky
Hacker News: Ask HN
Hacker News: Ask HN
Jina AI
Jina AI
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
雷峰网
雷峰网
Vercel News
Vercel News
A
About on SuperTechFans
爱范儿
爱范儿
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
The Last Watchdog
The Last Watchdog
Engineering at Meta
Engineering at Meta
Spread Privacy
Spread Privacy
Security Archives - TechRepublic
Security Archives - TechRepublic
博客园 - 司徒正美
量子位
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Hacker News - Newest:
Hacker News - Newest: "LLM"
Recorded Future
Recorded Future
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Martin Fowler
Martin Fowler
Project Zero
Project Zero

博客园 - 有点懒惰的大青年

用一个实际业务场景演示ON和WHERE的正确使用方式 idea中查看源码 使用Hutool对时间的花式操作 合并K个升序列表 比较器 枚举类的设计模式 spring boot中使用RedissonClient实现分布式锁 idea同时启动application,启用不同端口 @PostConstruct用法 ApplicationContext 事件发布与监听机制详解 java自带命令jps介绍 关于maven中标签的说明 mysql的跨库查询 linux命令ll显示结果的含义 Files类的使用 java环境变量设置 java值传递和引用传递 kafka的集群与可靠性 kafka的消费全流程 关于kafka 达梦数据库执行计划介绍 关于MQ 用位运算实现加减乘除(3)
异或运算
有点懒惰的大青年 · 2026-05-23 · via 博客园 - 有点懒惰的大青年

1)0 ^ N = N

2) N ^ N = 0

3) 同样一批数,有交换律和结合律,异或得到的结果一定是同一个。

交换律:a ^ b = b ^ a

结合律:(a ^ b) ^ c = a ^ (b ^ c)

其实都是来自于异或运算就是无进位相加这一个概念。

题目一:    

一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这个数?

思路:

将数组中所有数异或一遍,得到的数,就是最终的求值。

image

题目二:    

怎么把一个int类型的数,提取出最右侧的1来

题目的画图如下:

image

image

image

a & (~a + 1) 可以得到结果,保留a最后侧的1。a 与上 (a取反 + 1),也就是a 与 a的相反数。也就是:

a & (-a)

package class02;

public class Code02_EvenTimesOddTimes {

    // arr中,只有一种数,出现奇数次
    public static void printOddTimesNum1(int[] arr) {
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^= arr[i];
        }
        System.out.println(eor);
    }

    public static void main(String[] args) {
        int a = 5;
        int b = 7;

        //这样运算下来,a和b发生交换
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;

        System.out.println(a);
        System.out.println(b);

        int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 };
        printOddTimesNum1(arr1);
    }
}

题目四:  

一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数?

image

image

image

image

思路:

1.假设数组中a、b出现了奇数次,准备一个变量eor,将数组中的数全部异或一遍,最后得到的结果就是a^b

2.因为a!=b,所以eor!=0,那么eor肯定有一个位置是1。可以利用上面所学的,找到eor最右侧的1.

3.不妨设eor从右往左数,第三个位置上的数是1,说明啥?说明a和b在这第三个位置的数一定不相同。

4.可以将arr数组分成两半:第一半:第三个位置的数是1的所有数。第二半:第三个位置的数是0的所有数。a和b在这两类中一定是分开的。

5.那么在第一半中出现了偶数次的某些数,他们的第三个位置是1. 第二半中也会存在第三个位置是0的一些出现了偶数次的数。也就是说:这些出现了偶数次的数,也会被砍成两半,一半是左边,一半是右边,是互斥的:

6.分别将左半部分进行异或、右半部分进行异或。就得到了结果。

image

代码:

package class02;

import java.util.Arrays;


public class Code07_EvenTimesOddTimes {

    /**
     * arr中,有两种数,出现奇数次
     * 找到这两个数:
     */
    public static int[] printOddTimesNum2(int[] arr) {
        int eor = 0;
        for (int i = 0; i < arr.length; i++) {
            eor ^=  arr[i];
        }
        //eor = a ^ b
        //eor != 0
        //eor必然有一个位置上是1
        //0110010000
        //0000010000
        int rightOne = eor & (~eor + 1);    //提取出最右的1
        int onlyOne = 0; //eor'
        for (int i = 0; i < arr.length; i++) {
            //arr[i]  = 111100011110000
            //rightOne= 000000000010000
            if ((arr[i] & rightOne) != 0) {
                onlyOne ^= arr[i];
            }
        }

        return new int[]{onlyOne, eor ^ onlyOne};
    }

    public static void main(String[] args) {
        int[] arr = {6, 6, 6, 10, 4, 4, 12, 12, 12, 12, 3, 3};
        int[] result = printOddTimesNum2(arr);
        System.out.println(Arrays.toString(result));
    }
}

题目5:一个数组中有一种数出现K次,其他数都出现了M次,M > 1, K < M

找到出现了K次的数。

要求,额外空间复杂度O(1),时间复杂度O(N)

思路:

1.任何一个int类型的数是32位,可以将任何一个int数转化为二进制数组,比如a=8:[000000000000000..1000], a总能找到对应的二进制数组来表达的方法。

2.任何数都可以转化成数组形式的二进制数。

3.准备一个数组,长度是32.刚开始这个数组的数全是0。然后依次遇到一些数字,吧所有数字每个位置上的1全累加到数组t里。

image

image

假设某个数X出现了3次,a,b,c都出现了7次。如果这个数组在0位置的1的数量是7的整数倍,那说明X在第0个位置是不含1的。因为如果X在第0个位置含1的话,最后1的数量不可能是7的整数倍。因为X一共出现3次啊,它一定是7的整数倍+余数,余数就是这个K。

代码实现:

package class02;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Code08_KM {

    /**
     * 用来做对数器的对照方法
     * 保证这个一定是正确的,这里用hashMap词频统计来实现
     */
    public static int test(int[] arr, int k, int m) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            if (map.containsKey(arr[i])) {
                map.put(arr[i], map.get(arr[i]) + 1);
            } else {
                map.put(arr[i], 1);
            }
        }

        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == k) {
                return entry.getKey();
            }
        }
        return -1;
    }

    /**
     * 请保证arr中,只有一种数出现了K次,其他数都出现了M次。
     * <p>
     * 别看有两个for循环,但是下面的for循环次数是固定的,32次。所以时间复杂度是O(n)
     * 虽然申请了一个数组,但是长度也是固定32个。额外空间复杂度是O(1)
     */
    public static int onlyKTimes(int[] arr, int k, int m) {
        int[] t = new int[32];
        // t[0] 0位置的1出现了几个
        // t[i] i位置的1出现了几个
        for (int num : arr) {
            for (int i = 0; i <= 31; i++) {
                t[i] += (num >> i) & 1;
            }
        }
        int ans = 0;
        for (int i = 0; i < 32; i++) {
            if (t[i] % m != 0) {    //在第i位上,有1
                ans |= (1 << i);
            }
        }
        return ans;
    }

    /**
     * 用来产生一个随机数组
     * @param maxKinds  出现多少种数
     * @param range 每个数的大小范围
     * @param k 出现k次
     * @param m 出现m次  k < m
     * @return
     */
    public static int[] randomArray(int maxKinds, int range, int k, int m) {
        int ktimeNum = randomNumber(range);
        //产生多少种数,最少2种
        int numKinds = (int) (Math.random() * maxKinds) + 2;
        //数组的长度, k * 1 + (numKinds - 1) * m
        int[] arr = new int[k + (numKinds - 1) * m];
        //先在数组中填充出现K次的数
        int index = 0;
        for (; index < k; index++) {
            arr[index] = ktimeNum;
        }
        //继续填充出现M次的数
        numKinds--;
        HashSet<Integer> set = new HashSet<>();
        set.add(ktimeNum);
        while (numKinds != 0) {
            int curNum = 0;
            do {
                curNum = randomNumber(range);
            } while (set.contains(curNum));
            set.add(curNum);
            numKinds--;
            for (int i = 0; i < m; i++) {
                arr[index++] = curNum;
            }
        }

        //arr 填好了,但是顺序太有规律了,进行调整一下
        // i位置的数,我想随机和j位置的数做交换
        for (int i = 0; i < arr.length; i++) {
            int j = (int) (Math.random() * arr.length);  // 0 ~ N -1
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
        return arr;
    }

    /**
     * 产生一个 [-Range, +Range]之间的随机数
     */
    public static int randomNumber(int range) {
        return (int) ((range + 1) * Math.random()) - (int) ((range + 1) * Math.random());
    }

    public static void main(String[] args) {
        int[] array = {4, 3, 1, 3, 3, 1, 1, 4};
        int k1 = 2;
        int m1 = 3;
        //System.out.println(onlyKTimes(array, k1, m1));
        //System.out.println(test(array, k1, m1));

        //使用对数器进行测试
        int kinds = 10;     //测试数组中最多有10种数
        int range = 200;    //数组中数据大小范围 -200 ~ 200
        int testTimes = 100000;
        int max = 9;
        System.out.println("测试开始");
        for (int i = 0; i < testTimes; i++) {
            int a = (int) (Math.random() * max) + 1;        //a  1 ~ 9
            int b = (int) (Math.random() * max) + 1;        //b  1 ~ 9
            int k = Math.min(a, b);
            int m = Math.max(a, b);
            if (k == m) {   // 确保K < M
                m++;
            }

            int[] arr = randomArray(kinds, range, k, m);
            int ans1 = test(arr, k, m);
            int ans2 = onlyKTimes(arr, k, m);
            if (ans1 != ans2) {
                System.out.println("出错了!");
            }
        }
        System.out.println("测试结束");
    }
}

上面代码实现中,有对数器的使用,就是为了来测试我们写的方法是否正确。对照方法可以随便写,不管怎么实现,只要保证一定是正确的,就可以用来做test对照方法。

--