1. 程式人生 > >快速排序(遞迴版)

快速排序(遞迴版)

快速排序是最經典的排序演算法,它用途廣泛,效率很高,也經常被拿來檢驗coder的基本功底。

此篇作為快排的學習筆記。

快排的基本思想

快速排序是典型的分治演算法,理解了快排的思想,很容易寫出遞迴版的程式碼。

快排分以下三個步驟.

  1. Choose a pivot value. 選擇一個元素作為(pivot),這裡我一般選擇陣列的中間值作為軸。
  2. Partition.將當前陣列劃分。劃分遵循這樣的規則,所有小於軸的元素,放在軸的左邊,大於軸的元素,放在軸的右邊,等於軸的元素放在哪邊都可以。這樣相當於將一個數組劃分成了兩個部分。
  3. Sort both parts。對劃分後的陣列不斷重複1,2過程,直到陣列有序。

這樣為什麼可以得到有序的陣列呢?

每次劃分操作都將陣列劃分為兩部分。
左半部分的任何元素都將<=右半部分的任何元素。
所以遞迴的重複以上過程,最終得到的陣列一定是有序的。

下面給出這個遞迴過程的虛擬碼

QuickSort(array,low,high)
    if(low < high) : //滿足遞迴條件
        pivotIndex = Partition(array,low,high)
        QuickSort(array,low,pivotIndex - 1)
        QuickSort(array,pivotIndex + 1
,high);

這個思想十分簡單明瞭,難點在於如何編寫Partiton()函式。
實際上,也並不難理解

Partition的編寫與過程分析

我們重述一下Partation函式的作用。

劃分:在陣列中挑選一個作為劃分標準,所有小於軸的元素放到軸的左邊,大於軸的元素放到軸的右邊。然後Partation函式會返回這個最終的位置(同時也是軸在最終有序數列中的位置)

WiKi的虛擬碼:

partition(A, lo, hi)
     pivotIndex := choosePivot(A, lo, hi)
     pivotValue := A[pivotIndex]
     // put the chosen pivot at A
[hi] swap A[pivotIndex] and A[hi] storeIndex := lo // Compare remaining array elements against pivotValue = A[hi] for currIndex from lo to hi−1, inclusive if A[currIndex] < pivotValue swap A[currIndex] and A[storeIndex] storeIndex := storeIndex + 1 swap A[storeIndex] and A[hi] // Move pivot to its final place return storeIndex

分析 : 上述虛擬碼中有兩個重要的變數。

  1. storeIndex : 這個變數指示的是pivot經過一次劃分後的最終座標位置,初始化為low。
  2. currIndex : 顧名思義指示的是當前的迴圈位置

Q:為什麼要將pivot和A[high]預先交換?
A : 首先pivotIndex的選取是沒有什麼規定的,所以這裡以一個函式choosePivot代替。為了實現演算法,我們必須將除了pivot之外的所有元素與pivot比較,如果pivot是A[high]那麼迴圈寫起來會很方便,否則就將pivot與A[high]交換。

Q:for迴圈的作用是什麼?
A:迴圈體內依次遍歷除pivot的所有元素,將所有小於pivot的元素放到pivot最終位置(storeIndex)的左邊。(詳細用圖示解釋)

Q:為何迴圈完畢後還要交換一次?
A:迴圈完畢只是將所有小於pivot的元素放到了storeIndex(pivot最終位置)的左邊,而pivot還是在A[high]位置上,因此要交換。
值得注意的是,這裡不可以寫成swap(A[storeIndex],pivot),雖然還是把pivot放到了最終位置上,但是這樣做並沒有改變A[high]的值(A[high]還是pivot),所以最後一步是必不可少的。

下面給出一個圖示詳細說明一下 :
初始化

青綠色表示所有小於pivot的元素

(3 < pivot,因為curr和store開始指向同一元素,3和3自身交換,s++,c++)
這裡寫圖片描述

(7 > pivot,c++)
這裡寫圖片描述

(4 < pivot,4 和 7 交換,c++,s++)
這裡寫圖片描述

(2 < pivot,2 和 8 交換,c++, s++)
這裡寫圖片描述

(1 < pivot,1 和 7 交換,c++,s++)
這裡寫圖片描述

(迴圈結束)
這裡寫圖片描述

(最後,swap A[storeIndex] and A[hi])
這裡寫圖片描述

C++程式碼

int QuickSort(int array[],int low,int high)
{
    if(low < high){
        int pivotIndex = Partition(array,low,high);//如果可以劃分
        QuickSort(array,low,pivotIndex - 1);
        QuickSort(array,pivotIndex + 1,high);
    }
}
int Partition(int array[],int low,int high)
{
    int pivotIndex = (low + high) / 2;
    int pivot = array[pivotIndex];
    int storeIndex = low;//pivot所在的最終位置
    //put the chosen pivot at array[high]
    swap(array[pivotIndex],array[high]);
    //put the chosen pivot at array[high]

    for(int currIndex = low ; currIndex < high ; ++currIndex){
        if(array[currIndex] <= pivot){//也可以寫<=
            swap(array[currIndex],array[storeIndex]);
            storeIndex++;
        }
    }
    swap(array[storeIndex],array[high]);//move pivot to its final position
    //you can never write swap(array[storeIndex],pivot]
    //cause if you use this form,you can not change the value of array[high]
    //that's wrong
    return storeIndex;
}

相關推薦

快速排序

快速排序是最經典的排序演算法,它用途廣泛,效率很高,也經常被拿來檢驗coder的基本功底。 此篇作為快排的學習筆記。 快排的基本思想 快速排序是典型的分治演算法,理解了快排的思想,很容易寫出遞迴版的程式碼。 快排分以下三個步驟. Choose

