2021-11-15剑指offer--位运算

JZ65 不用加减乘除做加法

题目地址
描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

数据范围:两个数都满足 0 ≤ n ≤ 1000
进阶:空间复杂度 O(1),时间复杂度 O(1)

示例1

输入:
1,2
返回值:
3

示例2

输入:
0,0
返回值:
0

解题思路
对于数A(i)(1或0)和B(i)(1或0),A(i)和B(i)都只占一个比特位,因为AB都是int类型,因此i∈[0,31]。
此时A(i)B(i)进行相加:
进位 = A(i)&B(i)
相加后当前位 = A(i)^B(i)
详细可见下表:

A(i)B(i)当前位进位
0000
0110
1010
1101

如上图所示,
(1) 当前位的和值等于 A(i)^B(i)
(2) 进位等于 A(i)&B(i),进位需要加在计算位的前一位,所以左移1位,即A(i)&B(i)<<1
所以规律就是 A+B=A^B+(A&B)<<1;

代码

public class Solution {
    public int Add(int num1,int num2) {
        while(num2!=0){
            int c=(num1 & num2)<<1;
            num1=num1^num2;
            num2=c;
        }
        return num1;
    }
}

JZ15 二进制中1的个数

题目地址
描述
输入一个整数 n ,输出该数32位二进制表示中1的个数。其中负数用补码表示。

数据范围:- 2^{31} <= n <= 2^{31}-1
即范围为:-2147483648<= n <= 2147483647
示例1

输入:
10
返回值:
2
说明:
十进制中10的32位二进制表示为0000 0000 0000 0000 0000 0000 0000 1010,其中有两个1。  

示例2

输入:
-1
返回值:
32
说明:
负数使用补码表示 ,-1的32位二进制表示为1111 1111 1111 1111 1111 1111 1111 1111,其中32个1  

代码
一道简单题,遍历数字的每一个比特位,为1就计数加一。此时要注意右移操作要采用>>>,右移过程中最高位补零。

public class Solution {
    public int NumberOf1(int n) {
        int count=0;
        while(n!=0){
            if((n&1)==1){
                count++;
            }
            n>>>=1;
        }
        return count;
    }
}

JZ16 数值的整数次方

题目地址
描述
实现函数 double Power(double base, int exponent),求base的exponent次方。

注意:

  1. 保证base和exponent不同时为0。
  2. 不得使用库函数,同时不需要考虑大数问题
  3. 有特殊判题,不用考虑小数点后面0的位数。

数据范围:|base|≤100 ,|exponent|≤100 ,保证最终结果一定满足 |val|≤10^4
进阶:空间复杂度 0(1) ,时间复杂度 0(n)

示例1

输入:
2.00000,3
返回值:
8.00000

示例2

输入:
2.10000,3
返回值:
9.26100

示例3

输入:
2.00000,-2
返回值:
0.25000
说明:
2的-2次方等于1/4=0.25 

代码
此处采用两种方法:暴力解法快速幂法

public class Solution {
    public double Power(double base, int exponent) {
        if(exponent<0){
            base=1/base;
            exponent=-exponent;
        }
        return func2(base,exponent);
  }
    //暴力法
    public double func1(double base,int exponent){
        double res=1;
        while(exponent!=0){
            res*=base;
            exponent--;
        }
        return res;
    }
    //快快速幂
    public double func2(double base,int exponent){
        double res=1;
        double tmp=base;
        while(exponent!=0){
            if((exponent&1)==1){
                res*=tmp;
            }
            exponent>>=1;
            tmp*=tmp;
        }
        return res;
    }
}

JZ56 数组中只出现一次的两个数字

题目地址
描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

数据范围:数组长度 2≤n≤1000,数组中每个数的大小 0<val≤1000000
要求:空间复杂度 O(1),时间复杂度 O(n)

提示:输出时按非降序排列。

示例1

输入:
[1,4,1,6]
返回值:
[4,6]
说明:
返回的结果中较小的数排在前面   

示例2

输入:
[1,2,3,3,2,9]
返回值:
[1,9]

解题思路
我们知道,对于异或(^)操作,相同为0,不同为1.
如下所示:

ABA^B
000
011
101
110

据此我们可以得到如下推论
A^A=0
A^0=A
A^ A ^B=B
因此,对于当前题目,设数组为arr,设只出现一次的两个数字分别为a,b。

  1. 遍历数组,对数组元素做异或操作(即arr[0]^ arr[1]^ arr[2] ^ …^arr[arr.length-1]),因为除ab之外,其余均重复出现2次,因此异或后结果为a ^ b
  2. 因为ab互不相等,因此a^b肯定存在某一位为1,比如第5位为1(“第5位为1” 设为条件①
  3. 将数组根据条件①分为两组,一组满足条件①,设该数组为arr1;一组不满足条件①,设该数组为arr2.
  4. 我们可以确定,ab肯定分为不同的两组(因为ab在第5位不相同),对arr1或arr2进行异或操作,就可以获取a或b,之后根据a^b,就可以得出另外一个数。
    代码
import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * @param array int整型一维数组 
     * @return int整型一维数组
     */
    public int[] FindNumsAppearOnce (int[] array) {
        int sum12=0;//sum12 = a^b
        for(int element:array){
            sum12^=element;
        }
        int mostRight1=sum12 & ((~sum12)+1);
        int res1=0;
        for(int element:array){
            if((element&mostRight1)!=0){
                res1^=element;
            }
        }
        int[] res=new int[2];
        int res2=sum12^res1;
        res[0]=res1<res2?res1:res2;
        res[1]=res[0]==res1?res2:res1;
        return res;
    }
}

JZ64 求1+2+3+…+n

题目地址
描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围:0<n≤200
进阶: 空间复杂度 O(1) ,时间复杂度 O(n)

示例1

输入:
5
返回值:
15

示例2

输入:
1
返回值:
1

代码
我们来看看条件:
不让用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
因此一般的方法不能采用,此处我们利用**&&的特性**,对于 表达式1&&表达式2,如果表达式1为真,那么之后不再执行表达式2,根据此特性,用于设定递归调用的出口。

public class Solution {
    public int Sum_Solution(int n) {
        boolean x= n>1 && (n += Sum_Solution(n-1))>0;
        return n;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值