持续更新中ing~
😀😀😀 P88 指的是在LeetCode中的题号为88
P88—合并两个有序数组
import java.util.Arrays;
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i : nums2) { // int i = m - 1, j = n - 1, k = m + n - 1;
nums1[m++] = i; // 从后往前比较并插入元素
} // while (i >= 0 && j >= 0) {
Arrays.sort(nums1); // if (nums1[i] > nums2[j]) {
// nums1[k--] = nums1[i--];
// }else {
// nums1[k--] = nums2[j--];
// }
// }
// 如果nums2还有剩余元素,直接插入到nums1的前面
// while (j >= 0) {
// // 添加范围判断
// if (k >= 0) {
// nums1[k--] = nums2[j--i];
// }
// }
}
}
Arrays.sort();
对数组进行排序
P479–最大回文数乘机
- 构造回文数的方法
long x = i;
long y = i;
//构造回文数 97 97 -> 9779
while (y > 0) {
x = x * 10 + (y % 10);
y /= 10;
}
假如x=y=97
,经过第一轮 x=970 + 97 % 10 =970 +7=977
,y=97/10=9
;
经过第二轮 x=9770 + 9 % 10=9770+9=9779
;
x % 10
: 取到的x
的(从后往前)最后一位
x / 10
:去掉x
的最后一位
- 判断
x
是否有因数
// 从最大值开始逐个尝试可能的因数
for (long j = max; j * j >= x; j--) {
if (x % j == 0) {
return (int) (x % 1337);
}
}
如果 x
对一个数取余(%
),结果为 0,则该数为 x
的因数
🌕P564–寻找最近的回文数 30.4%
答案可以做出来了,不过一直超时 下次TODO吧
class Solution {
public String nearestPalindromic(String n) {
long num = Long.parseLong(n);
for (long i = 1;; i++) {
if (isPalindrome(num - i))
return "" + (num - i);
if (isPalindrome(num + i))
return "" + (num + i);
}
}
boolean isPalindrome(long x) {
long t = x, rev = 0;
while (t > 0) {
rev = 10 * rev + t % 10;
t /= 10;
}
return rev == x;
}
}
P231–2的幂
如果存在一个整数 x
使得 n == 2x
,则认为 n
是 2 的幂次方。
if (n <= 0) {
return false;
} else {
return (n & (n - 1)) == 0;
}
-
当一个数是2的幂时,它的二进制表示中只有一个1,其余位都是0
- 2的幂的二进制表示为
1000...
,而2的幂减去1的结果为0111...
。通过进行按位与运算(n & (n - 1))
,如果结果为0,则说明n
的二进制表示中只有一个1,即n
是2的幂。
2:0010 ;4:0100 ; 8:1000 ;16:0001 0000 ;32:0010 0000
1:0001 ;3:0011 ; 7:0111 ;15:1111 ; 31:0001 1111
2的幂次方,那么它的二进制减去1后,原来的最高位的1会变成0,而其他位保持不变。将原数和减去1后的数进行按位与操作,结果应该是0。
- 2的幂的二进制表示为
-
例子:
假设
n = 8
,它的二进制表示是1000
。现在,计算n - 1
得到7
的二进制表示是0111
。然后, 进行按位与运算
n & (n - 1)
得到1000 & 0111 = 0000
,结果为0,说明n
是2的幂。同样,如果
n = 6
,其二进制表示为110
,计算n - 1
得到5
的二进制表示是101
。 进行按位与运算
110 & 101
得到100
,结果非零,说明n
不是2的幂。
PS: 按位与运算 ( & )
按位与是一种二进制运算,它对两个二进制数的对应位进行逻辑与操作。按位与的规则如下:
- 如果两个对应位都为1,则结果位为1。
- 如果其中任意一个对应位为0,则结果位为0。
下面是一个简单的按位与示例:
1010 (十进制:10) & 1101 (十进制:13) ------- 1000 (十进制:8)
P342–4的幂
在判断是否是2的幂次方的基础上再加以判断是否是4的幂次方
return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
(n & 0xaaaaaaaa) == 0
:将n
与十六进制数 0xAAAAAAAA 进行按位与运算
0100 & 1010 1010 1010 1010 1010 1010 1010 1010 -------------------------------------------------------- 0000
1010 1010 1010 1010 1010 1010 1010 1010
| | | | | | |
64 16 4检查n与n/4、n/16、n/64等进行按位与操作的结果是否都为0。如果都为0,说明n是4的幂次方。
P326–3的幂
传入的值n
是一个整数类型 -2^31 <= n <= 2^31 - 1
,即 2147483648<=n<=2147483647
在这个整数范围内,最多可达到3^19
,所以
- 如果
n<=0
,直接返回flase
- 如果
n>0
,判断n
是否能被3^19
取余是否为0,为0则返回true;反之,返回false
return n>0 && Math.pow(3,19) % n ==0;
P504–七进制数
给定一个整数
num
,将其转化为 7 进制,并以字符串形式输出。
思路:
- 先判断是否为
0
,若为0则直接返回字符串0
; - 判断
num<0
,若小于零则,boolean isNegivate = false;
反之,为true; - 构造一个
StringBuilder result对象
,迭代除以7来将一个整数转化为7进制,并将每一步的余数拼接起来,循环while(num > 0)
,每次除以7,结果插入result
的头部,来构造七进制数,然后num /=7
,接着下一次循环
public String convertToBase7(int num) {
if (num == 0){
return "0";
}
StringBuilder result = new StringBuilder();
boolean isNagetive = num < 0;
num=Math.abs(num);
while(num > 0){
result.insert(0,num % 7); //头部插入余数
num /= 7;
}
if (isNagetive){
result.insert(0,"-");
}
return result.toString();
}
P263–丑数
丑数 就是只包含质因数
2
、3
和5
的正整数
public boolean isUgly(int n) { if(n<=0){ return false; } while (n%5==0){ n/=5; } while (n%3==0){ n/=3; } while (n%2==0){ n/=2; } return n==1; }
- 先把能除以5除尽,再把除以3除尽,最后除以2除尽,如果最后
n==1
了,则代表,n
的质数只有2、3、5
;
P190–点到二进制数
颠倒给定的 32 位无符号整数的二进制位。
public int reverseBits(int n) {
int ans = 0;
for (int bitsSize = 31;n != 0;n = n >>>1,bitsSize--){ // 每次 n >>>1,无符号右移1位
ans += (n & 1) << bitsSize; //将原来最后一位移到对应的前面
}
return ans;
}
思路:
- 取当前 n 的最后一位:
n & 1
- 将最后一位移动到对应位置,第一次为 31 位,第二次是 30 位,即:31、30、29… 1、0,写作代码
bit << 31
; - 退出条件,二进制反转时,如果剩余位数全位 0,则可以不用再反转。
位移运算
PS:Java的整数类型都是有符号的
对于有符号整数,Java使用了一种叫做二进制补码的方法来表示负数。在这种方法中,最高位(也就是最左边的位)被用作符号位。如果符号位是0,那么这个数就是正数;如果符号位是1,那么这个数就是负数。对于负数,其余的位表示这个数的绝对值的二进制补码。
二进制补码是一种用于表示负整数的方法。对于一个负数,我们首先取它的绝对值,然后将这个绝对值转换为二进制形式,接着对这个二进制数取反(也就是将所有的1变为0,所有的0变为1),最后再加1。得到的结果就是这个负数的二进制补码。
例如,我们来看一下如何得到-5的二进制补码:
- 首先,我们取5的绝对值,得到5
- 然后,我们将5转换为二进制形式,得到
0101
- 接着,我们对
0101
取反,得到1010
- 最后,我们对
1010
加1,得到1011
所以,-5的二进制补码就是
1011
- 左移带符号运算符 (
<<
):- 语法:
a << b
- 将
a
的二进制表示向左移动b
位,右侧用零填充。相当于将a
乘以 2 的b
次方。 - 例子:
5 << 2
的结果是20
,因为二进制表示101
左移两位变为10100
,即 20。
- 语法:
- 右移带符号运算符 (
>>
):- 语法:
a >> b
- 将
a
的二进制表示向右移动b
位,左侧用原来的最高位填充。如果a
是正数,则用零填充;如果a
是负数,则用一填充。 - 例子:
-8 >> 2
的结果是-2
,因为二进制表示-8
是11111111111111111111111111111000
,右移两位变为11111111111111111111111111111110
,即 -2。 - 最高位为符号位,带符号位移,则最高位仍和原来的一样,即保留原来符号
- 语法:
- 右移无符号运算符 (
>>>
):- 语法:
a >>> b
- 将
a
的二进制表示向右移动b
位,左侧用零填充。不考虑正负号,始终在左侧填充零。 - 例子:
-8 >>> 2
的结果是1073741822
,因为二进制表示-8
右移两位变为00111111111111111111111111111110
,即 1073741822。
- 语法:
P191–位1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
public int hammingWeight(int n) {
return Integer.bitCount(n);
}
🗡 question: 直接调用Integer.bitCount(n);
方法,返回一个整数n的二进制表示中1的个数
P476–数字的补数
对整数的二进制表示取反(
0
变1
,1
变0
)后,再转换为十进制表示,可以得到这个整数的补数。
- 例如,整数
5
的二进制表示是"101"
,取反后得到"010"
,再转回十进制表示得到补数2
。给你一个整数
num
,输出它的补数。
- 暴力解
public int findComplement(int num) {
int count = 0;
int n = num;
while (n>0){
n = n>>1; // 将 n 右移一位
count++; // 递增位数计数
}
int mask = (1 << count) - 1; //构造一个全1的数
return num ^ mask;
}
思路:
-
因为要得到补数,(
0
变1
,1
变0
),而一个二进制数对于一个全是1的二进制数做异或操作
^
,可以实现(0
变1
,1
变0
),所以先构造出和原来二进制数位数一样的全是一的二进制数 -
通过while循环,每次都将一个n右移一位,count++,当
n
变为0时,循环结束,此时count的值就是二进制数的位数 -
构造位数相等的全是一的二进制数mask,将 1 左移count位,得到的是头部位 1,后面有count个 0 的二进制数,所以要 - 1,得到count位全是 1 的二进制数
int count = 3; int result = (1 << count) - 1;
(1 << count) ------》 1 0 0 0
(1 << count) - 1 -------》 1 1 1
-
最后
num ^ mask;
做异或操作,得到的是一个二进制数,return直接返回十进制数;
2.内置函数
public int findComplement(int num) {
int mask = Integer.highestOneBit(num) - 1;
return num ^ mask;
}
思路:
-
确定整数的最高有效位(最左侧的1的位)的位置。
-
构建一个具有相同位数的掩码,将该位及其右边的所有位都设置为1。
这里,
Integer.highestOneBit(num)
返回的是num
中最左侧的1的位置,然后我们减去1,得到的掩码将最左侧的1及其右边的所有位都设置为1。最后,执行异或操作得到补数。
P461–汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数
x
和y
,计算并返回它们之间的汉明距离。
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
思路:
- 对比的是两者二进制数,位不相同的个数,可以直接用异或操作
^
来得出不相同的位数 - 异或操作
^
得出来的是,如果两个对应的二进制位相同,则结果为0;如果两个对应的二进制位不同,则结果为1,所以最后直接统计1的个数就是不同位的个数;
异或操作 ^
异或(XOR)操作是二进制运算中常见的一种。在异或操作中,如果两个对应的二进制位相同,则结果为0;如果两个对应的二进制位不同,则结果为1。
以下是异或操作的真值表:
vbnetCopy code0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
在 Java 中,异或操作使用 ^
运算符表示。在二进制操作中,对于两个数 a
和 b
,a ^ b
将对 a
和 b
的每一位进行异或操作。例如:
javaCopy codeint a = 5; // 二进制表示为 101
int b = 3; // 二进制表示为 011
int result = a ^ b; // 异或操作,结果为 110
System.out.println(Integer.toBinaryString(result)); // 输出二进制表示为 110
在上述代码中,a
和 b
进行异或操作后,得到的结果是 110
,这是因为对应的二进制位上有两个位不同。
P477–汉明距离总和
两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。
给你一个整数数组
nums
,请你计算并返回nums
中任意两个数之间 汉明距离的总和 。
public int totalHammingDistance(int[] nums) {
int totalDistance = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j < nums.length; j++) {
totalDistance +=Integer.bitCount(nums[i]^nums[j]);
}
}
return totalDistance;
}
思路:
- 给了我一个数组,求数组中每个数之间的汉明距离,则实现每两个数依次握手一次即可;
- 从第一个数开始,依次向后面的数握手,然后再从第二个数依次向后面握手……
P693交替位二进制数
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。
public boolean hasAlternatingBits(int n) {
while (n!= 0) {
if ((n & 1) == 1) {
if ((n & 2) == 2) {
return false;
}
} else {
if ((n & 2) == 0) {
return false;
}
}
n = n >> 1;
}
return true;
}
思路:
- 通过循环逐位比较,每次右移一次n
- 每一层循环,只判断最后一位和倒数第二位是否不相同
- 先判断
n & 1 == 1
是否等于1,等于1代表,n的尾数为1,然后再判断倒数第二位(n & 2) == 2
是否为2,为2的意思值为10
,所以本次循环尾数和次尾数是不相同的,开启下一次循环。
按位与操作 &
n & 1
是按位与操作,它会将 n
和1的二进制表示进行按位与操作。
对于 n=4
,二进制表示是 100
,而1的二进制表示是 001
。
按位与操作的规则是,如果对应位都是1,则结果为1;否则,结果为0。
所以,4 & 1
的按位与操作过程如下:
100 (4的二进制表示)
& 001 (1的二进制表示)
-------
000 (按位与的结果,即0)
因此,n & 1
的结果为0。
🌕 P393–UTF-8编码验证 43.7%
给定一个表示数据的整数数组
data
,返回它是否为有效的 UTF-8 编码。UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:
- 对于 1 字节 的字符,字节的第一位设为 0 ,后面 7 位为这个符号的 unicode 码。
- 对于 n 字节 的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为 0 ,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。
这是 UTF-8 编码的工作方式:
Number of Bytes | UTF-8 octet sequence | (binary) --------------------+--------------------------------------------- 1 | 0xxxxxxx 2 | 110xxxxx 10xxxxxx 3 | 1110xxxx 10xxxxxx 10xxxxxx 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
x
表示二进制形式的一位,可以是0
或1
。**注意:**输入是整数数组。只有每个整数的 最低 8 个有效位 用来存储数据。这意味着每个整数只表示 1 字节的数据。
public boolean validUtf8(int[] data) {
int i = 0;
while (i < data.length){
int numBytes = getNumBytes(data[i]); // 获取当前字符的字节数
if (numBytes < 1 || numBytes > 4 || i+numBytes > data.length){ //还要判断字符是否越界
return false;
}
for (int j = 1;j < numBytes;j++){
if ((data[i+j] & 0xC0) != 0x80){
return false;
}
}
i += numBytes; // 移动到下一个字符
}
return true;
}
private int getNumBytes(int b){
if ((b & 0x80) == 0){
return 1;
}else if ((b & 0xE0) == 0xC0){
return 2;
}else if ((b & 0xF0) == 0xE0){
return 3;
}else if ((b & 0xF8) == 0xF0){
return 4;
}else{
return -1;
}
}
思路:
-
先弄清什么是UTF-8编码,因为传进来的是一个数组,必定要实现数组的遍历,这里我们用
while
循环条件为i<data.length
,而每次遍历都需要干什么呢? -
首先UTF-8编码,字节数的取值范围为
[1,4]
,所以他传递过来的数组不知道有几个UTF-8数,我们可以根据遍历的顺序来逐个判断,因为UTF-8编码可以根据第一个字节中从头到尾有多少个1来判断,第一个数占了多少个字节,(几个1就是几个字节,如果为0,则为1个字节),我们就编写一个getNumBytes
函数来统计有多少个字节; -
根据前面判断出来占用
numBytes
个字节数,来进行检查该数之后(他的剩下的字节部分)是否满足后面字节的前两位一律设为 10
,利用for
循环逐个利用(data[i+j] & 0xC0) != 0x80
来判断前两位是否为10
0xC0
(11000000),0x80
(10000000) -
如果遇到了后面的不满足
前两位为10
,则return false
不满足退出;满足的话就继续验证下一个数i += numBytes;
,直到都满足return true
### P88—合并两个有序数组
import java.util.Arrays;
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i : nums2) { // int i = m - 1, j = n - 1, k = m + n - 1;
nums1[m++] = i; // 从后往前比较并插入元素
} // while (i >= 0 && j >= 0) {
Arrays.sort(nums1); // if (nums1[i] > nums2[j]) {
// nums1[k--] = nums1[i--];
// }else {
// nums1[k--] = nums2[j--];
// }
// }
// 如果nums2还有剩余元素,直接插入到nums1的前面
// while (j >= 0) {
// // 添加范围判断
// if (k >= 0) {
// nums1[k--] = nums2[j--i];
// }
// }
}
}
Arrays.sort();
对数组进行排序
P479–最大回文数乘机
- 构造回文数的方法
long x = i;
long y = i;
//构造回文数 97 97 -> 9779
while (y > 0) {
x = x * 10 + (y % 10);
y /= 10;
}
假如x=y=97
,经过第一轮 x=970 + 97 % 10 =970 +7=977
,y=97/10=9
;
经过第二轮 x=9770 + 9 % 10=9770+9=9779
;
x % 10
: 取到的x
的(从后往前)最后一位
x / 10
:去掉x
的最后一位
- 判断
x
是否有因数
// 从最大值开始逐个尝试可能的因数
for (long j = max; j * j >= x; j--) {
if (x % j == 0) {
return (int) (x % 1337);
}
}
如果 x
对一个数取余(%
),结果为 0,则该数为 x
的因数
🌕P564–寻找最近的回文数 30.4%
答案可以做出来了,不过一直超时 下次TODO吧
class Solution {
public String nearestPalindromic(String n) {
long num = Long.parseLong(n);
for (long i = 1;; i++) {
if (isPalindrome(num - i))
return "" + (num - i);
if (isPalindrome(num + i))
return "" + (num + i);
}
}
boolean isPalindrome(long x) {
long t = x, rev = 0;
while (t > 0) {
rev = 10 * rev + t % 10;
t /= 10;
}
return rev == x;
}
}
P231–2的幂
如果存在一个整数 x
使得 n == 2x
,则认为 n
是 2 的幂次方。
if (n <= 0) {
return false;
} else {
return (n & (n - 1)) == 0;
}
-
当一个数是2的幂时,它的二进制表示中只有一个1,其余位都是0
- 2的幂的二进制表示为
1000...
,而2的幂减去1的结果为0111...
。通过进行按位与运算(n & (n - 1))
,如果结果为0,则说明n
的二进制表示中只有一个1,即n
是2的幂。
2:0010 ;4:0100 ; 8:1000 ;16:0001 0000 ;32:0010 0000
1:0001 ;3:0011 ; 7:0111 ;15:1111 ; 31:0001 1111
2的幂次方,那么它的二进制减去1后,原来的最高位的1会变成0,而其他位保持不变。将原数和减去1后的数进行按位与操作,结果应该是0。
- 2的幂的二进制表示为
-
例子:
假设
n = 8
,它的二进制表示是1000
。现在,计算n - 1
得到7
的二进制表示是0111
。然后, 进行按位与运算
n & (n - 1)
得到1000 & 0111 = 0000
,结果为0,说明n
是2的幂。同样,如果
n = 6
,其二进制表示为110
,计算n - 1
得到5
的二进制表示是101
。 进行按位与运算
110 & 101
得到100
,结果非零,说明n
不是2的幂。
PS: 按位与运算 ( & )
按位与是一种二进制运算,它对两个二进制数的对应位进行逻辑与操作。按位与的规则如下:
- 如果两个对应位都为1,则结果位为1。
- 如果其中任意一个对应位为0,则结果位为0。
下面是一个简单的按位与示例:
1010 (十进制:10) & 1101 (十进制:13) ------- 1000 (十进制:8)
P342–4的幂
在判断是否是2的幂次方的基础上再加以判断是否是4的幂次方
return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
(n & 0xaaaaaaaa) == 0
:将n
与十六进制数 0xAAAAAAAA 进行按位与运算
0100 & 1010 1010 1010 1010 1010 1010 1010 1010 -------------------------------------------------------- 0000
1010 1010 1010 1010 1010 1010 1010 1010
| | | | | | |
64 16 4检查n与n/4、n/16、n/64等进行按位与操作的结果是否都为0。如果都为0,说明n是4的幂次方。
P326–3的幂
传入的值n
是一个整数类型 -2^31 <= n <= 2^31 - 1
,即 2147483648<=n<=2147483647
在这个整数范围内,最多可达到3^19
,所以
- 如果
n<=0
,直接返回flase
- 如果
n>0
,判断n
是否能被3^19
取余是否为0,为0则返回true;反之,返回false
return n>0 && Math.pow(3,19) % n ==0;
P504–七进制数
给定一个整数
num
,将其转化为 7 进制,并以字符串形式输出。
思路:
- 先判断是否为
0
,若为0则直接返回字符串0
; - 判断
num<0
,若小于零则,boolean isNegivate = false;
反之,为true; - 构造一个
StringBuilder result对象
,迭代除以7来将一个整数转化为7进制,并将每一步的余数拼接起来,循环while(num > 0)
,每次除以7,结果插入result
的头部,来构造七进制数,然后num /=7
,接着下一次循环
public String convertToBase7(int num) {
if (num == 0){
return "0";
}
StringBuilder result = new StringBuilder();
boolean isNagetive = num < 0;
num=Math.abs(num);
while(num > 0){
result.insert(0,num % 7); //头部插入余数
num /= 7;
}
if (isNagetive){
result.insert(0,"-");
}
return result.toString();
}
P263–丑数
丑数 就是只包含质因数
2
、3
和5
的正整数
public boolean isUgly(int n) { if(n<=0){ return false; } while (n%5==0){ n/=5; } while (n%3==0){ n/=3; } while (n%2==0){ n/=2; } return n==1; }
- 先把能除以5除尽,再把除以3除尽,最后除以2除尽,如果最后
n==1
了,则代表,n
的质数只有2、3、5
;
P190–点到二进制数
颠倒给定的 32 位无符号整数的二进制位。
public int reverseBits(int n) {
int ans = 0;
for (int bitsSize = 31;n != 0;n = n >>>1,bitsSize--){ // 每次 n >>>1,无符号右移1位
ans += (n & 1) << bitsSize; //将原来最后一位移到对应的前面
}
return ans;
}
思路:
- 取当前 n 的最后一位:
n & 1
- 将最后一位移动到对应位置,第一次为 31 位,第二次是 30 位,即:31、30、29… 1、0,写作代码
bit << 31
; - 退出条件,二进制反转时,如果剩余位数全位 0,则可以不用再反转。
位移运算
PS:Java的整数类型都是有符号的
对于有符号整数,Java使用了一种叫做二进制补码的方法来表示负数。在这种方法中,最高位(也就是最左边的位)被用作符号位。如果符号位是0,那么这个数就是正数;如果符号位是1,那么这个数就是负数。对于负数,其余的位表示这个数的绝对值的二进制补码。
二进制补码是一种用于表示负整数的方法。对于一个负数,我们首先取它的绝对值,然后将这个绝对值转换为二进制形式,接着对这个二进制数取反(也就是将所有的1变为0,所有的0变为1),最后再加1。得到的结果就是这个负数的二进制补码。
例如,我们来看一下如何得到-5的二进制补码:
- 首先,我们取5的绝对值,得到5
- 然后,我们将5转换为二进制形式,得到
0101
- 接着,我们对
0101
取反,得到1010
- 最后,我们对
1010
加1,得到1011
所以,-5的二进制补码就是
1011
- 左移带符号运算符 (
<<
):- 语法:
a << b
- 将
a
的二进制表示向左移动b
位,右侧用零填充。相当于将a
乘以 2 的b
次方。 - 例子:
5 << 2
的结果是20
,因为二进制表示101
左移两位变为10100
,即 20。
- 语法:
- 右移带符号运算符 (
>>
):- 语法:
a >> b
- 将
a
的二进制表示向右移动b
位,左侧用原来的最高位填充。如果a
是正数,则用零填充;如果a
是负数,则用一填充。 - 例子:
-8 >> 2
的结果是-2
,因为二进制表示-8
是11111111111111111111111111111000
,右移两位变为11111111111111111111111111111110
,即 -2。 - 最高位为符号位,带符号位移,则最高位仍和原来的一样,即保留原来符号
- 语法:
- 右移无符号运算符 (
>>>
):- 语法:
a >>> b
- 将
a
的二进制表示向右移动b
位,左侧用零填充。不考虑正负号,始终在左侧填充零。 - 例子:
-8 >>> 2
的结果是1073741822
,因为二进制表示-8
右移两位变为00111111111111111111111111111110
,即 1073741822。
- 语法:
P191–位1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
public int hammingWeight(int n) {
return Integer.bitCount(n);
}
🗡 question: 直接调用Integer.bitCount(n);
方法,返回一个整数n的二进制表示中1的个数
P476–数字的补数
对整数的二进制表示取反(
0
变1
,1
变0
)后,再转换为十进制表示,可以得到这个整数的补数。
- 例如,整数
5
的二进制表示是"101"
,取反后得到"010"
,再转回十进制表示得到补数2
。给你一个整数
num
,输出它的补数。
- 暴力解
public int findComplement(int num) {
int count = 0;
int n = num;
while (n>0){
n = n>>1; // 将 n 右移一位
count++; // 递增位数计数
}
int mask = (1 << count) - 1; //构造一个全1的数
return num ^ mask;
}
思路:
-
因为要得到补数,(
0
变1
,1
变0
),而一个二进制数对于一个全是1的二进制数做异或操作
^
,可以实现(0
变1
,1
变0
),所以先构造出和原来二进制数位数一样的全是一的二进制数 -
通过while循环,每次都将一个n右移一位,count++,当
n
变为0时,循环结束,此时count的值就是二进制数的位数 -
构造位数相等的全是一的二进制数mask,将 1 左移count位,得到的是头部位 1,后面有count个 0 的二进制数,所以要 - 1,得到count位全是 1 的二进制数
int count = 3; int result = (1 << count) - 1;
(1 << count) ------》 1 0 0 0
(1 << count) - 1 -------》 1 1 1
-
最后
num ^ mask;
做异或操作,得到的是一个二进制数,return直接返回十进制数;
2.内置函数
public int findComplement(int num) {
int mask = Integer.highestOneBit(num) - 1;
return num ^ mask;
}
思路:
-
确定整数的最高有效位(最左侧的1的位)的位置。
-
构建一个具有相同位数的掩码,将该位及其右边的所有位都设置为1。
这里,
Integer.highestOneBit(num)
返回的是num
中最左侧的1的位置,然后我们减去1,得到的掩码将最左侧的1及其右边的所有位都设置为1。最后,执行异或操作得到补数。
P461–汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数
x
和y
,计算并返回它们之间的汉明距离。
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
思路:
- 对比的是两者二进制数,位不相同的个数,可以直接用异或操作
^
来得出不相同的位数 - 异或操作
^
得出来的是,如果两个对应的二进制位相同,则结果为0;如果两个对应的二进制位不同,则结果为1,所以最后直接统计1的个数就是不同位的个数;
异或操作 ^
异或(XOR)操作是二进制运算中常见的一种。在异或操作中,如果两个对应的二进制位相同,则结果为0;如果两个对应的二进制位不同,则结果为1。
以下是异或操作的真值表:
vbnetCopy code0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
在 Java 中,异或操作使用 ^
运算符表示。在二进制操作中,对于两个数 a
和 b
,a ^ b
将对 a
和 b
的每一位进行异或操作。例如:
javaCopy codeint a = 5; // 二进制表示为 101
int b = 3; // 二进制表示为 011
int result = a ^ b; // 异或操作,结果为 110
System.out.println(Integer.toBinaryString(result)); // 输出二进制表示为 110
在上述代码中,a
和 b
进行异或操作后,得到的结果是 110
,这是因为对应的二进制位上有两个位不同。
P477–汉明距离总和
两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。
给你一个整数数组
nums
,请你计算并返回nums
中任意两个数之间 汉明距离的总和 。
public int totalHammingDistance(int[] nums) {
int totalDistance = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j < nums.length; j++) {
totalDistance +=Integer.bitCount(nums[i]^nums[j]);
}
}
return totalDistance;
}
思路:
- 给了我一个数组,求数组中每个数之间的汉明距离,则实现每两个数依次握手一次即可;
- 从第一个数开始,依次向后面的数握手,然后再从第二个数依次向后面握手……
P693交替位二进制数
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。
public boolean hasAlternatingBits(int n) {
while (n!= 0) {
if ((n & 1) == 1) {
if ((n & 2) == 2) {
return false;
}
} else {
if ((n & 2) == 0) {
return false;
}
}
n = n >> 1;
}
return true;
}
思路:
- 通过循环逐位比较,每次右移一次n
- 每一层循环,只判断最后一位和倒数第二位是否不相同
- 先判断
n & 1 == 1
是否等于1,等于1代表,n的尾数为1,然后再判断倒数第二位(n & 2) == 2
是否为2,为2的意思值为10
,所以本次循环尾数和次尾数是不相同的,开启下一次循环。
按位与操作 &
n & 1
是按位与操作,它会将 n
和1的二进制表示进行按位与操作。
对于 n=4
,二进制表示是 100
,而1的二进制表示是 001
。
按位与操作的规则是,如果对应位都是1,则结果为1;否则,结果为0。
所以,4 & 1
的按位与操作过程如下:
100 (4的二进制表示)
& 001 (1的二进制表示)
-------
000 (按位与的结果,即0)
因此,n & 1
的结果为0。
🌕 P393–UTF-8编码验证 43.7%
给定一个表示数据的整数数组
data
,返回它是否为有效的 UTF-8 编码。UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:
- 对于 1 字节 的字符,字节的第一位设为 0 ,后面 7 位为这个符号的 unicode 码。
- 对于 n 字节 的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为 0 ,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。
这是 UTF-8 编码的工作方式:
Number of Bytes | UTF-8 octet sequence | (binary) --------------------+--------------------------------------------- 1 | 0xxxxxxx 2 | 110xxxxx 10xxxxxx 3 | 1110xxxx 10xxxxxx 10xxxxxx 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
x
表示二进制形式的一位,可以是0
或1
。**注意:**输入是整数数组。只有每个整数的 最低 8 个有效位 用来存储数据。这意味着每个整数只表示 1 字节的数据。
public boolean validUtf8(int[] data) {
int i = 0;
while (i < data.length){
int numBytes = getNumBytes(data[i]); // 获取当前字符的字节数
if (numBytes < 1 || numBytes > 4 || i+numBytes > data.length){ //还要判断字符是否越界
return false;
}
for (int j = 1;j < numBytes;j++){
if ((data[i+j] & 0xC0) != 0x80){
return false;
}
}
i += numBytes; // 移动到下一个字符
}
return true;
}
private int getNumBytes(int b){
if ((b & 0x80) == 0){
return 1;
}else if ((b & 0xE0) == 0xC0){
return 2;
}else if ((b & 0xF0) == 0xE0){
return 3;
}else if ((b & 0xF8) == 0xF0){
return 4;
}else{
return -1;
}
}
思路:
-
先弄清什么是UTF-8编码,因为传进来的是一个数组,必定要实现数组的遍历,这里我们用
while
循环条件为i<data.length
,而每次遍历都需要干什么呢? -
首先UTF-8编码,字节数的取值范围为
[1,4]
,所以他传递过来的数组不知道有几个UTF-8数,我们可以根据遍历的顺序来逐个判断,因为UTF-8编码可以根据第一个字节中从头到尾有多少个1来判断,第一个数占了多少个字节,(几个1就是几个字节,如果为0,则为1个字节),我们就编写一个getNumBytes
函数来统计有多少个字节; -
根据前面判断出来占用
numBytes
个字节数,来进行检查该数之后(他的剩下的字节部分)是否满足后面字节的前两位一律设为 10
,利用for
循环逐个利用(data[i+j] & 0xC0) != 0x80
来判断前两位是否为10
0xC0
(11000000),0x80
(10000000) -
如果遇到了后面的不满足
前两位为10
,则return false
不满足退出;满足的话就继续验证下一个数i += numBytes;
,直到都满足return true