1. 程式人生 > >經典排序演算法——快速排序及其優化

經典排序演算法——快速排序及其優化

快速排序

快速排序的思想:通過一趟排序將資料分割成獨立的兩部分,其中一部分資料都比另一部分的所有資料都要小,然後再按照此方法對這兩部分資料分別進行快速排序,可以遞迴的進行。

時間複雜度:

  • 好的情況(無序的):O(nlog2n)
  • 壞的情況(有序的):O(n^2) 
快速排序法不穩定。

讓我們先通過一個例子來了解一下:

12    5    4   25    10    15

首先:我們選擇 12 為基準,比 12 小的放左邊。

5     4    10    12    25    15

同樣,用快速排序對 12 的前半部分進行排序:

4    5    10    12    25    15

此時,前半部分排序完畢,後半部分一樣用快速排序:

4    5    10    12    15    25

我們可以用遞迴的方法來求解。

下面我們換個例子來深入的理解快速排序演算法。

23  4  34  54  65  76  22  3  56  13  35  28  32  43  5

如下圖所示,為第一趟的快速排序。

此時,23 的前半部分都比 23 小,23 的後半部分都比 23 大。

我們可以對它的前半部分和後半部分分別進行快速排序,直到排序結束。

下面我們用 java 來實現快速排序演算法。

public static void quickSort(int[] array){
	Quick(array,0,array.length -1);
}

public static void Quick(int[] array,int start,int end){
	int par = partion(array,start,end);
	if(par > start + 1){
		Quick(array,start,par-1);//對前半段進行快速排序
	}
	if(par < end-1){
		Quick(array,par+1,end);	//對後半段進行快速排序。
	}
}

下面這個函式是返回基準位置的。即上面的例子中第一趟快速排序後 23 的位置。 

public static int partion(int[] array,int start,int end){
	int tmp = array[start];
	while(start < end){
		while(start < end && array[end] >= tmp){
			--end;
		}
		if(start >= end){
			break;
		}else{
			array[start] = array[end];
		}
		while(start < end && array[start] <= tmp){
			++start;
		}
		if(start >= end){
			break;
		}else{
			array[end] = array[start];
		}
	}
	array[start] = tmp;
	return start;
}

這是我們用遞迴來解決它,其實還有另外一種辦法,我們可以利用棧來解決這個問題。

還是上面的例子,讓我們來分析一下。

23  4  34  54  65  76  22  3  56  13  35  28  32  43  5

首先:我們來給來個快速排序,將它分成兩部分。

5  4  13  23  22  23  76  65  56  54  35   28  32  43  34

然後:和前面一樣,我們分別對左邊和右邊進行快速排序,但不同的是,這次我們要把下標 start 和 end 存入棧中,然後將右邊的下標 start 和 end 也存入棧中。

如圖


接著我們從棧裡取出,使 low 和 high 分別指向 start 和 end ,然後進行快速排序。


依次類推,我們一邊向棧裡取出元素,一邊向棧裡儲存元素,直到棧為空,表示我們的排序結束。

下面我們用 java 實現一下這個非遞迴的。

