歸併排序:算法
一、是創建在歸併操做上的一種排序算法,該算法爲分治法的一個應用數組
二、步驟:數據結構
假設待排序序列爲 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); }
注:深拷貝可認爲拷貝後指針所指地址不一樣,但內容相同;而淺拷貝不止值相同,連指針所指地址也相同,會受到原來內容改變的影響排序
堆排序:
一、堆的性質:
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); } }
注:若使用最小堆時,咱們只能保證父節點大於子節點,而沒法保證兄弟節點的大小關係,所以咱們先構建最大堆,再進行堆排序
上面兩種排序算法的 main 函數和上一篇冒泡排序的 main 函數同樣