LeetCode(剑指offer) day1
剑指 Offer II 001. 整数除法
题目描述:
给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。
注意:
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1
思路:
此题思路在于将除法转换为减法,并转换为减法的思路上进行优化,其减法的思路在于可以对减数不断的乘以2来处理,有点类似于二分法的思想,达到一个临界值,便可开始下一次计算,值得注意的是,此题要求只能为32位有符号数,可以将被除数和除数转换为负数进行处理,避免其数据溢出。而在这个乘以2处理的方式上,可以采用位运算来优化效率。
未利用位运算进行优化
public int divide(int a, int b) {
if(a == Integer.MIN_VALUE && b == -1){
return Integer.MAX_VALUE;
}
if(a == 0){
return 0;
}
if(b == 1){
return a;
}
boolean flag = false;
if((a>0 && b>0)||(a<0 && b<0)){
flag = true;
}
int left = a > 0 ? -a : a;
int right = b > 0 ? -b : b;
if(left > right){
return 0;
}
int res = 0;
while (left <= right){
int value = right;
int k = 1;
while (left < value + value){
value += value;
k += k;
}
left -= value;
res += k;
}
return flag ? res : -res;
}
利用位运算
public int divide(int a, int b) {
if(a == Integer.MIN_VALUE && b == -1){
return Integer.MAX_VALUE;
}
if(a == 0){
return 0;
}
if(b == 1){
return a;
}
boolean flag = false;
if((a>0 && b>0)||(a<0 && b<0)){
flag = true;
}
int left = a > 0 ? -a : a;
int right = b > 0 ? -b : b;
if(left > right){
return 0;
}
int res = 0;
int count = 31;
while (left <= right){
while (left >> count >= right ){
count--;
}
left -= right << count;
res += 1 << count;
}
return flag ? res : -res;
}
位运算
运算符:
& 与 两个位都为1时,结果才为1。
| 或 两个位都为0时,结果才为0。
<< 左移 各二进位全部左移若干位,高位丢弃,低位补0。
>> 右移 各二进位全部右移若干位,对无符号数,高位补0,有符号数,右移补1
^ 异或 两个位相同为0,相异为1。
位运算高级操作:
去掉最后一位 0100−>0010 x>>1
在最后加一个0 0100−>1000 x<<1
在最后加一个1 0100−>1001 x<<1+1
将最后一位变为1 0100−>0101 x|1
将最后一位变为0 0101−>0100,这里实际上就是先确保最低位变为1,再减去1。 x|1-1
最后一位取反 0100−>0101 ,利用异或性质,其中除最后一位其余不变 x∧1
把右数的第k位变为1 0001−>1001,k=4 x|(1<<(k-1))
把右数的第k位变为0 1001−>0001,k=4,这个操作实际上就是先得到了1000,然后取反得到0111,最后利用按位与的性质其余位不变,最高位为0 x&∼(1<<(k−1))
位运算应用:
位运算实现乘除法
将x左移一位实现× 2,将x右移一位实现÷ 2
a < < 1 ≡ a ∗ 2
a > > 1 ≡ a / 2
位运算判断奇偶数
我们知道,在二进制中,最低位决定了是奇数还是偶数,所以我们可以提取出最低位的值,即与1相与即可实现目的,为0则是偶数,为1则是奇数。
位运算统计二进制数1的个数
int count(int x){
int cnt = 0;
while(x){
x = x & (x - 1);
cnt ++;
}
return cnt;
}
此处只列举了一些常用的,更多更细请点击下方链接
剑指 Offer II 002. 二进制加法
题目描述:给定两个 01 字符串 a
和 b
,请计算它们的和,并以二进制字符串的形式输出。
输入为 非空 字符串且只包含数字 1
和 0
。
思路:用一个中间变量来存储其进位,通过对其进行模2运算获取当前位,进行除2运算获取进位。
public String addBinary(String a, String b) {
int aLen = a.length();
int bLen = b.length();
StringBuilder str = new StringBuilder();
int i = aLen - 1;
int j = bLen - 1;
int carry = 0;
while (carry == 1 || i >= 0 || j >= 0){
if(i>=0 && a.charAt(i) == '1'){
carry++;
}
if(j>=0 && b.charAt(j) == '1'){
carry++;
}
str.append(carry%2);
carry/=2;
i--;
j--;
}
return str.reverse().toString();
}
剑指 Offer II 003. 前 n 个数字二进制中 1 的个数
题目描述:给定一个非负整数 n
,请计算 0
到 n
之间的每个数字的二进制表示中 1 的个数,并输出一个数组。
思路: 此题主要是利用位运算的性质,即通过n与n-1相与可以消去一个1,当其不能进行消去的时候,便已经统计到其1的个数了。
public int[] countBits(int n) {
int[] arr = new int[n + 1];
for (int i=0 ; i<=n ; i++){
int index = 0;
int j = i;
while (j > 0){
j &= j-1;
index++;
}
arr[i] = index;
}
return arr;
}