最快排序和搜索算法的最簡代碼實現_轉

轉自: https://blog.csdn.net/shendl/article/details/4053853算法

前言

         算法的核心問題是排序和搜索。這2個領域應用最廣,研究也最透。本文我將講解排序和搜索領域最高效的兩個算法:快速排序算法和二分搜索算法。數據庫

         教科書和不少實現庫給出的這兩個算法的代碼很是複雜,很難理解,本文中我給出的代碼是最簡單的實現代碼,易於理解,效率也很高。數組

緣起

         剛纔有人問我怎樣實現快速排序,我在5分鐘以內寫了一個快速排序的Java代碼出來,不知道他們有沒有理解。所以,我想到要寫做這篇文章。介紹一下快速排序算法和二分搜索算法的最簡實現。性能優化

         個人二分搜索算法是在我用Flex開發工做流編輯器時實現的。當時的需求是在2個圖形之間畫出鏈接線,要求根據鼠標操做來繪製,而且線段的起點和終點都是在圖形的外框上。編輯器

         上面的描述可能比較抽象,這麼說吧,原來我實現的GUI效果是,2個方框,使用鼠標把它們鏈接起來,我繪製的線是鼠標點下和釋放這2個端點鏈接起來的線段。可是,這樣的線段比較醜,客戶要求線段的兩頭應該在2個方框的邊框上。函數

         怎麼解決這個問題呢?我把線段看作是排序後的點的集合,而後就可使用二分搜索算法搜索到線段和邊框的交點,而後把它們繪製出來。性能

         當時的二分搜索算法是用ActionScript3寫的,如今我把它改爲Java了。優化

快速排序算法和二分搜索算法

         算法主要分爲排序算法、搜索算法、圖算法。圖算法我用得很少,沒有發言權,本文就不說了。ui

         排序算法中最快的是快速排序算法,搜索算法中最快的是二分搜索算法。我也最喜歡這2個算法。搜索引擎

         由於,它們是使用遞歸實現的,代碼簡潔清晰,效率又很是高。

         根據個人理解,算法的本質就是數學。根據輸入和設定的目標,採用有限的步驟實現輸出。一般,使用計算機實現的算法,都會用到循環,這樣才能發揮計算機高速運算的優點。

         循環和遞歸是等效的,這已經被科學家所證實。數學上沒有循環,只有遞歸的概念,所以使用遞歸代替循環表示算法有不少好處:

1)遞歸的代碼要比循環簡潔不少,也優雅不少。

2)遞歸的代碼能夠用數學方式建模,能夠從數學角度驗證其正確性。

不少函數式語言甚至沒有循環的概念和關鍵字,強迫你使用遞歸來實現循環。如,ErLang。

遞歸也有一些缺點,遞歸使用棧來保存函數地址和參數、返回值,而棧是有必定大小的,過多的遞歸調用可能會形成棧溢出。

可是,遞歸算法會容易轉變爲循環。我更欣賞遞歸的簡潔,除非真的出現棧溢出的問題,我是不會使用循環的。

二分搜索算法

理論:

         二分搜索算法用於針對已排序的集合進行搜索。

它的原理是:

1,  找到排序數組的中間元素,若是它匹配目標值,那麼就返回它在數組中的索引。

2,  若是沒有找到,那麼判斷中間值比目標值大仍是小,

若是中間值比目標值大,那麼就對第一個元素到middle-1的元素遞歸這個過程。

若是中間值比目標值小,那麼就對middle+1到最後一個元素。

3,  若是結束的索引小於開始的索引,返回-1,表示沒有找到。

4,  若是子集合有2個元素,那麼各自比較。由於Java的整數除法會捨棄小數,若是數組只有2個元素,那麼middle值一直都是第一個元素。

通過上述的遞歸過程,最終將返回匹配元素的索引,或者是-1,表示找不到。

 

二分搜索算法之因此速度快,是由於它每次能夠把數組切分紅兩半,每次遞歸調用都能去除一半數據,而不用匹配每個數據。

 

代碼:

         下面是代碼,邏輯清楚,代碼簡單。

