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) | 当前位 | 进位 |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 1 |
如上图所示,
(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次方。
注意:
- 保证base和exponent不同时为0。
- 不得使用库函数,同时不需要考虑大数问题
- 有特殊判题,不用考虑小数点后面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.
如下所示:
A | B | A^B |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
据此我们可以得到如下推论
A^A=0
A^0=A
A^ A ^B=B
因此,对于当前题目,设数组为arr,设只出现一次的两个数字分别为a,b。
- 遍历数组,对数组元素做异或操作(即arr[0]^ arr[1]^ arr[2] ^ …^arr[arr.length-1]),因为除ab之外,其余均重复出现2次,因此异或后结果为a ^ b
- 因为ab互不相等,因此a^b肯定存在某一位为1,比如第5位为1(“第5位为1” 设为条件①)
- 将数组根据条件①分为两组,一组满足条件①,设该数组为arr1;一组不满足条件①,设该数组为arr2.
- 我们可以确定,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;
}
}