演算法是神奇的。
it技術日新月異,各種語言、工具、平台快速更迭著。
而演算法、資料結構,幾乎是不會過時的。
之前學習了演算法與資料結構,但是總感覺沒有用武之地,對演算法的認識大多僅限於」程式設計大賽」。因為不常用,所以漸漸生疏。
前陣子在學習lighttpd的原始碼,發現裡面的演算法、資料結構幾乎貫穿了整個專案,陣列、字串、鍊錶、樹等資料結構以及二分查詢等演算法的靈活應用,使得整個專案的邏輯更加清晰,而效率也提公升了許多。
我這才見識到演算法與資料結構在工程應用中的重要性。
我們日常的學習中,大多學的是最標準的演算法版本,然而實際問題卻千變萬化,實際用到的演算法也常常是標準版本的變形。所以,光靠「背演算法」是很難有成效的,我們需要理解乙個演算法背後的思想,才能以不變應萬變。
在接下來,我將重新把一些常用的經典演算法捋一遍,力求理解演算法的內涵。
說到演算法,我想大家對二分演算法並不陌生,但是能快速寫出正確的**的人卻不多。接下來,我們將以二分演算法及其變形為例,開始走進演算法的重修之路。
也許我說,麻煩你快速寫乙個二分查詢的演算法,為簡單起見,寫個遞迴版本的吧!
然後刷刷刷下面的**就出來了:
int binarysearch(int arr, int
left, int
right, int x)
return -1;
}
以上**似乎已經挺完美了。
可是,要是arr==null呢?要是arr是降序排序的呢??
這就涉及兩個問題:
1.**的健壯性
我們只需修改下if條件:
if (null != arr && left
<= right)
就可以讓我們的程式更加健壯。
2.明確需求,對症下藥
對於這一點,我覺得《程式設計珠璣》就講的很精彩,裡面從乙個排序演算法講起,從一開始的「我要對一些檔案排序」一步步挖掘需求,從盲目的歸併演算法到針對特定範圍內的整數的點陣圖排序。
所以在面試中,或者在實際應用中,如果你能夠多問一句:「陣列是公升序還是降序?」我想就能少犯錯誤。
下面我們來寫一寫非遞迴版本(只考慮陣列公升序),並指出一些常見的問題:
int binarysearch(int arr, int left, int right, int x)
return -1;
}
最後別忘了針對各種輸入情況進行測試。
**在這裡備註乙個小技巧:
你可能注意到,我在if語句中進行相等測試的語句是:
if (null == arr)
if (arr == null)
而我那樣寫的理由很簡單,如果粗心的你一不小心把 == 寫成了 = ,那麼該語句將變成乙個賦值語句,而通常情況下編譯器是檢測不到這個錯誤的,因為該語句在語法上是正確的。而對於我的寫法,如果 == 寫成 = ,由於左邊是乙個右值,所以該語句在語法上是錯誤的,編譯器將幫我們指出這個錯誤。
因此,這個小細節體現的是良好的程式設計習慣。
在迴圈有序陣列中查詢指定元素,也就是說在類似這樣的陣列中查詢指定的元素。
我們使用二分法,當我們取出mid之後,陣列將被分成兩個部分,每部分有兩種可能: 純有序陣列 or 迴圈有序陣列。
如果(arr[mid] >= arr[left]),說明左側是乙個純有序陣列,於是當x<=arr[mid]&&x>=arr[left]時,待測元素肯定會在mid的左側,其他情形則會在mid的右側。
如果(arr[mid] < arr[left]),說明右側是乙個純有序陣列,於是當x<=arr[left]&&x>=arr[mid]時,待測元素肯定會在mid的右側,其他情形則會在mid的左側。
int search(int arr, int left, int right, int x)
else
}return -1;
}
需要注意的是,我們的**仍然有缺陷。
1.純有序陣列是旋轉陣列的特例,不過顯然我們的**可以處理這種情況。
2.如果陣列中的數字是可重複的呢?
比如,那麼我們的程式在查詢0時將失敗!因為在我們的程式中,將認為是乙個純有序陣列。
(對於,我們的程式能夠正確處理)。
為解決這個問題,我們需要對arr[mid] == arr[left] 的情況進行處理。(我的思路:對left到mid部分進行順序查詢,如果該部分的值均相同,說明左側是乙個純有序陣列,否則為迴圈有序陣列。再根據結果進入下一步,具體實現留給讀者去思考。)
假如集合中的元素有重複,要找到x首次出現的位置。
只需要對 arr[mid] == x 的情況進行特殊處理,檢查前乙個元素是否是重複元素。
int binarysearch(int arr, int left, int right, int x)
else
if (arr[mid] > x) right = mid-1;
else left = mid+1;
}return -1;
}
在乙個有序的陣列裡,資料裡面元素可能有重複的,查詢指定x所在的索引範圍。
例如:int arr = ; 查詢3的話,應該返回[5,9]。
思路1:用二分法隨意找乙個x的索引,之後往兩邊找重複的x;
以上演算法在x比較多的情況下效率不高。
思路2:兩次二分法,第一次找x首次出現的位置(變形2)left,第二次找比x大的數的最小索引right,於是答案為[left,right-1]。
第二次二分的**(找第乙個比x大的數且其索引最小):
int binarysearch(int arr, int left, int right, int x)
else left = mid+1;
}return -1;
}
好了~就先講到這裡,每天進步一點點,come on!
(●』◡』●)
本人水平有限,如文章內容有錯漏之處,敬請各位讀者指出,謝謝!
查詢演算法 二分查詢
利用二分查詢演算法查詢某乙個元素,前提條件是該被查詢的元素是乙個已經有序的陣列。二分查詢的思想是將陣列元素的最高位 high 和最低位 low 進行標記,取陣列元素的中間 mid 和和要查詢的值 key 進行比較,如果目標值比中間值要大,則將最低位設定為mid 1,繼續進行查詢。如果目標值小於中間值...
查詢演算法 二分查詢
二分查詢的思路是很簡單的,前提是這組資料是有順序的。思路是從中間找乙個數,判斷大小,如果數比中間數大,說明在中間數到結尾的數中,如果小於,則說明在開始和中間數之間,經過多次相同操作,就可以得到我們想查詢的數時間複雜度就是 o logn 非遞迴的實現 const testarr let i 0whil...
查詢演算法 二分查詢
二分查詢是乙個常用的查詢演算法,其原理在於通過不斷切分乙個規則排序,對半的去尋找目標元素所在的區間與位置。但是其有乙個前提,那就是資料結構需要是順序儲存結構,並且關鍵字大小有序排列。例子如下 例 有乙個數列 12,23,45,56,67,89 請使用二分查詢找到56的位置 解 首先mid 0 5 2...