常見的演算法:二分法查詢,氣泡排序和選擇排序
今天主要說一下常見的演算法 ,於是我百度了一下點進那 “ 牛逼 ” 的知乎看了一眼 ,完蛋了 ,這都是些神馬 ??? 我怎麼一個都不會呢 ,我要的可是那種很常見的演算法啊 ,好吧 ,無形中又被深深的傷了一刀 ,好在我迅速調節狀態 。管他呢 ,我就按照自己 low 的來吧 。進入正題 ,我要說的幾種演算法就是二分法查詢 ,氣泡排序和選擇排序 。以陣列為例 ,談談它們在 Java 中的實現 。
二分法查詢 ,舉個例子說明一下 ,我們在玩猜數字遊戲的時候經常會用到 。小明給出數的範圍 ,0 ~ 100 ,我們猜 50 ,小明回答說 “ 大了 ” ,那我們會接著猜 25 ,就這樣我們一次次的逼近了真相 。這就是二分法的思想 。也可以說是分治的思想 ,分而治之 ,逐一攻破 。我們同樣也可以將這種思想應用到有序陣列的查詢上 。我們可以拿陣列中間的數和待查詢元素進行比較 ,其過程類似與猜數字 ,變得就是 “ 中間 ” 位的那個數 。最終我們可以找到目標元素 。方法實現 :
private static int binarySearch(int[] list,int target) {
int low = 0;
int high = list.length - 1;
//直到low>high時還沒找到關鍵字就結束查詢,返回-1
while(low<=high){
int mid = (low+high) / 2;
if(target < list[mid]){
high = mid - 1;
}
else if(target > list[mid]){
low = mid + 1;
}
else if(target == list[mid]){
return mid;
}
}
return -1;
}
氣泡排序 ,想象一下魚兒在水中吐了一個泡 ,在向上的過程中 ,是不是在越變越大 。其實氣泡排序的思想也就是這樣的 。給你 n 個數 ,我們用第一個數和第二個數進行比較 ,大的那個就放第二位 ,然後第二位和第三位比較 ,大的那個放在第三位 ,依次比下去 ,待到和最後一個比較結束 ,我們就能得到這 n 個數中最大的那個 ,且放在最後一個位置上 。( 這要是不明白就回頭再讀一遍 ) 這樣一輪比較結束我們得到一個最大的數 ,第二輪我們再進行比較 ,同理 ,可以得到剩餘 n - 1 個數中最大的數 。且放在倒數第二位 ,這就就是內層 for 迴圈條件 - 1 的原因 ,因為沒必要和上一輪的最後一個數進行比較嘛 。方法實現 :
public static void bubbleSort(int[] arr) {
/*
* 外面的for迴圈決定一個長度為 n 的資料要比較多少輪才能完成排序。
* 裡面的for迴圈決定每次一輪迴圈中要做多少次才能結束。
*/
for(int i = 0; i < arr.length - 1; i++) {
for(int j = 0; j < arr.length - 1 - i; j++){
//從小到大,大的值放後面位置。
if (arr[j] > arr[j+1]){
int temp = arr[j]
arr[j] = arr[j + 1]
arr [j + 1] = temp
}
}
}
}
PS . 只需要換一個符號 ,就可以得到從大到小的序列 。
快速排序 ,快速排序是對氣泡排序的一種改進 。它的基本思想是將要排序的資料分割成獨立的兩部分 ,其中一部分的所有資料都比另外一部分的所有資料都要小 。然後在按照此方法對兩部分資料分別進行快速排序 ,最終得到一個有序的序列 。需要說明的是 ,快速排序的平均時間複雜度是 O(nlogn) 。但是時間複雜度還是 O(n^2) ,因為時間複雜度是按照最壞結果來計算的 。為了方便理解 ,使用一組資料模擬一下排序的過程 。
假設要排的陣列為:int[] a = { 5 2 8 9 2 3 4 9 };
選擇 key = 5, 開始時 i = 0,j = 7
下標 0 1 2 3 4 5 6 7
開始 5 2 8 9 2 3 4 9
i j
第一次找 5 2 8 9 2 3 4 9
i j
交換: 5 2 4 9 2 3 8 9
i j
第二次找 5 2 4 9 2 3 8 9
i j
交換: 5 2 4 3 2 9 8 9
i j
第三次找 5 2 4 3 2 9 8 9
ij
調整key: 2 5 4 3 5 9 8 9
ij
方法實現 :
private static void quickSort(int[] a, int low, int high) {
//找到遞迴演算法的出口
if( low > high) {
return;
}
int i = low;
int j = high;
//預設 key
int key = a[low];
//開始一趟排序
while( i < j) {
//先從右往左找到第一個小於 key 的數 ,
//這麼做是因為在與 key 值交換的時候,保證了交換的數小於現有的 key 值
//若是大於的話,j 指標就會繼續向左移動 。
while(i<j && a[j] > key){
j--;
}
//從左往右找到第一個大於等於 key 的數
while( i<j && a[i] <= key) {
i++;
}
//交換,達到以 key “分治” 的效果
if(i<j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
// 當 i = j 時,調整 key 的值為 a[i] == a[j]
int temp = a[i];
a[i] = a[low];
a[low] = temp;
//對 key 左邊的數快速排序
quickSort(a, low, i-1 );
//對 key 右邊的數快速排序
quickSort(a, i+1, high);
}
後記 :不得不說 ,這些簡單的演算法我都沒有很熟悉 ,只能是那種原理清楚 ,但是實現起來卻是磕磕絆絆的狀態 。還有 ,以上給出的實現是可以進一步優化的 ,記得之前在面試的時候被問到過氣泡排序的問題 ,現場也被問到過如何優化 。