挖坑法實現快速排序(Java,Scala)

瞭解快速排序

快速排序是C.R.A.Hoare於1962年提出的一種劃分交換排序。它採用了一種分治的策略,一般稱其爲分治法(Divide-and-ConquerMethod)。快速排序是一種不穩定的排序法。html

快速排序的操做步驟

  1. 取出一個數做爲基準。(下文將該基準命名爲temp)
  2. 全部小數在其前,全部大數在其後,所以temp左右兩邊共產生了兩個子區間。
  3. 兩個子區間內部未必是有序的。所以分別在每一個區間內重複1,2操做,直到各個區間只有一個數。禁止套娃?

因爲每一個區間內的處理方法是重複的,所以咱們能夠用遞歸函數來實現它(遞歸函數佔用棧空間),也能夠採起while循環來解決。數組

快速排序能夠輕鬆應付龐大且無序的數組,可是若用於高度有序(甚至徹底升序,降序排列好的,元素內容徹底重複的)的數組,表現反而會很糟糕。markdown

挖坑法提供的解決思路

上段中提到的操做步驟2有不少種方法實現,思路大同小異。在這裏使用挖坑法來實現。 咱們首先準備:ide

  • 兩個」指針」 (雖然它們實際上只是數組下標號,但爲了方便理解,下文仍統稱爲指針) lowhigh,分別指向數組的首和尾,且high只向移動,low只向移動。
  • 默認將數組首元素保存到temp當中。

換個說法,在程序開始前,在arr[low]的位置挖了坑,該位置上的數被移到了temp中。函數

有一個大前提:在調用該函數時low < high必須成立,不然再也不繼續遞歸。由於low==high意味着這個數組只有1個元素,那就沒有必須再進行排序了。oop

程序運行,low指針不動,而high指針判斷它所目前指向的值是否大於temp。知足條件則左移,不然high指針會停下來,將當前指向的值覆蓋到arr[low]位置上。(演示圖裏第一次判斷就中了獎 =D) ui

或者說,high位置上的數被挖出來填到了low位置上的坑。spa

此刻, high指針不動, low指針判斷它所目前指向的值是否小於 temp。知足條件則右移,不然 low指針會停下來,將當前指向的值覆蓋到 arr[high]位置上。(這一段敘述和上一段敘述互爲反過程。)

或者說,low位置上的數又被挖出來填了上一步high指針位置留下來的坑。指針

high指針再次開始左移,直到發現大於等於temp的數,則將其挖出來填到low指針的地方並暫停,隨後low指針又不斷右移,直到發現小於temp的數,再將它挖出填到high指針處......highlow指針一直處於交替相向移動的狀態。code

如此下去,總有一刻high指針和low指針會指向同一位置。此刻無需再移動highlow指針,只須要把最初挖出來的temp值,放到這個坑內便可。

咱們此時能夠發現,原先的數組已經被分爲了三部分:比temp小的數,temp,比temp大(或等)的數組。

只須要再對Smaller和Greater區間進行遞歸操做,重複以前所述的步驟就能夠了。

基於挖坑法的Java快速排序

static void quickSortJ(Integer[] arr, int start, int end) {
        if (start < end) {
            Integer low = start;
            Integer high = end;
            Integer temp = arr[low];

            while (low < high) {

                while (high > low && arr[high] >= temp) high--;
                if (high > low) arr[low] = arr[high];
                while (high > low && arr[low] < temp) low++;
                if (high > low) arr[high] = arr[low];
                
            }
            
            arr[high] = temp;
            quickSortJ(arr, start, low - 1);
            quickSortJ(arr, high + 1, end);
        }
    }
複製代碼

我只要[Smaller[], Temp, Greater[]]!

咱們在進行一輪排序後(左右指針法,挖坑法,etc.)一定能將數組分割成如下形式:[Smaller[], temp, Greater[]]。那能不能用更簡單偷懶的方法來獲得Smaller區間和Greater區間呢? 在Scala中,能夠直接利用過濾器filter來過濾出比temp小(或大)的數。如:

arr.filter(x => x > temp) //filter處理以後獲得的還是Array[],放心食用。

然後再對過濾出來的Smaller[],和Greater[]繼續進行遞歸便可。用Java的流處理一樣能夠實現相似邏輯,可是比較麻煩。

用Scala實現快速排序

def quickSortScl(arr: Array[Int]): Array[Int] = {
    if (arr.length <= 1)
       arr
    else {
      //這裏的temp沒有選取首元素,而是取中間元素.
      val temp =arr(arr.length/2)
      //返回一個[SmallerThanTemp[],temp,GreaterThanTemp[]].
      Array.concat(
        quickSortScl(arr.filter(x => x<temp)),
        //若是有多個相等的元素,則該數組內的元素個數大於1.
        arr.filter(x => x==temp),
        quickSortScl(arr.filter(x => x>temp))
      )
    }
  }
複製代碼

傳送門連接

相關文章
相關標籤/搜索