异或运算总结

1.性质

1)异或运算:0^N == N      N^N == 0

能长时间记住的概率接近0% 所以,异或运算就记成无进位相加运算!

2)异或运算满足交换律和结合率 上面的两个性质用无进位相加来理解就非常的容易

3) 同样一批数子的亦或运算,不管怎么排列亦或,结果相等

2.认识异或运算应用

1)题目一 如何不用额外变量交换两个数独立内存区域的 a,b变量

答 : a =甲 ,b =乙

a =a ^b

b =a^b

a =a^b

分析过程:

a=a^b =甲^乙

b=a^b =甲^乙^乙=甲^(乙^乙)=甲

a=a^b =甲^乙^甲=(甲^甲)^乙=乙

交换结束!!!

注意:如果同一个内存地址的两个引用,不能这样,N^N 就等于0了

 2)题目二 一个数组中有一个数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这个数

答:

//只有1出现了基数次
int[] intArray = {1,1,1,2,2,4,4,5,5,6,6,7,7,7,7};
// 找出来
int jishuValue =0;
for (int i = 0; i < intArray.length; i++) {
 jishuValue= jishuValue ^ intArray[i];
}
// 分析N^N=0,偶数次N^N ==0 ,0^M=M,所以出现基数次的M就被找出来了
System.out.println("jishuValue = " + jishuValue);//jishuValue = 1

3) 题目三 怎么把一个int类型的数,提取出最右侧的1来

 a=        0 1 1 1 0 1 0 1 0 0 0

~a=      1  0 0 0 1 0 1 0 1 1 1 , 取反让1左侧 &的结果为0 ,右侧& 的结果为0,又因为自己1& 0 ==0 ,所以+1处理一下

~a+1=  1 0 0 0 1 0 1 1 0 0 0 a & (~a+1)  ==  0 0 0 0 0 0 0 1 0 0 0    ==a&(-a),  

  找出来最右侧的1 了.因为~a+1==-a,所以 a&(~a+1) ==a&(-a)

 4) 题目四 一个数组中有两个数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两个数

public static void main(String[] args) {

    int[] intArray = {1, 1, 1, 2, 4, 4, 5, 5, 6, 6, 7, 7, 7, 7};
    //step1 找出来1,2 的亦或
    int jishuEor = 0;
    for (int i = 0; i < intArray.length; i++) {
        jishuEor = jishuEor ^ intArray[i];
    }
    //        此时的jishuEor == 1^2
    //    找出基数1,推出基数2
    //    step2 找出1: 因为1,2 肯定不相等 ==> jishuEor != 0 ==> jishuEor 的二进制数中肯定有1 ,找到最右侧的1,
    //              通过整个1的位置,将数组分成这个位置为1 的 和不为1 的,对为1的进行单独的亦或。为1 的这一组,因为对应分到的这部分偶数项亦或也为0,
    //              就剩下单独的一个基数了 ,比如是1, 根据 1^(1^2) ==>2 的值

    int rightest1 = jishuEor ^ (-jishuEor);
    int eOr = 0;
    for (int i = 0; i < intArray.length; i++) {
        if ((rightest1 & intArray[i]) != 0) {
            eOr = eOr ^ intArray[i];
        }
    }
    //    此时eOr为一个基数
    int secondJishu = eOr ^ jishuEor;
    System.out.println("基数1 = " + eOr);
    System.out.println("基数2 = " + secondJishu);
}

5 )题目5 一个数组中有一个数出现K次,其他数都出现了M次, M > 1,  K < M 找到,出现了K次的数, 要求,额外空间复杂度O(1),时间复杂度O(N)

思路:

已知 int [] arr, 出现k次的值设为x,其他数都出现m次,k<m,m>1,求x

因为:每个int 类型数,都是32位的二进制数,可以将其转换为32位数长度的整数数组;

所以,

1.  可以将数组中所有的数按二进制位对应位置出现1的次数统计,比如在0位置出现了

5次,1位置出现了4次......就能整理出一个32位的二进制数,记作w。 w的二进制每一位代表着数组arr中在这个位置上出现1的次数之和。

