最近想複習下c++,很久沒怎麼用了,畢業時的一些經典排序演算法也忘差不多了,所以剛好一起再學習一遍。
除了冒泡、插入、選擇這幾個複雜度o(n^2)的基本排序演算法,希爾、歸併、快速、堆排序,多多少少還有些晦澀難懂,幸好又大神dreamcatcher-cx都總結成了**,一步步很詳細,十分感謝。
而且就時間複雜度來說,這幾種演算法到底有什麼區別呢,剛好做了下測試。
**參考:
//: basic_sort
#include
#include
#include
#include
using
namespace
std;
//獲取函式名字的巨集
#define get_name(x) #x
//生成隨機數的巨集
#define random(a,b) (rand()%(b-a+1)+a)
//列印容器物件(vector)的巨集
#define prt(nums) \}/*
氣泡排序
基本思想: 對相鄰的元素進行兩兩比較,順序相反則進行交換,這樣,每一趟會將最小或最大的元素「浮」到頂端,最終達到完全有序
**:
考的最多的排序了吧。
1. 兩層迴圈,最裡面判斷兩個數的大小,大的換到後面(正序)
2. 內部迴圈一遍後,最大的數已經到最後面了
3. 下一次內部迴圈從0到倒數第二個數(最後乙個數通過第一步迴圈比較已經最大了)
4. 依次迴圈下去
時間複雜度o(n^2),空間複雜度是o(n) */
void bubble_sort(vector &nums)
}}}/*
插入排序
基本思想: 每一步將乙個待排序的記錄,插入到前面已經排好序的有序序列中去,直到插完所有元素為止。
**:
1. 兩層迴圈,第一層i表示從左開始已經排好虛的部分
2. 第二層迴圈,將當前的數以及它前面的所有數兩兩比較,交換大的數到後面(正序)
3. 保證前面的數是排序好的,將新讀取的數通過遍歷前面排好序的部分並比較,插入到合適的位置
時間複雜度o(n^2),空間複雜度是o(n) */
void insert_sort(vector &nums)
}}}/*
選擇排序
**:
基本思想: 每一趟從待排序的資料元素中選擇最小(或最大)的乙個元素作為首元素
1. 兩層迴圈,第一層從左到右遍歷,讀取當前的數
2. min存放最小元素,初始化為當前數字
3. 內部迴圈遍歷和比較當前數字後後面所有數字的大小,如果有更小的,替換min為更小數字的位置
4. 內部遍歷之後檢查min是否變化,如果變化,說明最小的數字不在之前初始化的min位置,交換使每次迴圈最小的元素被移動到最左邊。
時間複雜度o(n^2),空間複雜度是o(n) */
void selection_sort(vector &nums)
}if (min != i)
}}/*
希爾排序
**:
1. 最外層迴圈設定間隔(gap),按常規取gap=length/2,並以gap = gap/2的方式縮小增量
2. 第二個迴圈從gap位置向後遍歷,讀取當前元素
3. 第三個迴圈從當前元素所在分組的上乙個元素開始(即減去gap的位置),通過遞減gap向前遍歷分組內的元素,其實就是比較分組內i和i-gap元素的大小,交換大的到後面
希爾排序的時間複雜度受步長的影響,不穩定。 */
void shell_sort(vector &nums)
}}}//
合併兩個有序序列
void merge_array(vector &nums, int b, int m, int e, vector &temp)
//遞迴對序列拆分,從b(開始)到e(結束)的序列,取中間點(b + e) / 2拆分
void merge_sort_recur(vector &nums, int b, int e, vector &temp)}/*
歸併排序
**:
基本思想: 利用歸併的思想實現的排序方法,該演算法採用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然後遞迴求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。
1. 合併兩個有序序列的函式,合併後結果存入臨時的temp
2. 從中間分,一直遞迴分到最小序列,即每個序列只有乙個元素,單位為1(乙個元素肯定是有序的)
3. 然後兩兩比較合併成單位為2的n/2個子陣列,在結果上繼續兩兩合併
時間複雜度是o(nlogn),空間複雜度是o(n)。 */
void merge_sort(vector &nums)
//將啟始位置b作為基準,大於基準的數移動到右邊,小於基準的數移動到左邊
void quick_sort_recur(vector &nums, int b, int
e) swap(nums[b], nums[lb]);
//cout << "nums: ";
//prt(nums);
//cout << endl;
quick_sort_recur(nums, b, lb);
quick_sort_recur(nums, lb + 1
, e);
}}/*
快速排序
**:
基本思想: 快速排序也是利用分治法實現的乙個排序演算法。快速排序和歸併排序不同,它不是一半一半的分子陣列,而是選擇乙個基準數,把比這個數小的挪到左邊,把比這個數大的移到右邊。然後不斷對左右兩部分也執行相同步驟,直到整個陣列有序。
1. 用乙個基準數將陣列分成兩個子陣列,取第乙個數為基準
2. 將大於基準數的移到右邊,小於的移到左邊
3. 遞迴的對子陣列重複執行1,2,直到整個陣列有序
空間複雜度是o(n),時間複雜度不穩定。 */
void quick_sort(vector &nums)
//調整單個二叉樹的根節點和左右子樹的位置,構建大頂堆
//在左右子樹中挑出最大的和根節點比較,把最大的數放在根節點即可
void max_heapify(vector &nums, int root, int
end)
if (nums[curr] else
}}/*
堆排序 **:
基本思想: 將待排序序列構造成乙個大頂堆,此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就為最大值。然後將剩餘n-1個元素重新構造成乙個堆,這樣會得到n個元素的次小值。如此反覆執行,便能得到乙個有序序列了
堆的概念(i是乙個二叉樹的根節點位置,2i+1和2i+2分別是左右子樹):
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
1. 由底(最後乙個有葉子的根節點n/2-1)自上構建大頂堆
2. 根節點(0)和末尾交換,末尾變為最大
3. 對餘下的0到n-1個數的根節點(0)二叉樹進行大頂堆調整(呼叫max_heapify)(根節點(0)的葉子節點已經大於下面的所有數字了)
堆執行一次調整需要o(logn)的時間,在排序過程中需要遍歷所有元素執行堆調整,所以最終時間複雜度是o(nlogn)。空間複雜度是o(n)。 */
void heap_sort(vector &nums)
for (int i = n - 1; i > 0; i--)
}void func_excute(void(* func)(vector &), vector nums, string
func_name)
intmain()
///:~
在陣列很小的情況下,沒有太大區別。但是較長陣列,考的最多的氣泡排序就明顯比較吃力了~
具體原因只能從時間複雜度上面來看,但為什麼差這麼多,我也不是完全明白~
執行結果,排序演算法分別耗時:
陣列長度: 5000;bubble_sort耗時:183.4 (ms)
insert_sort耗時:106.525 (ms)
selection_sort耗時:68.036 (ms)
shell_sort耗時:1.096 (ms)
merge_sort耗時:1.226 (ms)
quick_sort耗時:1.398 (ms)
heap_sort耗時:1.514 (ms)
program ended with exit code: 0
C 實現七種經典排序演算法
具體的排序方法如下所示 1 氣泡排序 基本思想 比較相鄰的兩個數,如果前者比後者大,則進行交換。每一輪排序結束,選出乙個未排序中最大的數放到陣列後面。常見氣泡排序演算法如下所示 void bubblesort int arr,int n 公升級版氣泡排序法 通過從低到高選出最大的數放到後面,再從高到...
七種排序演算法總結
根據排序過程中借助的主要操作,將7種內排序演算法按照下圖所示進行分類。將7種演算法的各種指標進行對比,如下表所示。從平均情況來看,顯然最後3種改進演算法要勝過希爾排序,並遠遠勝過前3種簡單演算法。從最好情況看,反而冒泡和直接插入排序要更勝一籌,也就是說,如果待排序列總是基本有序,反而不應該考慮四種複...
七種常用排序演算法總結
1.氣泡排序 bubble sort package com.zha.wmls.sort 氣泡排序 每一輪找出乙個最大的,放在最後面,穩定 時間複雜度為n 2 public class bubblesort long befortime system.currenttimemillis sort a...