1. 程式人生 > >面試常見排序演算法

面試常見排序演算法

本文先給一個表格總結各個排序演算法的時間複雜度、空間複雜度、穩定性等。然後重點介紹快速排序和堆排序兩個面試過程中必問的排序,最後再介紹其他排序演算法。

一、常見排序演算法總結

穩定性:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj之前,而在排序後的序列中,ri仍在rj之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。

排序演算法總結

注意:堆排序和快速排序都是不穩定的,堆排序的平均情況和最好最壞情況時間複雜度都是O(nlogn)。
快速排序平均情況和最好情況時O(nlogn),最壞情況時O(n2),面試過程中常問。

二、快速排序演算法總結

2.1、快排的基本思想

  1. 先從數列中取出一個數作為基準數
  2. 分割槽過程,將小於或等於基準數的放到左邊,將大於基準數的放到右邊。
  3. 再對第二步得到的左右兩個區間重複1、2過程,直到各區間只有一個數。

例項介紹
有以下一組數:

72 6 57 88 60 42 83 73 48 85

1. 選取基準數:原則上基準數要隨機選取,但是我們這邊的資料可以認為是隨機的,所以我們每次的基準數都選取第一個數。這裡為72.
2. 分割槽。分為如下左右兩區:

6 57 60 42 48 72 88 83 73 85

3. 對左右兩區重複1、2步,截止到每個區只有一個數。
左區重複,選基準數、分割槽過程

6 57 60 42 48

右區

88 83 73 85

2.2、快排的時間複雜度和空間複雜度分析

時間複雜度

快速排序的時間複雜度依賴於劃分是否平衡。

  • 最壞情況分析:O(n^2)
    每次劃分的時候左右兩區間分別包含了n-1個元素和0個元素

  • 最好情況分析:O(nlogn)
    每次劃分的時候左右兩區間的元素個數幾乎相等。

2.3、快排的程式編寫

int
* quickSort(int* A, int n) { // write code here //入參校驗 if(A==NULL || n<=0){ cout<<"入參不合法"<<endl; return NULL; } //快排 coreQuickSort(A, 0, n-1); return A; } /* * 快排,裡面遞迴呼叫,遞迴截止條件為left>=right */ void coreQuickSort(int* A, int left, int right){ if(left<right){ int mid=parition(A, left, right); coreQuickSort(A, left, mid-1); coreQuickSort(A, mid+1, right); } } /** * 一次劃分 *@return 一次劃分完後的中間索引 */ int parition(int* A, int left, int right){ int key=A[right]; int lessNumIndex=left-1; for(int search=left; search<=right; search++){ if(A[search]<=key){ lessNumIndex++; swap(A, lessNumIndex, search); } } return lessNumIndex; } /** * 交換函式 */ void swap(int* A, int index1, int index2){ int tmp=A[index1]; A[index1]=A[index2]; A[index2]=tmp; }

三、堆排序演算法總結

3.1、堆排序的思想

堆:以最小堆為例,根節點不大於左右子節點。
堆排序:先將陣列初始化為最小堆,然後每次取出堆頂的元素,取完之後對堆進行維護,維護其為最小堆。這樣取出來的元素就會是從小到大排列的。

3.2、堆排序的時間複雜度和空間複雜度分析

時間複雜度:

時間複雜度是O(n*logn),對陣列初始化為堆的時候,時間複雜度為O(n),取完堆頂元素後,需要維護堆,每次維護堆的時間複雜度為O(logn),共需要N-1次維護堆的操作。
所以O(N)+(N-1)*O(logN)=O(N*logN)

3.3、堆排序程式編寫

int* heapSort(int* A, int n) {
        int lastPosi=n-1;
        maxHeapBuild(A, lastPosi);     //建最大堆
        while(lastPosi>=1){            //維護最大堆
            swap(A, 0, lastPosi);
            lastPosi=lastPosi-1;
            heapifyMax(A, 0, lastPosi);
        }
        return A;
    }

    //建立最大堆
    void maxHeapBuild(int* A, int lastPosi){
        for(int i=(lastPosi-1)/2; i>=0; i--){
            heapifyMax(A, i, lastPosi);
        }
    }

    //堆下沉,建立最大堆和維護堆的時候都需要的過程
    void heapifyMax(int* A, int posi, int lastPosi){
        int maxLR=2*posi+1;
        while(maxLR<=lastPosi){
            if( maxLR < lastPosi && A[maxLR+1]>A[maxLR]){
                maxLR++;
            }
            if(A[maxLR] <= A[posi]){
                break;
            }
            else{
                swap(A, posi,maxLR);
                posi=maxLR;
                maxLR=2*posi+1;
            }
        }
    }

    //交換
    void swap(int* A, int index1, int index2){
        int tmp=A[index1];
        A[index1]=A[index2];
        A[index2]=tmp;
    }