2. 这个和可能全部由m次的数组成,也可能由出现k次的数全部组成,也可能由k次,m次的数一起组成。但是由于k<m, 因此 这个数%m ,如果值 !=0 ,说明有余数,表示 x在这个位置上一定是1。同理只要在每个位置上都取模遍历一遍,就能得到每个位置是0还是1,也就能得到这个x。

package com.dchealth.test;

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


public class Test {
    //step1.把数组中的每一个元素转换为32位二进制的数组,数组中32位的每一位,对应累加
    //step2. 32位数组中每一位 % m,如果不为0,表示出现k次的那个元素在该位置上有加1,也就是说出现k次的元素,转换位32位的二进制后在该位置上为1
    //step3,得到求值
    public static void main(String[] args) {
        // 遍历次数
        int times = 10000;
        // 元素范围 [-30,30]
        int numRange = 30;
        // 数组arr中同一种元素最多出现的次数
        int oneElementMaxFindTimes = 10;
        // 数组中元素的种类数
        int maxKind = 10;

        int a = (int) (Math.random() * oneElementMaxFindTimes) + 1;
        int b = (int) (Math.random() * oneElementMaxFindTimes) + 1;

        int k = Math.min(a, b);
        int m = Math.max(a, b);
        if (k == m) {
            m++;
        }
        System.out.println("开始!");
        for (int i = 0; i < times; i++) {
            // 构建数组arr
            int[] arr = buildArray(numRange, k, m, maxKind);
            if (getKTimesNumMethod2(arr, k, m) != getKTimesNumMethod1(arr, k, m)) {
                System.out.println(" 出错了!");
                for (int j = 0; j < arr.length; j++) {
                    System.out.print(arr[j] + " ");
                }
                System.out.println(" ");
                System.out.println("k = " + k);
                System.out.println("m = " + m);
                return;
            }

        }
        System.out.println("结束!");

    }

    private static int[] buildArray(int numRange, int k, int m, int kind) {
        //数组长度(至少大于等于两种)
        int elementKindsNum = (int) (Math.random() * kind) + 2;
        int len = k * 1 + (elementKindsNum - 1) * m;
        int[] arr = new int[len];
        // 数组中添加出现k次的元素
        int elementOfFindKTimes = buildRandomNumOfInRange(numRange);
        int index = 0;
        for (index = 0; index < k; index++) {
            arr[index] = elementOfFindKTimes;
        }
        elementKindsNum--;
        HashSet<Integer> set = new HashSet<>();
        set.add(elementOfFindKTimes);
        // 数组中添加出现m次的元素
        while (elementKindsNum != 0) {
            //构建元素 && 添加m次
            int currentNum = 0;
            do {
                currentNum = buildRandomNumOfInRange(numRange);
            } while (set.contains(currentNum));
            set.add(currentNum);
            elementKindsNum--;
            for (int i = 0; i < m; i++) {
                arr[index++] = currentNum;
            }

        }
        // 打乱有序的arr
        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;

    }

    public static int buildRandomNumOfInRange(int range) {
        return (int) (Math.random() * (range + 1)) - (int) (Math.random() * (range + 1));
    }

    public static int getKTimesNumMethod2(int[] arr, int k, int m) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        int[] t = new int[32];
        for (int num : arr) {
            for (int i = 0; i < 32; i++) {
                t[i] += (num >> i) & 1;
            }
        }
        int res = 0;
        for (int i = 0; i < 32; i++) {
            if (t[i] % m != 0) {
                res |= (1 << i);
            }
        }
        return res;
    }

    public static int getKTimesNumMethod1(int[] arr, int k, int m) {
        if (arr == null || arr.length == 0) {
            return -1;
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int num : arr) {
            if (map.get(num) != null) {
                map.put(num, map.get(num) + 1);
            } else {
                map.put(num, 1);
            }
        }
        for (int num : arr) {
            if (map.get(num) == k) {
                return num;
            }
        }
        return -1;
    }
}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值