Day7/7:2021-1-31 剑指offer-位运算

在这里插入图片描述

7.位运算

// jdk中二进制转十进制方式
System.out.println(Integer.parseInt("1001", 2));

// jdk中十进制转二进制方式
System.out.println(Integer.toBinaryString(9));
System.out.println(Integer.toString(9, 2));
剑指 Offer 15 .二进制中1的个数

思考:那就记住吧。只能用>>>。而不能用>>。

public class Offer15 {
    public static void main(String[] args) {
        int n = 9;
        System.out.println(hammingWeight(n));
    }

    public static int hammingWeight(int n) {
        int res = 0;
        while (n != 0) {
            res += n & 1;
            n >>>= 1;
        }
        return res;
    }
}
// 在传递时也把符号一起传递,比如+3、-2在传递再传出时依然是+3、-2,而使用>>>时就会统一变为3、2。带符号于无符号的差别就在此。
public class Offer15_1 {
    public static void main(String[] args) {
        System.out.println(2 >> 1);//1
        System.out.println(2 >>> 1);//1
        System.out.println(-2 >> 1);//-1
        System.out.println(-2 >>> 1); //2147483647
    }
}
// 感觉也不是呀。
剑指 Offer 56 - 基础题
基础内容:
如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?
另一种是,一个数组中一个数字出现了奇数次,其他数字都出现了偶数次,求出这个出现奇数次的数字。

答案很简单:全员进行异或操作即可。考虑异或操作的性质:对于两个操作数的每一位,相同结果为 0,不同结果为 1。那么在计算过程中,成对出现的数字的所有位会两两抵消为 0,最终得到的结果就是那个出现了一次的数字。

在一开始设置的为0。
n&(n-1)会删掉二进制中最后一个1。
public class Offer56_1_3 {
    public static void main(String[] args) {
        // 找出数组中只出现一次的数。
        int[] nums = new int[]{1, 1, 2, 2, 3};
        System.out.println(singleNumbers(nums));
    }

    public static int singleNumbers(int[] nums) {
        int res = 0;
        for (int num : nums) {
            res = res ^ num;
        }
        return res;
    }
}
// 结果为3
剑指 Offer 56 - I 数组中数字出现的次数

一个整型数组 nums 里除一个数字之外,其他数字都出现了两次。请写程序找出这个只出现一次的数字。全员异或操作即可得到结果。

(1)位运算

题解:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/jian-zhi-offer-56-i-shu-zu-zhong-shu-zi-tykom/

image-20210130173526122

// 位运算
import java.util.Arrays;
import java.util.HashMap;

public class Offer56_1_2 {
    //一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
    public static void main(String[] args) {
        int[] nums = new int[]{4, 1, 4, 6};
        System.out.println(Arrays.toString(singleNumbers(nums)));
    }

    public static int[] singleNumbers(int[] nums) {
        //xor用来计算nums的异或和
        int xor = 0;

        // 计算异或和 并存到xor
        // e.g. [2,4,2,3,3,6] 异或和:(2^2)^(3^3)^(4^6)=2=010
        for (int num : nums) {
            xor ^= num;
//            System.out.println(Integer.toBinaryString(xor));
        }

        //设置mask为1,则二进制为0001
        // mask是一个二进制数,且其中只有一位是1,其他位全是0,比如000010,
        // 表示我们用倒数第二位作为分组标准,倒数第二位是0的数字分到一组,倒数第二位是1的分到另一组
        int mask = 1;

        // & operator只有1&1时等于1 其余等于0
        // 用上面的e.g. 4和6的二进制是不同的 我们从右到左找到第一个不同的位就可以分组 4=0100 6=0110
        // 根据e.g. 010 & 001 = 000 = 0则 mask=010
        // 010 & 010 != 0 所以mask=010
        // 之后就可以用mask来将数组里的两个数分区分开
        System.out.println("xor=" + Integer.toBinaryString(xor));
        System.out.println("mask=" + Integer.toBinaryString(mask));
        while ((xor & mask) == 0) {
            mask <<= 1;
            System.out.println(Integer.toBinaryString(mask));
        }
        System.out.println("mask=" + Integer.toBinaryString(mask));
        //两个只出现一次的数字
        int a = 0, b = 0;

        for (int num : nums) {
            //根据&是否为0区分将两个数字分区,并分别求异或和
            if ((num & mask) == 0) a ^= num;
            else {
                b ^= num;
            }
        }
        return new int[]{a, b};
    }
}
(2)hashmap
// hashmap
import java.util.Arrays;
import java.util.HashMap;

public class Offer56_1 {
    //一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
    public static void main(String[] args) {
        int[] nums = new int[]{4, 1, 4, 6};
        System.out.println(Arrays.toString(singleNumbers(nums)));
    }

    public static int[] singleNumbers(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int count = map.getOrDefault(nums[i], 0) + 1;
            map.put(nums[i], count);
        }
        int[] res = new int[2];
        int index = 0;
        for (Integer key : map.keySet()) {
            if (map.get(key) == 1) {
                res[index++] = key;
                if (index == 2) {
                    break;
                }
            }
        }
        return res;
    }
}
剑指 Offer 56 - II 数组中数字出现的次数 II
(1)位运算