【資料結構】八大排序快速排序和非方法

上一博文我們講了氣泡排序,但是由於他的時間複雜度過高為O(n*n),於是在氣泡排序的基礎上今天要說的是快速排序。 本文講述兩個內容: 1.快速排序的三種方法。 2.快速排序的優化 一.什麼是快速排序???      通過一趟排序將要排序的資料分割成獨立的兩部

C語言實現快速排序

#include<stdio.h> void Split(int left,int a[],int right); int Quicksort(int left,int a[],int right); int main() { int N; scanf("

七大排序演算法5------快速排序和非

         在本文中使用到的升序,降序,交換函式的程式碼見:這篇部落格 快速排序(遞迴實現) 快速排序的基本思想是在待排序序列中找到一個基準值(一般取待排序序列的最後一個元素),然後將該基準值放置在一個合適的位置,使得在基準值之前的元素都小於等於基準值,基準值之後的

快速排序法與迭代法

用遞迴法:程式碼簡潔,但執行速度很慢; 用迭代法:程式碼略多,但執行速度很快。 本文快速排序方法: 用兩個指標i和j,分別指向傳進來的低位地址和高位地址。去中間的數為基準值。i從左向右移動,碰到比基準

分治法之歸併排序+分治

/* 歸併排序 思想: 1.分而治之,將一個無序的數列一直一分為二,直到分到序列中只有一個數的時候,這個序列肯定是有序的,因為只有一個數,然後將兩個只含有一個數字的序列合併為含有兩個數字的有序序列,這

java快速排序

       public static void quickSort(int[] array, int start, int end) {               // 設計一個遞迴出口              if (start >= end) {     

[演算法入門]快速排序方法Java實現,大家一起來找茬啊~

基礎 總結一下,快速排序的步驟: 1、找到一個key值(就是陣列第一個值),先從右到左找,找到一個比它小的值,記錄下標。 2、然後從左往右找,找到一個比它大的值,記錄下標。 3、交換找到的兩個數

數組排序-----2.快速排序

比較 temp quick func 個數 ole length int emp /** 先去找數組中間一項,把這一項拿出來; 用拿出之後的數組中的每一項跟 拿出的這一項比較;比這項大的放到一個數組; 小的放到另外一個數組* 接著對著兩個數組做上述同樣的操作;* */ fu

Java實現--歸併排序

《Java資料結構和演算法》如此描述分治演算法: 把一個大問題分成兩個相對來說更小的問題,並且分別解決每一個小問題,對每一個小問題的解決方案是一樣的:把每個小問題分成兩個更小的問題,並且解決它們。這個過程一直持續小去知道達到易於求解的基值情況,就不用再繼續分了。 時間複雜度:

7-17 漢諾塔的非實現25 分附:

題目大意:略。 解題思路:如果考慮一下把64片金盤,由一根柱子上移到另一根柱子上,並且始終保持上小下大的順序。這需要多少次移動呢?這裡需要遞迴的方法。假設有n片,移動最少次數是f(n).顯

歸併排序與非的實現

摘要: (1)歸併排序幾乎以O(NlogN)的時間界實現,是典型的分治演算法; (2)歸併排序的基本思路很簡單:就是將目標序列分為兩個部分,將兩個子序列排序好之後,再將它們合併。注意到合併兩個已排序

【資料結構】求簡單迷宮是否存在路徑和非

求簡單迷宮是否存在路徑 程式碼中用到的棧程式碼如下: stack.h #pragma once #include<stdio.h> #include<stddef.h> #include"Maze.h" typedef Pos SeqType

c 和 Python 實現歸併排序 ,非

C: #include <iostream>   using namespace std;   #define MAXSIZE 9 typedef struct {     int r[MAXSIZE+1];     int length; }SqList;

歸併排序和非方法實現

/* 歸併排序 VS2010 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define OK 1 #define ERROR 0 #define MAX

排序】歸併排序和非版本

#include<iostream> using namespace std; void merge(int* a, int* temp, int begin, int middle, int end){ int i = begin; int j = mi

直接插入排序高級之C++實現

include ostream 源代碼 cpp -s 臨時 ios 結束 中間變量 直接插入排序(高級版)之C++實現 一、源代碼:InsertSortHigh.cpp 1 /*直接插入排序思想: 2  假設待排序的記錄存放在數組R[1..n]中。初始時,R[1]自成

算法——python實現快速排序二分法思想

append exc microsoft 部分 input temp style 數字 快速排序 實現思路   將所需要的數字存入一個列表中 首先,設置將最左側的那個數設置為基準數,在列表中索引為0 然後設置兩個移動位(用於比較),分別為最左邊和最右邊 然後最右邊那位向左

劍指offer——正則表示式匹配呼叫

當模式中的第二個字元不是“*”時: 1、如果字串第一個字元和模式中的第一個字元相匹配,那麼字串和模式都後移一個字元,然後匹配剩餘的。 2、如果 字串第一個字元和模式中的第一個字元相不匹配,直接返回false。 而當模式中的第二個字元是“*”時: 如果字串第一個字元跟模式第一個字元

集合的全排列問題實現

設R={r1,r2,r3,.....rn}要進行全排列的n個元素,集合X中元素的全排列記為perm(X),則(ri)perm(X)表示在全排列perm(X)的每一個排列前加上字首ri得到的排列。R的全排列定義可歸納定義如下: 當n=1時,perm(R) = (r),其中r為集合R中唯一元素 當n>1