【面试题16 数值的整数次方】
难度: 中等
实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
Leetcode题目对应位置: 面试题16:数值的整数次方
实现特定库函数(处理数值和字符串的函数)的功能是一类常见的面试题。
测试考虑:
n
可能为正或负,当它是负数时,就要先对 n
求绝对值,算出底数 x
的 n
次方后再去倒数x
为 0,且指数是负数时,若不进行特殊处理,会出现对 0 求倒数从而报错最直接的思路应该就是 base
循环相乘,这样做时间复杂度为 O(n),效率不高。
python 代码:
class Solution:
def myPow(self, x: float, n: int) -> float:
if x == 0 and n < 0: return -1
absN = n
if n < 0: absN = -n
res = self.power(x, absN)
if n < 0: res = 1 / res
return res
def power(self, x, n):
res = 1
for i in range(n):
res *= x
return res
C++ 代码:
#include
#include
bool g_InvalidInput = false;
bool equal(double num1, double num2);
double PowerWithUnsignedExponent(double base, unsigned int exponent);
double Power(double base, int exponent)
{
g_InvalidInput = false;
// 处理底数为0,指数为负的情况
if (equal(base, 0.0) && exponent < 0)
{
g_InvalidInput = true;
return 0.0;
}
// 处理指数为负的情况 -> 取绝对值
unsigned int absExponent = (unsigned int)(exponent);
if (exponent < 0) {
absExponent = (unsigned int)(-exponent);
}
// 计算次方
double result = PowerWithUnsignedExponent(base, absExponent);
if (exponent < 0) {
result = 1.0 / result;
}
return result;
}
double PowerWithUnsignedExponent(double base, unsigned int exponent)
{
double result = 1.0;
for (int i = 1; i <= exponent; i++) {
result *= base;
}
return result;
}
bool equal(double num1, double num2)
{
if ((num1 - num2 > -0.00000001) && (num1 - num2 < 0.00000001))
return true;
else
return false;
}
在 C++ 代码中,出错处理办法是设置全局变量 g_InvalidInput
来标识是否出错,若出错则返回值是 0 且全局变量 g_InvalidInput
为 true,用以区分出错时返回的 0 和正常计算返回的 0。这样做的好处是函数返回值可以作为参数传递给其他函数。但是这种出错处理办法的隐患在于:调用者可能会忘记检查全局变量。
第一种思路循环做乘,若需要求解 n 次方,就得做 n-1 次乘法,效率低下。
二分法求解思路: 假设要求 x 的 32 次方,那么只要知道了 x 的 16 次方,只要在 16 次方的基础上平方一次就可以了;而 16 次方又是 8 次方的平方…以此类推,实际上求 32 次方只需要做 5 次乘法:x = x*x
,通过循环更新 x
,就能得到 x^2
,x^4
,x^8
,x^16
,x^32
。
更一般地,对于 x 的 n 次方:
x n = { x n / 2 × x n / 2 n 为 偶 数 x ( n − 1 ) / 2 × x ( n − 1 ) / 2 × x n 为 奇 数 x^n=\begin{cases} x^{n/2} \times x^{n/2}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ n为偶数 \\ x^{(n-1)/2} \times x^{(n-1)/2} \times x\ \ \ \ \ n为奇数 \\ \end{cases} xn={xn/2×xn/2 n为偶数x(n−1)/2×x(n−1)/2×x n为奇数
该公式就是典型地通过递归来实现,相关题目:Leetcode刷题:剑指offer【面试题10-1 斐波那契数列】
时间复杂度: O ( l o g n ) O(logn) O(logn)
空间复杂度: O ( l o g n ) O(logn) O(logn)
代码逻辑:
res * res
,若 n 为奇数,则 res * x
Python 代码:
class Solution:
def myPow(self, x: float, n: int) -> float:
if x == 0 and n < 0: return -1
if x == 0: return 0
if n < 0: x, n = 1 / x, -n
return self.powerWithUnsignedExponent(x, n)
def powerWithUnsignedExponent(self, x, n):
if n == 0: return 1
if n == 1: return x
res = self.powerWithUnsignedExponent(x, n >> 1)
res *= res
if n & 1 == 1: # n为奇数
res *= x
return res
C ++ 代码只需要修改思路 1 中的 PowerWithUnsignedExponent
函数实现:
double PowerWithUnsignedExponent(double base, unsigned int exponent)
{
if (exponent == 0) {
return 1;
}
if (exponent == 1) {
return base;
}
double result = PowerWithUnsignedExponent(base, exponent >> 1);
result *= result;
if (exponent & 0x1 == 1) {
result *= base;
}
return result;
}
Python 代码实现:
class Solution:
def myPow(self, x: float, n: int) -> float:
if x == 0 and n < 0: return -1
if x == 0: return 0
if n < 0: x, n = 1 / x, -n
res = 1
while n:
if n & 1: res *= x
x *= x
n >>= 1
return res
代码来源:mian-shi-ti-16-shu-zhi-de-zheng-shu-ci-fang-kuai-s
发现一个很有意思的测试用例:
1.00000
-2147483648
用 C++ 写的话会报溢出,所以用强制类型转换来打个辅助。溢出的原因是有符号 int 类型变量在 C 里的范围是[-2147483648, 2147483647]
,如果对 -2147483648
直接取绝对值就会导致溢出,所以先用 unsigned int
对 n 进行强制类型转换就 ok 啦。
Line 12: Char 17: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself (solution.cpp)
C++ 代码实现:
class Solution {
public:
bool g_InvalidInput = false;
double myPow(double x, int n) {
if (x == 0 && n < 0){
g_InvalidInput = true;
return 0.0;
}
unsigned int m = (unsigned int) n; // 解决溢出
if (n < 0){
x = 1 / x;
m = -m;
}
double res = 1;
while(m){
if (m & 1 == 1)
res *= x;
x *= x;
m >>= 1;
}
return res;
}
};
参考资料:
[1] 剑指 offer 第二版
[2] LeetCode 面试题 16 参考题解