Pow(x, n)
- 方法一:暴力法
- 方法二:遞歸快速冪算法
- 方法三:迭代快速冪算法
- 方法四:位運算法
方法一:暴力法
思路
只需模擬將 x
相乘 n
次的過程。
如果 \(n < 0\),我們可以直接用 \(\dfrac{1}{x}\), \(-n\) 來替換 \(x , n\) 以保證 \(n \ge 0\)。該限制可以簡化我們的進一步討論。
但我們需要注意極端情況,尤其是負整數和正整數的不同范圍限制。
算法
我們可以用一個簡單的循環來計算結果。
class Solution {
public:
double myPow(double x, int n) {
long long N = n;
if (N < 0) {
x = 1 / x;
N = -N;
}
double ans = 1;
for (long long i = 0; i < N; i++)
ans = ans * x;
return ans;
}
};
復雜度分析
- 時間復雜度:\(O(n)\)。我們將
x
相乘n
次。 - 空間復雜度:\(O(1)\)。我們需要一個變量來存儲
x
的最終結果。
方法二:遞歸快速冪算法
class Solution {
public:
double fastPow(double x, long long n) {
if (n == 0) {
return 1.0;
}
double half = fastPow(x, n / 2);
if (n % 2 == 0) {
return half * half;
} else {
return half * half * x;
}
}
double myPow(double x, int n) {
long long N = n;
if (N < 0) {
x = 1 / x;
N = -N;
}
return fastPow(x, N);
}
};
復雜度分析
- 時間復雜度:O(log(n))O(log(n))。每次我們應用公式$ (x ^ n) ^ 2 = x ^ {2 * n}\(,\)n$ 就減少一半。 因此,我們最多需要 \(O(log(n))\)次計算來得到結果。
- 空間復雜度:\(O(log(n))\)。每次計算,我們都需要存儲 \(x ^ {n / 2}\) 的結果。 我們需要計算 \(O(log(n))\)次,因此空間復雜度為 \(O(log(n))\)。
方法三:迭代快速冪算法
遞歸或迭代的快速冪實際上是實現同一目標的不同方式。
class Solution {
public:
double myPow(double x, int n) {
long long N = n;
if (N < 0) {
x = 1 / x;
N = -N;
}
double ans = 1;
double current_product = x;
for (long long i = N; i ; i /= 2) {
if ((i % 2) == 1) {
ans = ans * current_product;
}
current_product = current_product * current_product;
}
return ans;
}
};
復雜度分析
- 時間復雜度:\(O(log(n))\)。對於
n
的每個二進制位,我們最多只能乘一次。所以總的時間復雜度為 \(O(log(n))\)。 - 空間復雜度:\(O(1)\)。我們只需要兩個變量來存儲
x
的當前乘積和最終結果。
位運算實現pow(x,n)
根據暴力法的思路來看特別簡單,但通過位運算呢?
我舉個例子吧,例如 n = 13,則 n 的二進制表示為 1101, 那么 m 的 13 次方可以拆解為:
\(m^{1101} = m^{0001} * m^{0100} * m^{1000}\)。
我們可以通過 & 1和 >>1 來逐位讀取 1101,為1時將該位代表的乘數累乘到最終結果。直接看代碼吧,反而容易理解:
int pow(int n){
int sum = 1;
int tmp = m;
while(n != 0){
if(n & 1 == 1){
sum *= tmp;
}
tmp *= tmp;
n = n >> 1;
}
return sum;
}
時間復雜度近為 \(O(logn)\),而且看起來很牛逼