常見排序算法(二)

歸併排序:算法

  一、是創建在歸併操做上的一種排序算法,該算法爲分治法的一個應用數組

  二、步驟:數據結構

    假設待排序序列爲 R[0...n-1],並採用二路歸併ide

    a、將 R 劃分成長度相同或相差爲 1 的兩個子序列 R1,R2函數

    b、遞歸地將子序列 R1 和 R2 劃分,直至子序列長度爲 1spa

    c、兩兩合併子序列,同時進行排序,直至序列長度爲 n,如合併 [1, 4] 和 [2, 3] ==> [1, 2, 3, 4]指針

注:n 路歸併即將序列劃分紅 n 個子序列,通常 n = 2code

  分治法是把問題實例劃分紅若干個子實例,並分別遞歸地解決每一個子實例,而後把這些子實例的解組合起來,獲得原問題實例的解blog

void merge(std::vector<int>& nums, int left, int mid, int right)
{
    
    int len = right - left + 1;
    int* temp = new int[len];       // 輔助空間O(n)
    int index = 0;
    int i = left;                   // 前一數組的起始下標 
    int j = mid + 1;                // 後一數組的起始下標 
    
    while (i <= mid && j <= right) {
//        選取較小的那一個 
        temp[index++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
    }
//    將未放回輔助空間的子序列所有放回去(因爲分治,子序列裏面已是排序好了的) 
    while (i <= mid) {
        temp[index++] = nums[i++];
    }
    while (j <= right) {
        temp[index++] = nums[j++];
    }
//    深拷貝 
    for (int k = 0; k < len; k++) {
        nums[left++] = temp[k];
    }
//    使用 new 關鍵字建立的空間在堆,須要主動刪除,而平時的變量聲明通常在棧裏,由計算機自動刪除並釋放空間 
    delete temp;
}

void mergeSort(std::vector<int>& nums, int left, int right)
{
    if (left >= right)
        return ;
    int mid = (left + right) / 2;
    mergeSort(nums, left, mid);
    mergeSort(nums, mid+1, right);
    merge(nums, left, mid, right);
}
View Code

 注:深拷貝可認爲拷貝後指針所指地址不一樣,但內容相同;而淺拷貝不止值相同,連指針所指地址也相同,會受到原來內容改變的影響排序

 

堆排序:

  一、堆的性質:

    a、在數據結構裏,堆是一棵近似的徹底二叉樹,除最底層,其餘都是全滿的(注意和滿二叉樹的區別)

    b、樹的每一個節點對應數組中的一個元素,其根節點對應下標爲 0 的元素

    c、下標爲 i 的元素,其父節點下標爲 (i+1) / 2 - 1,左孩子下標爲 2*i + 1,右孩子下標爲 2*i + 2

    d、最大堆中,父節點的值必須大於它的子節點的值;而最小堆則必須小於

  二、步驟:

    a、創建最大堆,方法詳細看代碼

    b、將最大堆的第一個元素(最大值)取出,而後拿最後的元素放到下標爲 0 的位置,從新調整最大堆(以下圖)

    c、重複操做 b 直至到第一個元素,此時該堆已變成最小堆

//向下調整 
void siftDown(std::vector<int>& nums, int star, int last_index)
{
    int i = star;
    int j = i * 2 + 1;
//    記錄要調整的節點 
    int temp = nums[i];
    while (j <= last_index) {
//        記錄較大的那個子節點 
        if (j < last_index && nums[j] < nums[j+1])
            j++;
//        若父節點大於等於最大的那個子節點,則退出循環,不然交換這兩個節點 
        if (temp >= nums[j])
            break;
        else {
            nums[i] = nums[j];
//            向下尋找子節點 
            i = j;
            j = 2 * j +1;
        }
    }
//    循環結束後 i 的位置即爲要調整節點的新位置 
    nums[i] = temp;
}

void heapSort(std::vector<int>& nums)
{
    int len = nums.size();
//    構建最大堆,注意是減 2,由於最後一個節點必定是子節點 
    for (int i = (len - 2) / 2; i >= 0; --i) {
        siftDown(nums, i, len-1);
    }
    //經過交換使最大堆變成最小堆 
    for (int i = len-1; i >= 0; --i){
        //交換首元素和下標爲i的元素 
        int t = nums[0];
        nums[0] = nums[i];
        nums[i] = t;
        //把最大元素排除在外重構最大堆 
        siftDown(nums, 0, i-1);
    }
}
View Code

 

 注:若使用最小堆時,咱們只能保證父節點大於子節點,而沒法保證兄弟節點的大小關係,所以咱們先構建最大堆,再進行堆排序

  上面兩種排序算法的 main 函數和上一篇冒泡排序的 main 函數同樣

相關文章
相關標籤/搜索