public static void QuickSort(int[] array){
	int[] stack = new int[array.length];
	int top = 0;
	int low = 0;
	int high = array.length -1;
	int par = partion(array,low,high);
	//入棧
	if(par > low+1){
		stack[top++] = low;
		stack[top++] = par-1;
	}
	if(par < high-1){
		stack[top++] = par+1;
		stack[top++] = high;
	}
	//出棧
	while(top > 0){
		high = stack[--top];
		low = stack[--top];
		par = partion(array,low,high);
		if(par > low+1){
			stack[top++] = low;
			stack[top++] = par-1;
		}
		if(par < high-1){
			stack[top++] = par+1;
			stack[top++] = high;
		}
	}	

快速排序的優化

快速排序有以下優化方案

  1. 當待排序陣列當中資料比較少的時候,用直接插入法。
  2. 聚集相同元素法。
  3. 隨機選取基準法。

當資料比較少時,採用直接插入法排序

當資料比較少時,採用直接插入法排序比較高效。

所以我們只需要在快速排序的方法上增加一個判斷語句即可

以下為用 java 實現

public static void Quick1(int[] array,int start,int end){
		//當資料少於 100 時用直接插入法排序即可
		if((end - start + 1) < 100){
			insertSort(array,start,end);
			return;
		}
		int par = partion(array,start,end);
		if(par > start + 1){
			Quick1(array,start,par-1);
		}
		if(par < end-1){
			Quick1(array,par+1,end);
		}
	}

聚集相同元素法

簡單來說就是找和基準相同的元素,這樣子可以減少遞迴的次數。

下面是把和基準相同的元素聚集到一起。


這裡附上程式碼

    public static int[] focus(int[] array,int start,int end,int par,int left,int right){
		//聚集和基準相同的元素
		//左邊找
		int parleft = par-1;
		int parright = par + 1;
		for(int i = par-1;i >= start;i--){
			if(array[i] == array[par]){
				if(i != parleft){
					swap(array,i,parleft);
					parleft--;
				}else{
					parleft--;
				}
			}
		}
		left = parleft;
		//右邊找
		for(int j = par+1;j <= end;j++){
			if(array[j] == array[par]){
				if(j != parright){
					swap(array,j,parright);
					parright++;
				}else{
					parright++;
				}
			}
		}
		right = parright;
		int[] brray = new int[2];
		brray[0] =left;
		brray[1] = right;
		return brray;
	}

然後我們對於基準左邊和基準右邊分別進行排序,和優化前不同的是我們不用再比較和基準相同的元素了。

對於左邊,我們令 low = 0,high = pl-1。

對於右邊,我們令 low = pr-1, high = 長度-1。

public static void Quick2(int[] array,int start,int end){
		int par = partion(array,start,end);
		int  left = par-1;
		int right = par+1;
		int[] b = focus(array,start,end,par,left,right);
		left = brrar[0];
		right = brrar[1];
		if(par > start + 1){
			Quick2(array,start,left);
		}
		if(par < end-1){
			Quick2(array,right,end);
		}
	}

隨機選取基準法。

在資料已經有效的情況下,快速排序時間複雜度太高,為了解決這個問題,課一隨機選取基準來提高效率。

public static void Quick3(int[] array,int start,int end){
		int par = partion(array,start,end);
		//假設已經有序,就會提高效率
		swap(array, start, (int)(Math.random()*10%(end-start))+start);
		if(par > start + 1){
			Quick(array,start,par-1);
		}
		if(par < end-1){
			Quick(array,par+1,end);
		}
	}

相關推薦

經典排序演算法——快速排序及其優化

快速排序快速排序的思想:通過一趟排序將資料分割成獨立的兩部分,其中一部分資料都比另一部分的所有資料都要小,然後再按照此方法對這兩部分資料分別進行快速排序,可以遞迴的進行。時間複雜度:好的情況(無序的):O(nlog2n)壞的情況(有序的):O(n^2) 快速排序法不穩定。讓我

經典排序演算法——快速排序、歸併排序、堆排序

之前兩篇關於排序演算法的綜述以及平方階複雜度的3種具體型別的排序演算法,這一篇將具體介紹其中平均時間複雜度在平方階O(nlog2n)O(nlog_2n)O(nlog2​n)的三個排序演算法,以及各種演算法的程式碼實現(親測正確)。 快速排序 快速排序是由東尼·霍

歸併排序演算法詳解及其優化

歸併排序演算法: 思想:分治法 每個遞迴過程涉及三個步驟 第一, 分解: 把待排序的 n 個元素的序列分解成兩個子序列, 每個子序列包括 n/2 個元素. 第二, 治理: 對每個子序列分別呼叫歸併排序__MergeSort, 進行遞迴操作 第三

經典排序演算法-快速排序(挖坑法、前後指標法)、基數排序

快速排序在實際應用中是比較表現好的排序演算法。快速排序我用兩種方法實現它。 第一種為方法,形象的稱為:挖坑法 基本思路:1、尋找pos位,然後將其分為兩段陣列,然後對這兩段陣列遞迴排序;      

用JavaScript實現十大經典排序演算法--快速排序

       快速排序,這也是在實際中最常用的一種排序演算法,速度快,效率高。就像名字一樣,快速排序是最優秀的一種排序演算法。 1)演算法原理       快速排序的基本思想:通過一趟排序將待排記錄分

經典排序演算法--快速排序

