1. 程式人生 > >快速排序 (Quick Sort)

快速排序 (Quick Sort)

文章轉載:http://bubkoo.com/2014/01/12/sort-algorithm/quick-sort/#javascript-語言實現

C. R. A. HoareC. R. A. Hoare

分治法的基本思想是:將原問題分解為若干個規模更小但結構與原問題相似的子問題。遞迴地解這些子問題,然後將這些子問題的解組合為原問題的解。

利用分治法可將快速排序的分為三步:

  1. 在資料集之中,選擇一個元素作為”基準”(pivot)。
  2. 所有小於”基準”的元素,都移到”基準”的左邊;所有大於”基準”的元素,都移到”基準”的右邊。這個操作稱為分割槽 (partition) 操作,分割槽操作結束後,基準元素所處的位置就是最終排序後它的位置。
  3. 對”基準”左邊和右邊的兩個子集,不斷重複第一步和第二步,直到所有子集只剩下一個元素為止。
    圖片來自維基百科圖片來自維基百科分割槽是快速排序的主要內容,用虛擬碼可以表示如下:
12345678910 function partition(a, left, right, pivotIndex) pivotValue := a[pivotIndex] swap(a[pivotIndex], a[right]) // 把 pivot 移到結尾 storeIndex := left for i from left to right-1 if a[i] < pivotValue swap(a[storeIndex], a[i]) storeIndex := storeIndex + 1 swap(a[right], a[storeIndex]) // 把 pivot 移到它最後的地方 return storeIndex // 返回 pivot 的最終位置

首先,把基準元素移到結尾(如果直接選擇最後一個元素為基準元素,那就不用移動),然後從左到右(除了最後的基準元素),迴圈移動小於等於基準元素的元素到陣列的開頭,每次移動 storeIndex 自增 1,表示下一個小於基準元素將要移動到的位置。迴圈結束後 storeIndex 所代表的的位置就是基準元素的所有擺放的位置。所以最後將基準元素所在位置(這裡是 right)與 storeIndex 所代表的的位置的元素交換位置。要注意的是,一個元素在到達它的最後位置前,可能會被交換很多次。

一旦我們有了這個分割槽演算法,要寫快速排列本身就很容易:

123456 procedure quicksort(a, left, right) if
right > left select a pivot value a[pivotIndex] pivotNewIndex := partition(a, left, right, pivotIndex) quicksort(a, left, pivotNewIndex-1) quicksort(a, pivotNewIndex+1, right)

例項分析

舉例來說,現有陣列 arr = [3,7,8,5,2,1,9,5,4],分割槽可以分解成以下步驟:

  1. 首先選定一個基準元素,這裡我們元素 5 為基準元素(基準元素可以任意選擇):
123 pivot ↓3 7 8 5 2 1 9 5 4
  1. 將基準元素與陣列中最後一個元素交換位置,如果選擇最後一個元素為基準元素可以省略該步:
123 pivot ↓3 7 8 4 2 1 9 5 5
  1. 從左到右(除了最後的基準元素),迴圈移動小於基準元素 5 的所有元素到陣列開頭,留下大於等於基準元素的元素接在後面。在這個過程它也為基準元素找尋最後擺放的位置。迴圈流程如下:

    迴圈 i == 0 時,storeIndex == 0,找到一個小於基準元素的元素 3,那麼將其與 storeIndex 所在位置的元素交換位置,這裡是 3 自身,交換後將 storeIndex 自增 1,storeIndex == 1:

    12345 pivot ↓ 3 7 8 4 2 1 9 5 5 ↑storeIndex

    迴圈 i == 3 時,storeIndex == 1,找到一個小於基準元素的元素 4:

    12345 ┌───────┐ pivot ↓ ↓ ↓ 3 7 8 4 2 1 9 5 5 ↑ ↑storeIndex i

    交換位置後,storeIndex 自增 1,storeIndex == 2:

    12345 pivot ↓3 4 8 7 2 1 9 5 5 ↑ storeIndex

    迴圈 i == 4 時,storeIndex == 2,找到一個小於基準元素的元素 2:

    12345 ┌───────┐ pivot ↓ ↓ ↓3 4 8 7 2 1 9 5 5 ↑ ↑ storeIndex i

    交換位置後,storeIndex 自增 1,storeIndex == 3:

    12345 pivot ↓3 4 2 7 8 1 9 5 5 ↑ storeIndex

    迴圈 i == 5 時,storeIndex == 3,找到一個小於基準元素的元素 1:

    12345 ┌───────┐ pivot ↓ ↓ ↓3 4 2 7 8 1 9 5 5 ↑ ↑ storeIndex i

    交換後位置後,storeIndex 自增 1,storeIndex == 4:

    12345 pivot ↓3 4 2 1 8 7 9 5 5 ↑ storeIndex

    迴圈 i == 7 時,storeIndex == 4,找到一個小於等於基準元素的元素 5:

    12345 ┌───────────┐ pivot ↓ ↓ ↓3 4 2 1 8 7 9 5 5 ↑ ↑ storeIndex i

    交換後位置後,storeIndex 自增 1,storeIndex == 5:

    12345 pivot ↓3 4 2 1 5 7 9 8 5 ↑ storeIndex
  2. 迴圈結束後交換基準元素和 storeIndex 位置的元素的位置:

