一、查詢
1.1 遞迴式二分查詢
- 作為查詢的必學演算法,二分查詢大家一定不陌生,透過前面我們所學的遞迴,那麼我們繼續強化遞迴思想,將二分查詢轉換成遞迴的方式。
- 任何迴圈都能改成遞迴,遞迴也可以改成任何迴圈。
演算法思想:
- 全範圍內二分查詢
-
等價於三個子問題
-
左邊找(遞迴):縮小範圍,可用遞迴,並且重複
-
中間比
-
右邊找(遞迴):縮小範圍,可用遞迴,並且重複
-
-
注意:
-
左查詢和右查詢只選其中一個
-
遞迴如果畫圖就會發現,其實是一個類似樹的樣子,有線性的,有二分的,三分的...
-
二分查詢就像下面這樣,但是每一次都會捨去一半,這也是二分查詢效率高的原因
/**
* 遞迴式二分查詢
* @param arr 陣列
* @param low 左指標
* @param high 右指標
* @param value 查詢值
* @return
*/
public static int binarySearch(int[] arr, int low, int high, int value) {
// 遞迴三步走:3. 找出口
if(low > high) return -1;
int mid = low + (high - low)/2;
if(value < arr[mid]) {
// 找重複、找變化:對左半部分進行二分查詢,最後返回的是我們查詢的結果
return binarySearch(arr,low, mid-1, value);
}
else if(value > arr[mid]) {
// 找重複、找變化:對右半部分進行二分查詢,最後返回的是我們查詢的結果
return binarySearch(arr,mid+1, high, value);
}
else return mid;
}
1.2 旋轉陣列最小數字
把一個陣列最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小值為1.
- 最小值一定在無序的那邊,並且在最大值的右側,因為我們題中給的就是有序遞增序列。
- 看到有序遞增序列的字眼,我們直接就能想到二分查詢。
- 此題也是二分查詢的一種變形
public static int ef(int[] arr) {
int low = 0;
int high = arr.length - 1;
// 沒有翻轉的情況
if(arr[low] < arr[high]) return arr[low];
while (low <= high) {
int mid = low + ((high - low) >> 1);
if (arr[mid] >= arr[low]) {
// 中間值大於左邊開始元素,則左邊是有序的,最小值一定藏在右邊
low = mid;
} else {
high = mid;
}
}
// 因為最小值一定在右側
return arr[high];
}
1.3 在有空字串中的有序字串查詢
有個排序後的字串陣列,其中散佈著一些空字串,編寫一個方法,找出給定字串(肯定不是空字串)的索引。
- 這個題就不畫圖了,非常簡單,就是當我們中間mid取到空字串時候,我們移動mid指標,直到不指向空為止。
public static int indexOf(String[] arr, String p) {
int begin = 0;
int end = arr.length - 1;
while(begin <= end) {
int mid = begin + ((end - begin) >> 1);
while(arr[mid].equals("")) {
mid++;
}
if(arr[mid].compareTo(p) > 0) {
end = mid - 1;
}else if(arr[mid].compareTo(p) < 0) {
begin = mid + 1;
}else {
return mid;
}
}
return -1;
}
1.4 找出最長連續遞增子序列
(1,9,2,5,7,3,4,6,8,0)中最長的遞增子序列為(3,4,6,8)
- 這個題非常的經典,我們可以採用雙指標策略,也稱之為滑動視窗演算法。
- 模擬一個視窗進行滑動,最後視窗內的區域就是我們想要的答案。
public static int zczxl(int[] arr) {
// 用於存放結果
int temp = 0;
for (int i = 0; i < arr.length; i++) {
// 滑動視窗右指標
int right = i+1;
int count = 0;
// 1.當右指標掃描到最後 或者 符合遞增條件時
while(right < arr.length && arr[right] > arr[i]) {
// 2.符合條件,我們讓右指標繼續移動,擴大我們的視窗,直到移動到不符合條件為止
right++;
i++;
count++;
}
// 3.更新結果,取最優解,也是最大值。
temp = temp < count+1 ? count+1 : temp;
// 4.我們透過更新i,也就是重新整理了左指標,讓左側視窗右移
}
return temp;
}
二、 夢迴遞迴
2.1 小白上樓梯
小白正在上樓梯,樓梯有n階臺階,小白一次可以上1階,2階或者3階,實現一個方法,計算小白有多少種走完樓梯的方式。
-
我們仍然再練習其它演算法的同時,不忘記我們的遞迴訓練。
-
和斐波那契數列很相似,不過變成了遞迴三分支的
-
我們透過逆推的方式,可以判斷最後上臺階只有三種模式,一種是差一步、第二種差兩個臺階、第三種差三個臺階
-
將這三種情況子問題我們透過遞迴求出後,彙總即可。
public static int slt(int n) {
// 3. 找出口
if(n == 0) return 0;
if(n == 1) return 1;
if(n == 2) return 2;
if(n == 3) return 4;
// 1.找重複
// 2.找變化
return slt(n-1) + slt(n-2) + slt(n-3);
}
2.2 設計一個高效的求a的n次冪的演算法
解法一:O(n)
- 這種解法正常人都能想出來
/**
* a的n次冪
* @param a
* @param n
* @return
*/
public static int pow1(int a, int n) {
int res = 1;
for (int i = 0; i < n; i++) {
res *= a;
}
return res;
}
解法二:
既然解法一是O(n),那麼我們如果想要再次最佳化演算法的時間,必然是logN級別的
- 81 = 3^2 * 3^2 = 3^4
- 所以我們先透過階乘求其一部分值,最後透過遞迴解決另一部分值,讓二者相乘就是我們的答案!
- 還是非常的應用了:遞迴自己幹一部分,另一部分交給別人的思想!
public static int pow(int a, int n) {
int res = a;
int ex = 1;
if(n == 0) return 1;
// 透過階層,我們先進行乘一部分
while((ex<<1) <= n) {
res *= res;
ex <<= 1;
}
return res * pow(a,n-ex);
}
三、結尾
- 對於藍橋杯查詢知識內容就總結這麼多,若想深入學習等待後續更新。
- 我將會繼續更新關於藍橋杯方向的學習知識,感興趣的小夥伴可以關注一下。
- 文章寫得比較走心,用了很長時間,絕對不是copy過來的!
- 尊重每一位學習知識的人,同時也尊重每一位分享知識的人。
- ?你的點贊與關注,是我努力前行的無限動力。?