(Java)算法——位运算在算法题中的应用

位运算在算法题中的应用

上篇博客总结的位运算的基础和基本的使用,这篇博客总结一下位运算在一些算法题中的使用

上篇博客:位运算基础及基本应用

题1——唯一一个重复的数

数组1-1000中(1001个数),有唯一一个重复的数,其他数只出现一次,求唯一一个重复的数。
要求:数组元素只能访问一次,不使用辅助空间

解题思路

利用位运算的异或性质解题a ^ b ^ b = a ^ 0 = a。原数组1-1000并且有一个重复的数(数组长度1001),与数组1-1000进行所有元素异或,最后剩下的是唯一重复的数,其余的数都已经抵消。

代码实现
	private static int findRepetitionNum ( int[] arr ) {
        int x = 0;
        //将1-1000进行运算,得出结果
        for (int i = 1; i < arr.length; i++) {
            x = (x ^ i );
        }
        //将x(将1-1000进行运算的结果)和目标数组(1-1000,并且包含一个重复的数)进行^运算
        //前后抵消,只剩下那个重复的数
        for (int i = 0; i <arr.length; i++) {
            x = x ^ arr[i];
        }
        return x;
    }
暴力破解(不符合要求,多用了一个辅助空间,数组)
解题思路

利用一个辅助数组,遍历原数组,值为辅助数组的索引,没出现一次+1,最后辅助数组中有一个索引的值为2,其索引为重复的数

代码实现
	private static int findRepetitionNumByViolence(int[] arr){
        int[] help=new int[arr.length];
        for (int i = 0; i <help.length ; i++) {
            help[arr[i]]++;
        }
        for (int i = 0; i <help.length ; i++) {
            if (help[i]==2){
                return i;
            }
        }
        return 0;
    }

题2——找出单独存在的数

在一个值均成对,只有一个单独存在的数组中,找出单独存在的数

解题思路

利用异或的性质,数组所有元素进行异或,剩下的是单一的数。(和题1非常类似)

代码实现
  private static int findOnlyNum ( int[] arr ){
        for (int i = 1; i <arr.length; i++) {
            arr[i] = arr[i] ^ arr[i-1] ;
            if (i==arr.length-1){
                return arr[i];
            }
        }
        return 0;
    }

题3——二进制表示中1的个数

输入一个整数,输出该二进制表示中1的个数

实现方式1

解题思路

将1每次左移1位,判断整数每次左移的&运算,如果为1则+1

代码实现
  	private static int sumOneCount ( int n ) {
        int count = 0;
        //整数共32位
        for (int i = 0; i < 32; i++) {
            if ((n & (1 << i)) == (1 << i)) {
                count++;
            }
        }
        return count;
    }

实现方式2

解题思路

将整数每次右移1位,进行&运算,如果为1则+1

代码实现
 	private static int sumOneCount ( int n ) {
        int count = 0;
        //整数共32位
        for (int i = 0; i < 32; i++) {
            if (((n >>> i)&1) == 1) {
                count++;
            }
        }
        return count;
    }

实现方式3

解题思路

每次进行-1操作,从低位到高位遇到的第一个1变0,其余0变1
例如 10100 -1 -> 10011
(n - 1) & n,削掉最低位的1
例如 (10100-1)&10100 -> 100000
每循环一次,销掉一个1,次数+1,直至变为0.结束

代码实现
 	private static int sumOneCount ( int n ) {
        int count = 0;
        while (n != 0) {
            n = ((n - 1) & n);
            count++;
        }
        return count;
    }

题4——交换一个整数的二进制奇偶位

交换一个整数的二进制奇偶位

解题思路

假设一个数n的二进制为 xyxy xyxy xyxy ……

  1. 和 1010 1010 1010 …… 做与运算,取出奇数位 ——>x0x0 x0x0 x0x0 ……
  2. 和 0101 0101 0101 …… 做与运算,取出偶数位 ——>0y0y 0y0y 0y0y ……
  3. 偶数位右移1位,奇数位左移1位,进行异或,交换位置——>yxyx yxyx yxyx ……
代码实现
	private static int transform ( int n ) {
        //假设n, xyxy xyxy xyxy ……
        //32位太麻烦,所以用16进制来表示
        //和 1010 1010 1010 …… 做与运算,取出奇数位  ——>x0x0 x0x0 x0x0 ……
        int ji = n & 0xaaaaaaaa;
        //和 0101 0101 0101 …… 做与运算,取出偶数位  ——>0y0y 0y0y 0y0y ……
        int ou = n & 0x55555555;
        //连起来为 yxyx yxyx yxyx ……
        return (ji >> 1) ^ (ou << 1);
    }

题5——整数是不是2的整数次方

整数是不是2的整数次方
要求:用一条语句判断

解题思路(和题3的方法3类似)

规律:整数是2的整数次方的数,二进制只有一个1
如果可以一次消除1之后变为0,说明是2的整数次方

代码实现
	public static boolean isTwoNum(int n){
        return ((n-1)&n)==0;
    }

题6——0-1间浮点实数的二进制表示

0-1间浮点实数的二进制表示。
给定一个0-1间的实数,例如0.625,类型为double,打印二进制表示为(0.101,因为小数点后的二进制分别为0.5,0.25.0.125……)
如果该数字无法精确地用32位以内的二进制表示,则打印"ERROR"

解题思路

0-1间浮点实数的二进制计算方法:**每次乘2,扣除整数,直至变为0;**若大于32位则报错

代码实现
	private static String transform(double n){
        StringBuilder sb=new StringBuilder("0.");
        while (n>0){
            //每次乘2
            double r=n*2;
            //判断取的整数位是0还是1
            if (r>=1){
                sb.append("1");
                n=r-1;
            }else{
                sb.append("0");
                n=r;
            }
            //若大于32位则报错;34 包括 “0”和“.”
            if (sb.length()>34){
                return "ERROR";
            }
        }
        return sb.toString();
    }

题7——出现k次和1次

出现k次和1次
数组中只有一个数出现了1次,其他数都出现了k次
输出只出现一次的数

实现方式1_位运算

解题思路

2个相同的二进制数不进位相加等于0
10个相同的十进制数不进位相加等于0
k个相同的K进制数不进位相加等于0

代码实现
	private static int findOneNum ( int[] arr, int k ) {
        int len = arr.length;
        //存每个数组元素的k进制的每一位
        char[][] kRadix = new char[len][];
        //数字中转成k进制最长的长度
        int maxLen = 0;
        //遍历每个数字
        for (int i = 0; i < len; i++) {
            //求每个数字的k进制并反转,然后转为字符数组;
            // 反转——从低位进行不进位加法,保证位对齐
            kRadix[i] = new StringBuffer(Integer.toString(arr[i], k)).reverse().toString().toCharArray();
            //记录数字中转成k进制最长的长度
            if (kRadix[i].length > maxLen) {
                maxLen = kRadix[i].length;
            }
        }
        //进行不进位加法
        int[] resArr = new int[maxLen];
        for (int i = 0; i < len; i++) {
            //不进位加法
            for (int j = 0; j < maxLen; j++) {
                if (j >= kRadix[i].length) {
                    resArr[j] += 0;
                } else {
                    //char-'0'——char转为int类型
                    resArr[j] += (kRadix[i][j] - '0');
                }
            }
        }
        //将出现一次的数从k进制转为10进制
        int res = 0;
        for (int i = 0; i < maxLen; i++) {
            //(int)(Math.pow(k,i))——k的i次方
            res += (resArr[i]% k) * (int) (Math.pow(k, i));
        }
        return res;
    }

实现方式1_哈希表

解题思路

利用哈希表,Java中的map存取<K,V>键值对,K为
数组的元素(不重复),V为该元素在数组中出现的次数。输出值为1的键,即是出现1次的数

代码实现
	private static int findOneNum2 ( int[] arr ) {
        Map<Integer, Integer> arrMap = new HashMap<>();
        for (int value : arr) {
            if (arrMap.containsKey(value)) {
                arrMap.put(value, arrMap.get(value) + 1);
            } else {
                arrMap.put(value, 1);
            }
        }
        for (Map.Entry<Integer, Integer> entry : arrMap.entrySet()) {
            if (entry.getValue() == 1) {
                return entry.getKey();
            }
        }
        return 0;
    }

这篇博客主要总结了一下,位运算在算法中的一些题解,下一篇总结下,利用位运算如何实现加减乘除?

发布了23 篇原创文章 · 获赞 15 · 访问量 3728

猜你喜欢

转载自blog.csdn.net/qq_42937522/article/details/104664642
今日推荐