快速排序原理   快速排序是基於“分治法”原理實現,所謂分治法就是不斷的將原陣列序列按照一定規律進行拆分,拆分後各自實現排序直到拆分到序列只剩下一個關鍵字為止。快速排序首先選取一個關鍵字為標誌位(關鍵字的選取影響排序效率),然後將序列中小於標誌位的關鍵字移動至標誌位左側,大於標誌位的關鍵字移動至右側。一趟比較

java排序演算法快速排序

快速排序 快速排序的思想方法: 1.先從數列中取出一個數作為基準數,記為x。 2.分割槽過程,將不小於x的數全放到它的右邊,不大於x的數全放到它的左邊。(這樣key的位置左邊的沒有大於key的,右邊的沒有小於key的,只需對左右區間排序即可)。 3.再對左右區間重複第二步,直到各區間

小甲魚 排序演算法 快速排序

小甲魚 排序演算法 快速排序   #include <stdio.h> //交換 void swap(int k[], int low, int high) { int temp; temp = k[low]; k[low] = k[high]; k[h

排序演算法快速排序(關鍵詞:資料結構/演算法/排序演算法/快速排序

快速排序 實現 def partition(nums, left, right): middle = (left+right) // 2 pivot = nums[middle] swap(nums, middle, right) # 現在主元 pivot 等於 num

最常用的排序演算法——快速排序

最常用的排序演算法,當然是快速排序演算法了,下面給出可以直接執行的快速排序演算法思想和程式碼: 思想: ①一般設定第一個數為基準數; ②先從右往左找一個小於基準數的數,在從左往右找一個比基準數大的數,然後交換他們;    繼續,直到兩邊探測變數“相遇”; ③交

排序演算法---快速排序,隨機快速排序和雙路快排(python版)

[原文連結](https://blog.csdn.net/m0_37519490/article/details/80648011) 1、什麼是快速排序演算法? 快速排序是由東尼·霍爾所發展的一種排序演算法,速度快,效率高,也是實際中最常用的一種演算法

[排序演算法]--快速排序的Java實現

快速排序 所謂快速排序:基於分治的思想,是氣泡排序的改進型。首先在陣列中選擇一個基準點並把基準點放於序列的開頭(該基準點的選取可能影響快速排序的效率,關於基準點的選擇方法後面再講解),然後分別從陣列的兩端掃描陣列,設兩個指示標誌(lo指向起始位置,hi指

[演算法學習筆記]又一個採用分治法的排序演算法----快速排序演算法

快速排序演算法 快速排序演算法是當前在實際排序應用中最好的選擇,雖然排序演算法最壞情況下的時間複雜度為O(n^2), 但是可以通過隨機化的方式避免最壞情況的發生, 而快速演算法的平均複雜度為O(n lgn), 而且隱含的常數因子非常小, 因此效率十分高.

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

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

排序演算法---快速排序c++實現

#include<iostream> using namespace std; void quicksort(int *list, int s, int t) { if(s < t) { int i = s, j = t; int piv

常用排序演算法——快速排序

快速排序法是一種高效的排序法,演算法的最終效能取決於選取的中間值,直接實現的快速排序法如下: #include <iostream> #include <algorithm> static int partition( int* array, in

Java-時間複雜度為O(nlogn)的排序演算法(快速排序, 歸併排序, 堆排序, 希爾排序)

/** 包含多種靜態排序方法 * Created by Andre on 2016/6/27. */ public class Sorter { /** * 快速排序 * 遞迴形式 * 第一個記錄為樞軸 * 不穩定

排序演算法---快速排序

          快速排序是對氣泡排序的改進,通過一趟排序將要排序的資料分割成獨立的兩個部分,其中一部分的所有資料比另一個部分的所有資料要小,再按照這種方法遞迴進行,使得整個資料變成有序序列。  

排序演算法----快速排序

排序演算法的思路是通過一趟排序將陣列分為兩部分,一部分比另一部分都要小,以此遞迴即可得到最終的排序。複雜度nlogn void Qsort(int a[], int low, int high)//l

排序演算法——快速排序演算法

網際網路的大型公司還在火熱招聘中,參與了一次又一次的筆試,都不通過,我還是太菜!!! 作為程式設計人員,需要邁過去“資料結構與演算法”這個坎,畢竟,筆試不會真的很虧,加油吧,少些水,多點實操。 一、快速排序演算法思想: 從一組資料中找出一個基準值,一般是選擇中間值作為基