【算】快速排序

分治

快速排序(如下簡稱「快排」)的核心思想是分治法。能夠說,分治提供了另外一種解決問題的思路。舉個例子來進行說明,抓穩扶好,直接開車了……java

舉例

現有一個集合{4,8,2,5,7,-1,3},咱們將對它進行從小到大排序:segmentfault

1.選取第一個元素4做爲基準值,後面的元素逐個和這個基準值比較大小函數

clipboard.png

顯然,要麼大於,要麼小於( 暫不考慮相等的狀況)。編碼

2.按比較大小結果進行安置:大於基準值的元素,置於它的左側;小於基準值的元素,置於它的右側(若是有相等狀況,左右隨意)spa

clipboard.png

爲好理解,畫出了數軸,將"基準值4"做爲中軸線。第二個元素8大於4,放於右側;第三個元素2小於4,放於左側……以此類推,最後一個元素放置完畢後是這樣的code

clipboard.png

3.「重複」。先拋開右側的big集合,專一於左側集合{2,-1,3}。此時,咱們把它看成source,重複第一步的操做。排序

clipboard.png

如此重複下去,直到只剩下一個元素的狀況。遞歸

clipboard.png

此時從左到右讀出圖中曾做爲基準值的元素(菱形)——-1,2,3,4,咱們發現已經排序好了。最後給出完整的操做示意圖:索引

clipboard.png

每次根據條件劃分紅兩部分,劃分後每部分分別治理,即分治。ip

遞歸

上面的例子中,第三步叫「重複」其實並不許確,真正的名字是遞歸

每一個遞歸函數都有兩部分

  • 基線條件:函數再也不調用本身,即退出無限循環調用的條件
  • 遞歸條件:調用本身,且經過一次次調用會逐步靠攏基線條件

拿上一篇文章(【算】選擇排序和二分查找)裏聊過的二分查找舉例:

/**
 * 二分查找,遞歸版
 * @param target
 * @param source
 * @return
 */
public static int doFind(int target,List<PositionBean> source){
    if(CollectionUtils.isEmpty(source)){
        throw new RuntimeException("集合爲空");
    }
    int halfIndex = source.size()/2;
    int halfElement = source.get(halfIndex).getVal();
    if(target==halfElement){    //基線條件:若是目標target和中間數相等,bingo!找到了,返回索引
        return source.get(halfIndex).getIndex();
    }else if(source.size()==1){ //另外一個基線條件:就剩下最後一個元素了,仍然與目標target不符,則返回「目標不存在」
        throw new RuntimeException("目標不存在");
    }else{  //遞歸條件:選取符合條件的那一半集合,遞歸調用本身
        if (target < halfElement) {
            List<PositionBean> tempSource = source.subList(0, halfIndex);
            return doFind(target, tempSource);
        } else {
            List<PositionBean> tempSource = source.subList(halfIndex, source.size());
            return doFind(target, tempSource);
        }
    }
}

快排

掌握了遞歸和分治思想後,快速排序就只剩下編碼部分了:

/**
 * 快排
 * @param source
 * @return
 */
public static List<Integer> doOrder(List<Integer> source){
    if(source.size()<=1){   //基線條件
        return source;
    }
    int temp = source.get(0);
    List<Integer> lowElements = new LinkedList<>();
    List<Integer> highElements = new LinkedList<>();
    for(int i=1,len=source.size();i<len;i++){
        int element = source.get(i);
        if(element<=temp){
            lowElements.add(element);
        }else{
            highElements.add(element);
        }
    }
    lowElements = doOrder(lowElements); //遞歸條件
    highElements = doOrder(highElements); //遞歸條件
    List<Integer> res = new LinkedList<>(lowElements);
    res.add(temp);
    res.addAll(highElements);
    return res;
}

快排的平均時間複雜度O(n log n)

相關文章
相關標籤/搜索