思考:

首先遍历一个数,将其转为二进制形式。数组形式。

比如 n=11=8+2+1=1011B

import java.util.Arrays;

public class Offer56_2_1 {
    public static void main(String[] args) {
        int n = 11;
        int[] count = new int[32];
        System.out.println("n=" + n + "二进制位:" + Integer.toBinaryString(n));
        for (int i = 0; i < 32; i++) {
            count[i] += n & 1;
            n >>>= 1;
            System.out.println("i=" + i + " count=" + Arrays.toString(count));
        }
    }
}
n=11二进制位:1011 你看,下面的结果是逆序的。这个是和自己一开始考虑的不一样哦。
i=0 count=[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
i=1 count=[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
i=2 count=[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
i=3 count=[1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
i=4 count=[1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
i=5 count=[1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

image-20210131152749876

image-20210131152810694

image-20210131152825407

public class Offer56_2 {
    public static void main(String[] args) {
        int[] nums = new int[]{9, 1, 7, 9, 7, 9, 7};
        System.out.println(singleNumber(nums));
    }

    public static int singleNumber(int[] nums) {
        int[] count = new int[32];
//        System.out.println(Arrays.toString(count));
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < 32; j++) {
                count[j] += nums[i] & 1;
                nums[i] >>>= 1;
//                System.out.println(Arrays.toString(count));
            }
        }
//        System.out.println(Arrays.toString(count));
        for (int i = 0; i < 32; i++) {
            count[i] = count[i] % 3;
        }
//        System.out.println(Arrays.toString(count));
        int res = 0;//[0,0,0,1]=0001
        for (int i = count.length - 1; i >= 0; i--) {
            res <<= 1;
            res |= count[i];
        }
        return res;
    }
}
(2)简单做法 HashMap
import java.util.HashMap;

public class Offer56_2 {
    public static void main(String[] args) {
        int[] nums = new int[]{9, 1, 7, 9, 7, 9, 7};
        System.out.println(singleNumber(nums));
    }

    // 在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
    public static int singleNumber(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int count = map.getOrDefault(nums[i], 0) + 1;
            map.put(nums[i], count);
        }
        // 遍历 map 的 values
        int res = -1;
        for (Integer key : map.keySet()) {
            if (map.get(key) == 1) {
                res = key;
                break;
            }
        }
        return res;
    }
}
剑指 Offer 64 .求1+2+…+n

思考:

// 但是这里使用了if,不满足题意。要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
public class Offer64 {
    public static void main(String[] args) {
        int n = 9;
        System.out.println(sumNums(n));
    }
    public static int sumNums(int n) {
        //终止条件
        if(n==1){
            return 1;
        }
        n+=sumNums(n-1);
        return n;
    }
}
// 使用 &&
public class Offer64_1 {
    public static void main(String[] args) {
        int n = 9;
        System.out.println(sumNums(n));
    }

    static int res = 0;
    static boolean flag;

    public static int sumNums(int n) {
        System.out.println("flag=" + (n > 1 && sumNums(n - 1) > 0) + "  n=" + n);
        flag = n > 1 && sumNums(n - 1) > 0;
        res += n;
        return res;
    }
}

剑指 Offer 65 .不用加减乘除做加法

思考:虽然系统分类是简单。但并不容易吧。

image-20210131221309300

image-20210131221321937

image-20210131221337755

public class Offer65 {
    public static void main(String[] args) {
        int a = 20;
        int b = 17;
        System.out.println(add(a, b));
    }

    public static int add(int a, int b) {
        if (b == 0) {
            return a;
        }

        // 转换成非进位和 + 进位
        return add(a ^ b, (a & b) << 1);
    }
}

剑指 Offer 66 .构建乘积数组

思考:

自己手写了一张笔记,还需要上传。

image-20210201095212989

image-20210201095226818

(1)在不使用除法的情况下
import java.util.Arrays;

public class Offer66_1 {
    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4, 5};
        System.out.println(Arrays.toString(constructArr(nums)));
    }

    public static int[] constructArr(int[] a) {
        //输入判断
        if (a == null || a.length == 0) {
            return new int[0];
        }
        int len = a.length;
        int[] res = new int[len];
        int[] left = new int[len];
        int[] right = new int[len];
        left[0] = right[len - 1] = 1;
        for (int i = 1; i < len; i++) {
            left[i] = left[i - 1] * a[i - 1];
        }
        for (int i = len - 2; i >= 0; i--) {
            right[i] = right[i + 1] * a[i + 1];
        }
        for (int i = 0; i < len; i++) {
            res[i] = left[i] * right[i];
        }
        return res;
    }
}
(2)在使用除法的情况下
import java.util.Arrays;

public class Offer66 {
    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4, 5};
        System.out.println(Arrays.toString(constructArr(nums)));
    }

    public static int[] constructArr(int[] a) {
        int[] res = new int[a.length];
        int value = 1;
        for (int i = 0; i < a.length; i++) {
            value *= a[i];
        }
        for (int i = 0; i < res.length; i++) {
            res[i] = value / a[i];
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值