刷題心得體會
自己在刷題的時候,總結出的一些適用於自己的小心得體會。
- 空間換時間
要求時間複雜度爲O(N)時,一個很普遍的思路是使用HashMap或HashSet來進行存儲,利用哈希表的特性來進行存儲。 - 前綴和
對於求區間和的問題,我們很容易想到利用前綴和數組來優化其查詢速度前綴和wiki:https://oi-wiki.org/basic/prefix-sum/
- 雙指針
-
快慢指針
-
左右指針
4.回溯法
-
LinkedList result = new LinkedList();
public void backtrack(路徑,選擇列表){
if(滿足結束條件){
result.add(結果);
}
for(選擇:選擇列表){
做出選擇;
backtrack(路徑,選擇列表);
撤銷選擇;
}
}
-
快速冪
快速冪是一種簡單而有效的小算法,它可以以[公式]的時間複雜度計算乘方。快速冪不僅本身非常常見,而且後續很多算法也都會用到快速冪。
無非是一個二分的思路。我們很自然地可以得到一個遞歸方程
- 非遞歸快速冪
//遞歸快速冪
int qpow(int a, int n)
{
if (n == 0)
return 1;
else if (n % 2 == 1)
return qpow(a, n - 1) * a;
else
{
int temp = qpow(a, n / 2);
return temp * temp;
}
}
- 遞歸快速冪
我們換一個角度來引入非遞歸的快速冪。還是7的10次方,但這次,我們把10寫成二進制的形式,也就是 [公式] 。
現在我們要計算 7^(1010)2
,可以怎麼做?我們很自然地想到可以把它拆分爲 7^(1000)2*7^(10)2
. 實際上,對於任意的整數,我們都可以把它拆成若干個 [公式] 的形式相乘。而這些[公式],恰好就是 7^1
、7^2
、7^4
……我們只需不斷把底數平方就可以算出它們。
//非遞歸快速冪
int qpow(int a, int n){
int ans = 1;
while(n){
if(n&1) //如果n的當前末位爲1
ans *= a; //ans乘上當前的a
a *= a; //a自乘
n >>= 1; //n往右移一位
}
return ans;
}
最初ans爲1,然後我們一位一位算:
1010的最後一位是0,所以a^1
這一位不要。然後1010變爲101,a變爲a^2
。
101的最後一位是1,所以a^2這一位是需要的,乘入ans。101變爲10,a再自乘。
10的最後一位是0,跳過,右移,自乘。
然後1的最後一位是1,ans再乘上a^8
。循環結束,返回結果。
這裏的位運算符,>>是右移,表示把二進制數往右移一位,相當於/2;&是按位與,&1可以理解爲取出二進制數的最後一位,相當於%2==1。這麼一等價,是不是看出了遞歸和非遞歸的快速冪的關係了?雖然非遞歸快速冪因爲牽扯到二進制理解起來稍微複雜一點,但基本思路其實和遞歸快速冪沒有太大的出入。
節選自快速冪專欄:https://zhuanlan.zhihu.com/p/95902286,原文寫的非常優秀!
50. Pow(x, n)