排序總結(不斷更新)


排序法 java

  最好時間分析  算法

最差時間分析 windows

平均時間複雜度 數組

穩定度 函數

空間複雜度 性能

冒泡排序 測試

O(n)(改進的冒泡排序) ui

O(n2) spa

O(n2) .net

穩定

O(1)

快速排序

O(n*log2n)

O(n2)

O(n*log2n)

不穩定

O(log2n)~O(n)

選擇排序

O(n2)

O(n2)

O(n2)

不穩定

O(1)

二叉樹排序


O(n2)

O(n*log2n)


O(n)

插入排序

O(n)

O(n2)

O(n2)

穩定

O(1)

堆排序

O(n*log2n)

O(n*log2n)

O(n*log2n)

不穩定

O(1)

希爾排序

/

與增量序列的選取有關

與增量序列的選取有關

不穩定

O(1)

歸併排序

O(n*log2n)
O(n*log2n)
O(n*log2n)
穩定 O(n)

本文中的排序驗證OJ:http://pta.patest.cn/pta/test/18/exam/4/question/633

OJ測試點說明:

  • 數據1:只有1個元素;
  • 數據2:11個不相同的整數,測試基本正確性;
  • 數據3:103個隨機整數;
  • 數據4:104個隨機整數;
  • 數據5:105個隨機整數;
  • 數據6:105個順序整數;
  • 數據7:105個逆序整數;
  • 數據8:105個基本有序的整數;
  • 數據9:105個隨機正整數,每一個數字不超過1000。
  • 排序算法穩定性:

        假定在待排序的記錄序列中,存在多個具備相同的關鍵字的記錄,若通過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj以前,而在排序後的序列中,ri仍在rj以前,則稱這種排序算法是穩定的;不然稱爲不穩定的。

        對於不穩定的排序算法,只要舉出一個實例,便可說明它的不穩定性;而對於穩定的排序算法,必須對算法進行分析從而獲得穩定的特性。須要注意的是,排序算法是否爲穩定的是由具體算法決定的,不穩定的算法在某種條件下能夠變爲穩定的算法,而穩定的算法在某種條件下也能夠變爲不穩定的算法。

        穩定的算法在排序複雜數據時能保持一樣數據的本來順序不變,好比你已經有一組按年齡排好的學生信息,你想按照身高排序而且相同身高時,年齡是有序的,這時穩定的算法才能達到要求。

    冒泡排序:

    冒泡排序算法的運做以下:(從後往前)

    1. 比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。
    2. 對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
    3. 針對全部的元素重複以上的步驟,除了最後一個。
    4. 持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較。

    代碼:

    public void bubbleSort(int num[])
    	{
    		for (int i = 0; i < num.length - 1; i++)
    		{
    			for (int j = 0; j < num.length - 1 - i; j++)
    			{
    				if (num[j] > num[j + 1])
    				{
    					int temp = num[j];
    					num[j] = num[j + 1];
    					num[j + 1] = temp;
    				}
    			}
    		}
    	}
    性能分析:

    須要n(n-1)/2次比較,複雜度爲O(n^2),最差最好都是O(n^2),空間複雜度爲O(1),可是若是排好序的很明顯一次都不用移動,應該是O(n)纔對。

    改進的冒泡排序:

    加了一個標誌位來判斷是否有過交換。使最好的排序複雜度爲O(n)

    public void bubbleSort(int num[])
    	{
    		boolean swap = false;
    		for (int i = 0; i < num.length; i++)
    		{
    			swap = false;
    			for (int j = 0; j < num.length - 1 - i; j++)
    			{
    				if (num[j] > num[j + 1])
    				{
    					int temp = num[j];
    					num[j] = num[j + 1];
    					num[j + 1] = temp;
    					swap = true;
    				}
    			}
    			if (!swap)
    			{
    				break;
    			}
    		}
    	}

    改進後的冒泡排序代碼在OJ上的運行狀況:

    能夠發如今數據達到10^5時就超時了,固然在順序序列時依舊可以經過。

    插入排序:

    插入排序的基本思想是:

        每步將一個待排序的紀錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到所有插入完爲止。

    代碼:

    public static void InsertSort(int num[], int N)
    	{
    		int temp = 0;
    		int j = 0;
    		for (int i = 1; i < N; i++)
    		{
    			temp = num[i];
    			for (j = i; j > 0 && temp < num[j - 1]; j--)
    			{
    				num[j] = num[j - 1];
    			}
    			num[j] = temp;
    		}
    	}

    插入排序在OJ上的運行狀況:

    性能分析:

    插入排序的最好狀況是,待排序列正好是正序的,此時每次只要在末尾插入數字便可,不須要進行移位判斷,此時時間複雜度爲O(n);最壞狀況是,待排序列是逆序的,此時每次都要移位全部的數字,騰出第一個位置再插入,此時時間複雜度爲O(n^2)。

    咱們能夠發現,冒泡排序和插入排序交換的次數是相同的。這裏提出逆序對的概念,逆序對就是對於下標i<j,若是A[i]>A[j],則稱(i,j)是一對逆序對。每次交換就是消滅了一對逆序對,因此交換次數就是逆序對的個數。

    因此插入排序的複雜度T(N,I)=O(N+I),I是逆序對的個數。因此若是I不多(基本有序),則插入排序簡單高效。

    這裏提出個定理:任意N個不一樣元素組成的序列平均具備N(N-1)/4個逆序對。

    定理:任何僅以交換相鄰兩元素來排序的算法,其平均複雜度爲Ω(n^2)(Ω指的是下界)。

    這意味着:要提升算法效率,咱們必須每次消去不止一個逆序對,每次交換相隔較遠的的2個元素。

    希爾排序:

    克服先前所說的僅交換相鄰元素的缺點,提出了一種新的排序方法。

    希爾排序的基本思想是:

    希爾排序是把記錄按下標的必定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減小,每組包含的關鍵詞愈來愈多,當增量(增量要互質)減至1時,整個文件恰被分紅一組,算法便終止。

    低(好比2)間隔有序的序列,高間隔也有序(好比5)。

    原始希爾排序:

    public static void ShellSort(int num[], int N)
    	{
    		int temp = 0;
    		int j = 0;
    		for (int n = N / 2; n > 0; n = n / 2)
    		{
    			for (int i = n; i < N; i++)
    			{
    				temp = num[i];
    				for (j = i; j >= n && temp < num[j - n]; j = j - n)
    				{
    					num[j] = num[j - n];
    				}
    				num[j] = temp;
    			}
    		}
    	}

    最壞狀況θ(n^2)(θ表示便是上界也是下界,代表增加速度是跟n^2同樣快的)

    希爾排序在OJ上運行狀況:

    能夠發現相比於插入排序來講,快了不少,速度比較穩定。


    性能分析:

    上述代碼最壞狀況的緣由是增量不互質(好比8,4,2,1)時,就如同插入排序。增量序列的選擇對希爾排序的時間複雜度影響很大。

    已知的最好步長序列是由Sedgewick提出的(1, 5, 19, 41, 109,...),該序列的項來自

    這兩個算式。

    通常來講希爾排序的速度在插入排序和快速排序之間。

    選擇排序:

    選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工做原理是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到所有待排序的數據元素排完。 選擇排序是不穩定的排序方法(好比序列[5, 5, 3]第一次就將第一個[5]與[3]交換,致使第一個5挪動到第二個5後面)。

    public static void SelectionSort(int num[], int N)
    	{
    		int min = 0;
    		for (int i = 0; i < N - 1; i++)
    		{
    			min = i;
    			for (int j = i; j < N; j++)
    			{
    				if (num[j] < num[min])
    				{
    					min = j;
    				}
    			}
    			if (min != i)
    			{
    				int temp = num[min];
    				num[min] = num[i];
    				num[i] = temp;
    			}
    		}
    	}

    選擇排序在OJ上的結果:


    性能分析:

    不管最好最壞狀況,都須要n(n-1)/2次比較,最壞狀況須要每次交換,最好狀況不交換,因此不管最好最壞都是O(n^2)

    發現選擇排序和冒泡排序其實差很少,每次都是最大(小)的數字交換到開頭,那二者有什麼區別呢?

    1. 冒泡排序是穩定的,選擇排序不穩定。

    2. 選擇排序每次只交換一組數據,冒泡排序可能交換屢次,可是二者比較次數是同樣的。

    3. 冒泡最壞的狀況複雜度纔是O(n^2),最好是O(n), 選擇平均複雜度就是O(n^2) 可是冒泡的最壞狀況處理要比選擇慢。

    堆排序:

    選擇排序太慢了,時間複雜度達到O(n^2)。如何減小時間複雜度呢?因爲在選擇排序中找到最小元須要O(n)的時間複雜度,有沒有更快的方法呢?咱們想到了堆。堆排序實際上是對選擇排序的一種改進。

    public static void HeapSort(int num[], int N)
    	{
    		BuildMinHeap(num);//O(n)
    		int[] temp = new int[N];
    		for (int i = 0; i < N; i++)
    		{
    			temp[i] = DeleteMin(num);//O(logN)
    		}
    		for (int i = 0; i < N; i++)
    		{
    			num[i] = temp[i];//O(n)
    		}
    	}
    很直觀的想法,創建一個最小堆,而後不斷取出根節點,保存到臨時數組中,爲了與其餘算法保持一致,咱們還需把臨時數組賦值給本來的數組。時間複雜度爲O(NlogN),可是空間複雜度就很高了。其實複製temp數組給num數組又費時間又費空間,有沒有辦法把這一步簡化掉呢?

    咱們想到一種方法:不創建最小堆,創建最大堆,建好堆之後,把根節點和最後一位進行交換(交換後最大的那個數放到了最後一位)。而後排除最後一位,繼續建最大堆,重複這個過程。

    public static void HeapSort(int num[], int N)
     {
         BuildMaxHeap(num, N);//創建最大堆O(N)
         for (int i = N - 1; i > 0; i--)
         {
             int temp = num[0];
             num[0] = num[i];
             num[i] = temp;
             MaxHeapFixdown(num, i, 0);//這個就至關於只調整根元素,只須要一次logN
         }
     }
    
    
     private static void BuildMaxHeap(int num[], int N)
     {
         for (int i = (N % 2 == 0 ? (N - 1) / 2 : (N - 2) / 2); i >= 0; i--)
         {
             MaxHeapFixdown(num, N, i);
         }
     }
    
    
     private static void MaxHeapFixdown(int[] num, int N, int i)
     {
         int child = 0;
         int temp = num[i];
         int j = 0;
         for (j = i; (j * 2 + 1) < N; j = child)
         {
             child = 2 * j + 1;
             if (2 * j + 2 < N && num[2 * j + 1] < num[2 * j + 2])
             {
                 child++;
             }
             if (temp > num[child])
             {
                 break;
             }
             else
             {
                 num[j] = num[child];
             }
         }
         num[j] = temp;
     }

    堆排序在OJ上的結果:

    因爲每次從新恢復堆的時間複雜度爲O(logN),共N - 1次從新恢復堆操做,再加上前面創建堆時間複雜度也爲O(N)。二次操做時間相加仍是O(N * logN)。故堆排序的時間複雜度爲O(N * logN)。

    定理:堆排序處理N個不一樣元素的隨機排列的平均比較次數是:2NlogN-O(NloglogN)

    雖然堆排序給出最佳平均時間複雜度,但實際效果不如Sedgewick增量序列的希爾排序

    歸併排序:

    思想很簡單,下面的圖就一目瞭然了,整體思想就是「分解」和「合併」

    最樸素的遞歸的思想:把總體數組分紅兩組,而後每組再分紅兩組,直到兩個數組總體有序,再不斷合併起來。

    private static void MergeSort(int[] num, int n)
    	{
    		int temp[] = new int[n];
    		MSort(num, temp, 0, n - 1);
    	}
    
    	public static void MSort(int num[], int temp[], int left, int right)
    	{
    		int center;
    		if (left < right)
    		{
    			center = (left + right) / 2;
    			MSort(num, temp, left, center);
    			MSort(num, temp, center + 1, right);
    			Merge(num, temp, left, center + 1, right);
    		}
    	}
    
    	/**
    	 * 
    	 * @param num
    	 *            待排數組
    	 * @param temp
    	 *            臨時數組
    	 * @param left
    	 *            第一個數組的第一個位置
    	 * @param right
    	 *            第二個數組的第一個位置(兩個數組是緊挨着的)
    	 * @param end
    	 *            最後一個位置
    	 */
    	private static void Merge(int[] num, int[] temp, int left, int right,
    			int end)
    	{
    		int tmp = left;// 指向插入到temp數組的位置
    		int leftEnd = right - 1;
    		int sum = end - left + 1;
    		while (left <= leftEnd && right <= end)
    		{
    			if (num[left] < num[right])
    			{
    				temp[tmp++] = num[left++];
    			}
    			else
    			{
    				temp[tmp++] = num[right++];
    			}
    		}
    		while (left <= leftEnd)
    		{
    			temp[tmp++] = num[left++];
    		}
    		while (right <= end)
    		{
    			temp[tmp++] = num[right++];
    		}
    		for (int i = end; sum > 0; i--, sum--)//left已經改變,只能從後往前賦值
    		{
    			num[i] = temp[i];
    		}
    	}




    歸併排序在OJ上的結果:


    因爲分治的思想,而且一次merge的時間複雜度爲O(n),因此T(n)=T(n/2)+T(n/2)+O(n)=>T(n)=O(NlogN),歸併排序的平均時間複雜度,最差最好時間複雜度都是爲O(NlogN)

    在這裏簡單證實一下:

    T(n)=2T(n/2)+O(n)=>T(n)=2^k*(T(n/(2^k)))+k*O(n)

    取2^k=n => k=logn => T(n)=n*T(1)+O(nlogn) => T(n)=O(nlogn)

    在上述代碼中,臨時數組temp[]是在MergeSort中就聲明瞭,可是隻有在Merge時纔用到,在MSort時每次都被傳來傳去的,爲何不在Merge時聲明呢?


    上圖給瞭解釋,若是在Merge中聲明,則要不斷的聲明而後釋放,若是一開始就聲明,Merge時用的都是一個數組。

    非遞歸的歸併排序

    上面給出了遞歸方式的歸併排序,咱們知道遞歸對棧的開銷很大,而且速度上也會慢,有沒有非遞歸的歸併排序呢?


    固然上圖是非遞歸的思想。咱們能夠看出,和遞歸的分治思想相比,非遞歸的感受就是一種合併的過程,不斷合併,直到有序。

    private static void MergeSort(int[] num, int n)
    	{
    		int temp[] = new int[n];
    		int length = 1;
    		while (length < n)
    		{
    			MergePass(num, temp, n, length);//把num賦給了temp
    			length = length * 2;
    			MergePass(temp, num, n, length);//有可能這一步會多餘,可是確保了最後結果會在num中
    			length = length * 2;
    		}
    
    	}
    
    	private static void MergePass(int[] num, int[] temp, int n, int length)
    	{
    		int i = 0;
    		for (i = 0; i <= n - 2 * length; i = i + 2 * length)
    		{
    			Merge(num, temp, i, i + length, i + 2 * length - 1);
    		}
    		if (i + length < n)//剩餘兩個不等長的數組
    		{
    			Merge(num, temp, i, i + length, n - 1);
    		}
    		else//就剩一個
    		{
    			for (int j = i; j < n; j++)
    			{
    				temp[j] = num[j];
    			}
    		}
    	}
    	
    	/**
    	 * 
    	 * @param num
    	 *            待排數組
    	 * @param temp
    	 *            臨時數組
    	 * @param left
    	 *            第一個數組的第一個位置
    	 * @param right
    	 *            第二個數組的第一個位置(兩個數組是緊挨着的)
    	 * @param end
    	 *            最後一個位置
    	 */
    	private static void Merge(int[] num, int[] temp, int left, int right,
    			int end)
    	{
    		int tmp = left;// 指向插入到temp數組的位置
    		int leftEnd = right - 1;
    		int sum = end - left + 1;
    		while (left <= leftEnd && right <= end)
    		{
    			if (num[left] < num[right])
    			{
    				temp[tmp++] = num[left++];
    			}
    			else
    			{
    				temp[tmp++] = num[right++];
    			}
    		}
    		while (left <= leftEnd)
    		{
    			temp[tmp++] = num[left++];
    		}
    		while (right <= end)
    		{
    			temp[tmp++] = num[right++];
    		}
    		//最後再也不賦值給num
    	}




    非遞歸歸併在OJ上的結果:


    與遞歸的相比彷佛沒什麼太大變化,時間複雜度都是O(NlogN),Merge函數有了一點區別,再也不最後再次賦給了num數組,爲何使最終結果在num數組中,MergeSort函數while循環中每次都執行兩次Merge,固然在最後時,最後一個Merge有多是多餘的操做,可是爲了確保結果在num中,也就不kao慮這些了。

    在實際應用中,歸併排序常常被用於外排序(所有元素不能在內存中一次完成)

    快速排序:

    該方法的基本思想是:

    1.先從數列中取出一個數做爲基準數(pivot)。

    2.分區過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。

    3.再對左右區間重複第二步,直到各區間只有一個數。

    性能分析:

    快排最好的狀況是,每次正好中分,複雜度爲O(nlogn)。最差狀況,複雜度爲O(n^2),退化成冒泡排序

    快排中有些要注意的問題:

    第一個問題:pivot的選擇

    剛剛談到快排的最壞狀況。最快狀況和pivot的選擇有關,假設咱們選擇了第一個元素做爲pivot,當待排序列爲順序時,每次將除了pivot之外的其餘元素再次遞歸,

    時間複雜度T(n)=T(n-1)+O(n)=>T(n)=O(n^2),退化成冒泡排序。

    經典的取pivot的方法有如下幾種:

    1. 取頭、中、尾的中位數

    public static int mid3(int[] arr, int left, int right)
    	{
    		int mid = (left + right) / 2;
    		int temp;
    		if (arr[left] > arr[mid])
    		{
    			temp = arr[left];
    			arr[left] = arr[mid];
    			arr[mid] = temp;
    		}
    		if (arr[left] > arr[right])
    		{
    			temp = arr[left];
    			arr[left] = arr[right];
    			arr[right] = temp;
    		}
    		if (arr[mid] > arr[right])
    		{
    			temp = arr[mid];
    			arr[mid] = arr[right];
    			arr[right] = temp;
    		}
    		temp = arr[mid];
    		arr[mid] = arr[right - 1];
    		arr[right - 1] = temp;
    		//Sort(arr, left + 1, right - 2);
    		return arr[right - 1];
    	}

    頭、中、尾按大小排好,把中當作pivot,移到right-1處,此時只需將left+1到right-2排序就能夠了,由於left確定比mid小,right確定比mid大。

    2. 取頭

    3. 取尾

    4. 隨機函數(隨機函數的代價很大,不建議)

    第二個問題:若是有元素等於pivot,是換仍是不換呢?

    1. 若是換,那當遇到全是同樣的數,每一次都要交換,可是有一個好處是,pivot會移動到中間的地方,正好中分,符合快排最好的狀況,複雜度爲O(nlogn)

    2. 若是不換,一樣是全是同樣的數,的確不用交換,i指針一直移到最後遇到j指針,pivot被移動到最後一位。分治時,右邊沒有元素,左邊n-1個元素,重複一下。符合快排最壞狀況,複雜度爲O(n^2)

    綜上,仍是換吧。


    private static void quickSort(int[] arr, int n)
    	{
    		Sort(arr, 0, n - 1);
    	}
    
    	public static void Sort(int[] arr, int left, int right)
    	{
    		if (left >= right)
    		{
    			return;
    		}
    		int pivot = mid3(arr, left, right);
    		int i = left;
    		int j = right - 1;
    		for (;;)
    		{
    			while (i < right - 1 && arr[++i] < pivot)
    				;
    			while (j > left + 1 && arr[--j] > pivot)
    				;
    			if (i < j)
    			{
    				int temp = arr[i];
    				arr[i] = arr[j];
    				arr[j] = temp;
    			}
    			else
    			{
    				break;
    			}
    		}
    		arr[right - 1] = arr[i];
    		arr[i] = pivot;
    		Sort(arr, left, i - 1);
    		Sort(arr, i + 1, right);
    	}
    
    	public static int mid3(int[] arr, int left, int right)
    	{
    		int mid = (left + right) / 2;
    		int temp;
    		if (arr[left] > arr[mid])
    		{
    			temp = arr[left];
    			arr[left] = arr[mid];
    			arr[mid] = temp;
    		}
    		if (arr[left] > arr[right])
    		{
    			temp = arr[left];
    			arr[left] = arr[right];
    			arr[right] = temp;
    		}
    		if (arr[mid] > arr[right])
    		{
    			temp = arr[mid];
    			arr[mid] = arr[right];
    			arr[right] = temp;
    		}
    		temp = arr[mid];
    		arr[mid] = arr[right - 1];
    		arr[right - 1] = temp;
    		// Sort(arr, left + 1, right - 2);
    		return arr[right - 1];
    	}



    這裏要注意的是,因爲我把pivot放到了right-1處,因此要i指針先動,若是放到left,就先動j指針。同理,在取a[0]爲pivot時,就先動j指針,取a[n-1]爲pivot時,就先動i指針。上面已經描述了,其實真正排序的是left+1到right-2的數據,代碼中i,j指針的初始值定義爲left,right-1是由於我後面的判斷是arr[++i],在此簡單說明一下。

    看下在OJ上跑的結果:

    第三個問題:小規模數據時,快排的速度。

    因爲傳統快排是使用遞歸的,遞歸速度很慢,在小規模數據時(例如N<50),可能還不如插入排序快。

    因此在小規模數據時,不要用傳統快排。(或者在寫快排時,設置一個Cutoff值,在規模小於Cutoff值時,調用插入排序,在規模大於Cutoff值時,再使用傳統快排)。

    加上cutoff之後:


    private static void quickSort(int[] arr, int n)
    	{
    		Sort(arr, 0, n - 1);
    	}
    
    	public static final int CUTOFF = 50;
    
    	public static void Sort(int[] arr, int left, int right)
    	{
    		if (right - left >= CUTOFF)
    		{
    			if (left >= right)
    			{
    				return;
    			}
    			int pivot = mid3(arr, left, right);
    			int i = left;
    			int j = right - 1;
    			for (;;)
    			{
    				while (i < right - 1 && arr[++i] < pivot)
    					;
    				while (j > left + 1 && arr[--j] > pivot)
    					;
    				if (i < j)
    				{
    					int temp = arr[i];
    					arr[i] = arr[j];
    					arr[j] = temp;
    				}
    				else
    				{
    					break;
    				}
    			}
    			arr[right - 1] = arr[i];
    			arr[i] = pivot;
    			Sort(arr, left, i - 1);
    			Sort(arr, i + 1, right);
    		}
    		else
    		{
    			InsertSort(arr, left, right);
    		}
    	}
    
    	public static int mid3(int[] arr, int left, int right)
    	{
    		int mid = (left + right) / 2;
    		int temp;
    		if (arr[left] > arr[mid])
    		{
    			temp = arr[left];
    			arr[left] = arr[mid];
    			arr[mid] = temp;
    		}
    		if (arr[left] > arr[right])
    		{
    			temp = arr[left];
    			arr[left] = arr[right];
    			arr[right] = temp;
    		}
    		if (arr[mid] > arr[right])
    		{
    			temp = arr[mid];
    			arr[mid] = arr[right];
    			arr[right] = temp;
    		}
    		temp = arr[mid];
    		arr[mid] = arr[right - 1];
    		arr[right - 1] = temp;
    		return arr[right - 1];
    	}
    
    	private static void InsertSort(int[] num, int start, int end)
    	{
    		int temp = 0;
    		int j = 0;
    		for (int i = start; i <= end; i++)
    		{
    			temp = num[i];
    			for (j = i; j > 0 && temp < num[j - 1]; j--)
    			{
    				num[j] = num[j - 1];
    			}
    			num[j] = temp;
    		}
    	}

    在OJ上的結果:

    感受稍微快了點吧。

    性能分析:

    快速排序爲何快呢?由於每次將pivot交換後的位置都是pivot這個數的最終位置,他不像插入排序那樣,位置始終在變化。

    快排最好的狀況是,每次正好中分,複雜度爲O(nlogn)。最差狀況是,若是pivot選的是第一個元素,那麼排好序的狀況耗時最多,複雜度爲O(n^2)

    快排的空間複雜度呢?快速排序在對序列的操做過程當中只需花費常數級的空間。空間複雜度O(1)。 但須要注意遞歸棧上須要花費最少O(logn) 最多O(n)的空間。

    參考資料:

    1. http://blog.csdn.net/morewindows/article

    2. http://www.cnblogs.com/luchen927/tag/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/

    3. http://mooc.study.163.com/learn/ZJU-1000033001?tid=1000044001#/learn/content

    相關文章
    相關標籤/搜索