12345 pivot ↓3 4 2 1 5 5 9 8 7 ↑ storeIndex

那麼 storeIndex 的值就是基準元素的最終位置,這樣整個分割槽過程就完成了。

引用維基百科上的一張圖片:

圖片來自維基百科圖片來自維基百科

JavaScript 語言實現

查看了很多關於 JavaScript 實現快速排序方法的文章後,發現絕大多數實現方法如下:

1234567891011121314151617 function quickSort(arr) {   if (arr.length <= 1) { return arr; }   var pivotIndex = Math.floor(arr.length / 2);   var pivot = arr.splice(pivotIndex, 1)[0];   var left = [];   var right = [];   for (var i = 0; i < arr.length; i++) {     if (arr[i] < pivot) {       left.push(arr[i]);     } else {       right.push(arr[i]);     }   }   return quickSort(left).concat([pivot], quickSort(right));}

上面簡單版本的缺點是,它需要Ω(n)的額外儲存空間,也就跟歸併排序一樣不好。額外需要的儲存器空間配置,在實際上的實現,也會極度影響速度和快取記憶體的效能。

摘自維基百科

按照維基百科中的原地(in-place)分割槽版本,實現快速排序方法如下:

123456789101112131415161718192021222324252627282930313233 function quickSort(array) { // 交換元素位置 function swap(array, i, k) { var temp = array[i]; array[i] = array[k]; array[k] = temp; } // 陣列分割槽,左小右大 function partition(array, left, right) { var storeIndex = left; var pivot = array[right]; // 直接選最右邊的元素為基準元素 for (var i = left; i < right; i++) { if (array[i] < pivot) { swap(array, storeIndex, i); storeIndex++; // 交換位置後,storeIndex 自增 1,代表下一個可能要交換的位置 } } swap(array, right, storeIndex); // 將基準元素放置到最後的正確位置上 return storeIndex; } function sort(array, left, right) { if (left > right) { return; } var storeIndex = partition(array, left, right); sort(array, left, storeIndex - 1); sort(array, storeIndex + 1, right); } sort(array, 0, array.length - 1); return array;}

另外一個版本,思路和上面的一樣,程式碼邏輯沒有上面的清晰

12345678910111213141516171819202122232425262728293031323334353637383940414243444546 function quickSort(arr) { return sort(arr, 0, arr.length - 1); function swap(arr, i, k) { var temp = arr[i]; arr[i] = arr[k]; arr[k] = temp; } function sort(arr, start, end) { sort(arr, 0, arr.length - 1); return arr; function swap(arr, i, k) { var temp = arr[i]; arr[i] = arr[k]; arr[k] = temp; } function sort(arr, start, end) { if (start >= end) return; var pivot = arr[start], i = start + 1, k = end; while (true) { while (arr[i] < pivot) { i++; } while (arr[k] > pivot) { k--; } if (i >= k) { break; } swap(arr, i, k); } swap(arr, start, k); sort(arr, start, Math.max(0, k - 1)); sort(arr, Math.min(end, k + 1), end); } }}

參考文章


相關推薦

排序快速排序Quick Sort

++ sort rgs partition 重復 foreach 一聲 reac 中一 原理,通過一趟掃描將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整

【 python 學習筆記 -- 數據結構與算法 】快速排序 Quick Sort

mark 效率 空間 eight png orm 歸並 應該 筆記 【快速排序】:   利用遞歸算法, 首先選擇一個基準值(pivot value),這裏我們選列表的第一個值作為例。這個基準值的作用是協助列表的分割。   這個基準值在正序列表中的正確位置,我們稱之為分割點(

快速排序 (Quick Sort)

文章轉載:http://bubkoo.com/2014/01/12/sort-algorithm/quick-sort/#javascript-語言實現 C. R. A. Hoare 分治法的基本思想是:將原問題分解為若干個規模更小但結構與原問題相似的子問題。遞

兩種風格的快速排序 Quick Sort

啊哈演算法中的quick_sort.cpp // // Created by jal on 18-9-2. // #include <bits/stdc++.h> using na

快速排序Quick Sort

sin aik tex 狀態 整體 splay 好的 優化 return 基本思想:   通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對著兩部分記錄進行排序,以達到整體有序的目的。 通俗點說就是:   在待排記錄中

quick sort / quick select sort 快排 / 快速選擇排序

快速排序(快排)和快速選擇排序,是兩種平均時間複雜度非常高效的演算法,其中: 快排平均時間複雜度是O(nlogn),最壞時間複雜度是O( n2 n 2

javascript quicksort quick sort, insertion sort 三分中值法 快速排序 插入排序

* Arr.js function Arr() { this.cmp = Arr.defaultCompareFunction; } Arr.prototype = []; Arr.fromArray = function(/*Array */a) /* :Arr */ { var

排序演算法(六):快速排序Quick Sort

基本思想: 1)選擇一個基準元素,通常選擇第一個元素或者最後一個元素, 2)通過一趟排序講待排序的記錄分割成獨立的兩部分,其中一部分記錄的元素值均比基準元素值小。另一部分記錄的 元素值比基準值大。 3)此時基準元素在其排好序後的正確位置 4)然後分別對這兩部分記錄用同樣

