【算法】歸並排序與快排


歸並排序

歸並排序是另一種不同的排序方法,因為歸並排序使用了遞歸分治的思想,所以理解起來比較容易。其基本思想是,先遞歸划分子問題,然后合並結果。把待排序列看成由兩個有序的子序列,然后合並兩個子序列,然后把子序列看成由兩個有序序列。。。。。倒着來看,其實就是先兩兩合並,然后四四合並。。。最終形成有序序列。空間復雜度為O(n),時間復雜度為O(nlogn)。
舉個栗子:

 

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
#include "Solution.h"

using namespace std;

void MergeArray(int array[], int start, int mid, int end, int temp[]) {
    int i = start;
    int j =  mid + 1;
    int k = 0;
    while (i <= mid && j <= end ) {
        if (array[i] < array[j]) {
            temp[k++] = array[i++];
        }else {
            temp[k++] = array[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = array[i++];
    }
    while (j <= end) {
        temp[k++] = array[j++];
    }
    for (int i = 0; i < k; i ++) {
        array[start + i] = temp[i];
    }

}
// 歸並排序,將數組前半部分后半部分分成最小單元,然后在合並
void MergeSort(int array[], int start,  int end, int temp[]) {
    if(start < end) {
        int mid = (start + end)/ 2;
        MergeSort(array, start, mid, temp);
        MergeSort(array, mid + 1, end, temp);
        MergeArray(array, start, mid, end, temp);
    }

}
// 在這里創建臨時數組,節省內存開銷,因為以后的temp都是在遞歸李使用的。
void MergeSortmain(int array[], int len) {
    int start = 0;
    int end = len - 1;
    int *temp = new int[len];
    MergeSort(array, start, end, temp);
}

void PrintArray(int array[], int len) {
    for (int i = 0 ; i < len; ++i) {
        cout << array[i] << " " ;

    }
    cout << endl;
}

int main() {

    int array[] = {3,5,3,6,7,3,7,8,1,2};

    MergeSortmain(array, 10);
    PrintArray(array, 10);
    
    return 0;
}

快速排序

快速排序一聽名字就覺得很高端,在實際應用當中快速排序確實也是表現最好的排序算法。冒泡排序雖然高端,但其實其思想是來自冒泡排序,冒泡排序是通過相鄰元素的比較和交換把最小的冒泡到最頂端,而快速排序是比較和交換小數和大數,這樣一來不僅把小數冒泡到上面同時也把大數沉到下面。
舉個栗子:對5,3,8,6,4這個無序序列進行快速排序,思路是右指針找比基准數小的,左指針找比基准數大的,交換之。
5,3,8,6,4 用5作為比較的基准,最終會把5小的移動到5的左邊,比5大的移動到5的右邊。
5,3,8,6,4 首先設置i,j兩個指針分別指向兩端,j指針先掃描(思考一下為什么?)4比5小停止。然后i掃描,8比5大停止。交換i,j位置。
5,3,4,6,8 然后j指針再掃描,這時j掃描4時兩指針相遇。停止。然后交換4和基准數。
4,3,5,6,8 一次划分后達到了左邊比5小,右邊比5大的目的。之后對左右子序列遞歸排序,最終得到有序序列。
上面留下來了一個問題為什么一定要j指針先動呢?首先這也不是絕對的,這取決於基准數的位置,因為在最后兩個指針相遇的時候,要交換基准數到相遇的位置。一般選取第一個數作為基准數,那么就是在左邊,所以最后相遇的數要和基准數交換,那么相遇的數一定要比基准數小。所以j指針先移動才能先找到比基准數小的數。
快速排序是不穩定的,其時間平均時間復雜度是O(nlgn)。
實現代碼:
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
#include "Solution.h"

using namespace std;
int partition(vector<int> &vi, int low, int up)
{
    int pivot = vi[up];//選擇最后一個元素作為比較元素
    int i = low-1;//這個慢速移動下標必須設定為比最小下表p小1,否則兩個元素的序列比如2,1無法交換
    for (int j = low; j < up; j++)
    {
        if(vi[j] <= pivot)
        {
            i++;
            swap(vi[i], vi[j]);
        }
    }
    swap(vi[i+1], vi[up]);
    return i+1;
}

//C++'s array range should be [low, up], the same as [low, up+1)
void quickSort(vector<int> &vi, int low, int up)
{
    if(low < up)
    {
        int mid = partition(vi, low, up);
        //Watch out! The mid position is on the place, so we don't need to consider it again.
        //That's why below is mid-1, not mid! Otherwise it will occur overflow error!!!
        quickSort(vi, low, mid-1);
        quickSort(vi, mid+1, up);
    }
}

void qSort(vector<int> &vi)
{
    quickSort(vi, 0, vi.size()-1);
}

int main() {

    int a[] = {3,5,7,9,2,3,1,0,7,5,4};
    vector<int> va(a, a+11);

    cout<<"Before quicksort:\n";
    for(auto x:va)
        cout<<x<<" ";
    cout<<endl;

    qSort(va);

    cout<<"After quicksort:\n";
    for(auto x:va)
        cout<<x<<" ";
    cout<<endl;

    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM