1. Divide Two Integers
思路: 由于不可以用*, /, %运算,我们只能用+, -还有移位运算。首先,如果我们用减法,依次从被除数中减掉除数,那么是O(N)的时间复杂度。那么有没有更快的方法吗?
我们试着用 “基”的思想来试一下:假设我们需要计算63 / 5:
- 首先,我们找到最大的基:5*1, 5*2, 5*4, 5*8, 5*16(超过),因此我们选5 * 8为最大的基;
- 我们从最大的基开始依次降低,如果加到和 ( sum ) 里小于等于被除数,则加入进去(8(ok), 4(ok), 2(no), 1(no));
- 在上步进行的同时,我们记录我们加了多少进去(8 + 4),得到的就是“商”(12)。
这样的时间复杂度是O(logN).
代码如下:
class Solution {
public:
int divide(int dividend, int divisor) {
if (dividend == INT_MIN && divisor == -1) return INT_MAX;
if (divisor < 0) {
if (divisor == INT_MIN) {
return dividend == INT_MIN ? 1 : 0;
}
return -divide(dividend, -divisor);
}
if (dividend < 0) {
if (dividend == INT_MIN) {
dividend += divisor;
return -1 - divide(-dividend, divisor);
}
return -divide(-dividend, divisor);
}
//both are positives
int e = 1;
//while ((divisor << e) <= dividend) { //find the biggest base
// ++e;
//}
while (dividend >> e >= divisor) ++e; //find the biggest base
--e;
int rs = 0;
for ( ; e >= 0; --e) {
if (dividend - (divisor<<e) >= 0) {
rs += 1 << e;
dividend -= (divisor<<e);
}
}
return rs;
}
};
注意,注释掉的那段
//while ((divisor << e) <= dividend) { //find the biggest base
// ++e;
//}
这段代码会造成死循环,当然也会越界,因为在向右移位的时候,会突变为INT_MIN。因此我们采用被除数向右移位,知道比除数还小的方法去找最大基。
2. Pow(x, n)
此道题和上题的处理思路类似,但更简单一些。我们把n用二进制表示b[i],同时记录下来相应的x的幂。如x^5, 5的二进制表示为[1, 0, 1], 我们记录下来x^1, x^2, x^4的值,然后根据二进制表示决定其需不需要乘到rs里面去。
用cache记录x的幂次的代码:
class Solution {
public:
double pow(double x, int n) {
// get n's binary representation, and spead up.
// cache x^0, x^1, x^2, x^4 if binary representation is 1, else cache 1.0
if (n == INT_MIN) return 1.0 / (pow(x, INT_MAX) * x);
if (n < 0) return 1.0 / pow(x, -n);
vector<double> cache;
cache.push_back(1.0);
double x_pow = x;
while (n > 0) {
int now = n % 2;
if (now == 0)
cache.push_back(1.0);
else
cache.push_back(x_pow);
n /= 2;
x_pow *= x_pow;
}
double rs = 1.0;
for (int i = 0; i < cache.size(); ++i)
rs *= cache[i];
return rs;
}
};
其实我们不需要用cache,因为我们只需要记住当前的幂次就好。
没有用cache的代码:
class Solution {
public:
double pow(double x, int n) {
// get n's binary representation, and spead up.
// record x^0, x^1, x^2, x^4 if binary representation is 1
if (n == INT_MIN) return 1.0 / (pow(x, INT_MAX) * x);
if (n < 0) return 1.0 / pow(x, -n);
double rs = 1.0;
double x_pow = x;
while (n > 0) {
int now = n % 2;
if (now != 0)
rs *= x_pow;
n /= 2;
x_pow *= x_pow;
}
return rs;
}
};
还有要注意的就是当负数转为正数的时候,记得考虑INT_MIN的情况。
3. Sqrt(x)
我们可以利用二分法,指定l, r来二分地试出来,但是会超时。
这里我们采用牛顿迭代法。
具体方式为:我们用牛顿迭代法来找到方程x^2 - a = 0的正实根。假如,根(我们想要的结果)为x0,那么此处的导数(切线斜率)为2*x0,切线方程为 y = 2*x0*x - x0^2 - a,与x轴的交点为x = x0 / 2 + a / (2 * x0)。我们就拿这个新的交点作为下次迭代的输入x0,来进一步迭代,直至方程x^2 - a 近似等于0.
class Solution {
public:
int mySqrt(int x) {
// 牛顿迭代法
if (x < 0) return INT_MIN;
if (x == 0) return 0;
const int a = x;
const double threshold = 0.1;
double x0 = x; //solution
double dis = INT_MAX;
while (dis > threshold) { // get the zero point of plot: y = x^2 - a; a is passed argument x,
// which means the solution we want is the solution of x^2 - a = 0
x0 = (x0 + a / x0) / 2;
dis = abs(x0*x0 - a);
}
return x0;
}
};