快速排序quick sort)

前面兩個小節,我們分別介紹了氣泡排序,插入排序,在我們本節講解一種更加高效的排序方法,快速排序,也即是常說的快排。 1、演算法思想 實際上,快排是對氣泡排序的一種改進,採用分而治之的思想。 在待排序表 A[1,2…n]中選取一個基準值pivot(通常選取序列首元素); 通過一趟排序操

PHP實現排序演算法----快速排序Quick Sort)、快排

基本思想: 快速排序(Quicksort)是對氣泡排序的一種改進。他的基本思想是:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行快速排序,整個排序過程可以遞迴進行,以達到整個序列有序的目的

quick sort(快速排序)

Quicksort or partition-exchange sort, is a fast sorting algorithm, which is using divide and conquer algorithm. Quicksort first di

快速排序quick sort)的Python實現

#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/8/11 12:59 AM # @Author : Blake import random def quick_sort(array):

Quick Sort快速排序

演算法思想        通過快速排序將要排序的資料分為獨立的兩部分,其中一部分的資料比另外一部分的資料都要小,然後再按照此方法對這兩部分資料分別再進行快速排序,整個過程可以遞迴進行,以此達到整個序列都有序。快速排序和歸併排序是互補的:歸併排序將序列分成兩個子序列分別排序,

javascript quicksort quick sort, insertion sort 三分中值法 快速排序 插入排序 heapsort, mergesort

* Arr.js function Arr() { this.cmp = Arr.defaultCompareFunction; } Arr.prototype = []; Arr.fromArray = function(/* Array */ a) {

C標準庫中的快速排序quick-sort)函式 [簡單應用]

#include <iostream> #include <cstdlib> using namespace std; const size_t INDEX_ZERO = 0; int com(const void *a,const void *

006.交換排序快速排序Quick Sort

基本思想:1)選擇一個基準元素,通常選擇第一個元素或者最後一個元素,2)通過一趟排序講待排序的記錄分割成獨立的兩部分,其中一部分記錄的元素值均比基準元素值小。另一部分記錄的 元素值比基準值大。3)此時基準元素在其排好序後的正確位置4)然後分別對這兩部分記錄用同樣的方法繼續進行

js實現冒泡排序(bubble sort快速排序quick sort)歸並排序(merge sort

mergesort dep lse small sort code n) ble 排序效率 排序問題相信大家都比較熟悉了。用js簡單寫了一下幾種常用的排序實現。其中使用了es6的一些語法,並且不僅限於數字——支持各種類型的數據的排序。那麽直接上代碼: function co

Quick Sort(三向切分的快速排序)(Java)

情況 ann int 方法 性能提升 ring system.in time clas 1 //三向切分的快速排序 2 //這種切分方法對於數組中有大量重復元素的情況有比較大的性能提升 3 4 public static void main(String

148 Sort List 鏈表上的歸並排序快速排序

UC 快速排序 歸並 get int key 方法 struct pos 在使用O(n log n) 時間復雜度和常數級空間復雜度下,對鏈表進行排序。 詳見:https://leetcode.com/problems/sort-list/description/ 方法一:歸

排序快速排序 && 選擇排序 && sort() 比較

#include <iostream> #include <cstdio> #include <algorithm> #include <ctime> #include <cstdlib> using namespace std; co