6.位运算
6.1二进制和位的概念
1)二进制和位的概念 2)正数怎么用二进制表达 3)负数怎么用二进制表达 4)打印二进制;直接定义二进制、十六进制的变量 5)常见的位运算(I、&、^、~、 << 、>>、>>>) 6)解释打印二进制的函数 -7)注意|、&是位运算或、位运算与;11、&&是逻辑或、逻辑与,两者是有区别的 8)相反数 取反加1 9)整数最小值的特殊性(取绝对值还是自己) 10)为什么这么设计二进制(为了加法的逻辑是一套逻辑,没有条件转移),那么为啥加法逻辑如此重要呢? 11)关于溢出(自己确保自己的调用所得到的结果不会溢出,一定是自己确保的,计算机不会给你做检查) 12)位运算玩法很多很多,特别是异或运算(后面的课会详细讲述)、如何用位运算实现加减乘除(后面的课会详细讲述)
6.2异或运算的骚操作
异或运算性质
1)异或运算就是无进位相加 2)异或运算满足交换律、结合律,也就是同一批数字,不管异或顺序是什么,最终的结果都是一个
-
0^n=n,n^n=0 4)整体异或和如果是x,整体中某个部分的异或和如果是y,那么剩下部分的异或和是x^y
这些结论最重要的就是1)结论,所有其他结论都可以由这个结论推论得到
其中第4)相关的题目最多,利用区间上异或和的性质
Nim博弈也是和异或运算相关的算法,将在后续【必备】课程里讲到
public class No136 { //给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 //你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。 public int singleNumber(int[] nums) { int m = 0; for (int i:nums){ m=m^i; } return m; } }
public class No137 { public static void main(String[] args) { System.out.println(singleNumber(new int[]{2, 2, 3, 2})); } //给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 //你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。 public static int singleNumber(int[] nums) { int [] count = new int[32]; for (int i:nums){ for (int j=31;j>=0;j--){ if ((i&(1<<j))!=0){ count[j]++; } } } int ans = 0; for (int i=0;i<count.length;i++){ if (count[i]!=0&&(count[i]%3!=0)){ ans+=1<<i; } } return ans; } }
public class No260 { public static void main(String[] args) { for (int i : singleNumber(new int[]{1, 2, 1, 3, 2, 5})) { System.out.println(i); } } //给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。 // //你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。 public static int[] singleNumber(int[] nums) { int ero = 0; //假设a和b为只出现一次的数字 for (int i:nums){ ero = ero^i; } //得到的结果必然是 a^b //拿到最有ero最右边的1的位置 int ero2 =ero&(~ero+1); //a和b中必有一个数在ero最右边为0或1 //选取为0的全部异或出来必然得到a或b int ero3 = 0; for (int i:nums){ if ((i&ero2)==0){ ero3=i^ero3; } } return new int[]{ero^ero3,ero3}; } }
public class No268 { //给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 public int missingNumber(int[] nums) { int ero = 0; for(int i=0;i<nums.length;i++){ ero=ero^i; } int ero1 = 0; for(int i:nums){ ero1= ero1 ^ i; } return ero^ero1; } }
6.2位运算骚操作
//给你两个整数 left 和 right ,表示区间 [left, right] ,返回此区间内所有数字 按位与 的结果(包含 left 、right 端点)。 public static int rangeBitwiseAnd(int left, int right) { while (right>left){ right = right-(right&(~right+1)); } return right; }
//颠倒给定的 32 位无符号整数的二进制位。 //提示: //请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下, // 输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的, // 其内部的二进制表示形式都是相同的。 //示例 1: // //输入:n = 00000010100101000001111010011100 //输出:964176192 (00111001011110000010100101000000) //解释:输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596, // 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。 public static int reverseBits(int n) { int ans = 0; for (int i=0;i<32;i++){ if ((n&(1<<i))!=0){ ans+=1<<(31-i); } } return ans; }
public class No191 { //编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中 //设置位 // 的个数(也被称为汉明重量)。 public int hammingWeight(int n) { int count = 0; for (int i=31;i>=0;i--){ if ((n&(1<<i))!=0){ count++; } } return count; } }
public class No231 { // 给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 // // 如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。 // public boolean isPowerOfTwo(int n) { // if(n<=0){ // return false; // } // int count = 0; // for (int i=31;i>=0;i--){ // if ((n&1<<i)!=0){ // count++; // } // } // if (count>1){ // return false; // } // return true; // } // 给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 // // 如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。 public boolean isPowerOfTwo(int n) { if(n<=0){ return false; } int s = n&(~n+1); return n==s; } }