/** * by 沈東良/良少 http://blog.csdn.net/shendl/ * @param array * @param start * @param end 這是最後一個元素的索引,第一次調用應該是array.length-1 * @param value * @return */

    public static int binarySearch(int[] array,int start,int end,int value){ int middle=(start+end)/2; if(end<start){ return -1; } if(end==(start+1)){ if(array[start]==value){ return start; }else if(array[end]==value){ return end; } }else if(array[middle]==value){ return middle; }else if(value>array[middle]){ return binarySearch(array,middle+1,end,value); }else if(value<array[middle]){ return binarySearch(array,start,middle-1,value); } return -1; }

         上述代碼稍加改變,就能夠排序任意類型。如使用泛型,而後加上對Comparable接口的實現,便可。

 

快速排序算法

         二分搜索算法確實很是快,可是它只能用於已排序數組。若是數組未排序呢,該怎麼辦呢?簡單,先用快速排序算法進行排序,而後再用二分搜索進行搜索。

         先排序再搜索,要比匹配每個元素快得多。搜索引擎,數據庫索引也都使用了對數據集合的排序技術,這樣搜索數據纔會快速。

理論:

         最慢,也是最容易想到的排序算法是插入排序算法:

1,  遍歷數組,找出最小的元素,把它放到第一個元素。

2,  循環查找未排序的數組,直到整個數組排序。

這須要2個嵌套的循環,意味着它的效率是O(n^2);

之因此插入排序的效率如此之地,是由於要找出整個數組中最小的數據,須要遍歷整個數組的元素。

而插入排序聰明就聰明在它不查找整個數組最小、次小…的元素,而是每次僅僅把小於某個元素的值移到一邊,經過迭代最終自動實現排序。這就大大節約了排序所需的計算步驟。

 

         快速排序算法的理論:

1,  若是S中的元素個數是0或者1,那麼返回。

2,  選取S中的任一元素v,稱爲中心點。

3,  將S集合中的元素分爲2個部分:L={小於pivot 的元素+ pivot }和R={大於或者等於pivot的元素}。

4,  返回L和R。

咱們使用Java使用的是就地排序的方式,所以不須要返回值。

由於Java是一種能夠修改變量的語言,用函數式語言的術語來講,就是有「反作用」。咱們利用這個反作用直接修改做爲參數的Array,不須要返回值。

 

代碼:

/** by 沈東良/良少 http://blog.csdn.net/shendl/ * 快速排序,有反作用 從小到大 * @param array * @param start * @param end這是最後一個元素的索引,第一次調用應該是array.length-1 */

    public static void quickSort(int[] array,int start,int end){ //有可能形成start>end 由於遞歸調用時j+1,可能引發j比end還大1。 另外若是數組是空的,或者輸入錯誤也會出現這種狀況

       if(end<=start){ return; }else { //取中間元素爲中心點,而後移到最右邊

           int sign=(start+end)/2; int tmp=array[end]; array[end]=array[sign]; array[sign]=tmp; int j=start; for(int i=start;i<=end-1;i++){ //小於的元素和標記互換,等於的不能互換,不然會造成死循環

              if(array[i]<array[end]) { tmp=array[i]; array[i]=array[j]; array[j]=tmp; j=j+1; } } //把標記元素和第一個>=它的元素位置互換,這樣數組就分紅2個部分,一個部分比中心值小,一個部分比中心值大。
 tmp=array[j]; array[j]=array[end]; array[end]=tmp; quickSort(array,start,j); quickSort(array,j+1,end); } }

         Java的Arrays類也使用快速排序算法進行排序。但它的代碼寫得像天書同樣。

 

         提升快速排序算法執行效率的主要方法是對中心點進行檢測,但願選中的中心點有更大的機率是整個數組的中值。

         個人實現中簡單的選擇數組中間的值做爲中心點,通常來講這樣的選擇效率仍是不錯的。

 

         注意上面的一項實現技術,咱們使用把中心數據元素移到數組最後的方式實現了數組的就地比較。這是比較經常使用的技術,把數據移到數組的最前面或者最後面,方便比較數據。

 

         另外,個人Java快速排序代碼使用了「反作用」,而在純函數式語言,如Haskell,ErLang中是沒有「反作用」的,也就是說變量不能夠從新賦值。此時就須要返回值,每次都建立新的子數組。但函數式語言的數組處理功能很強,也會作不少性能優化,所以函數式語言實現快速排序代碼更加簡單,沒有「反作用」,也更加數學化。

 

JDK使用二分搜索和快速排序算法實現搜索和排序,足見上述兩個算法的性能優點。

相關文章
相關標籤/搜索