§ 時間、空間複雜度比較
排序算法平均時間複雜度最差時間複雜度空間複雜度數據對象穩定性
1、冒泡排序
算法思想:
比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
針對所有的元素重複以上的步驟,除了最後一個。
持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
冒泡排序動圖演示:
代碼:
2、選擇排序
算法思想:
Ⅰ. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
Ⅱ. 從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾
Ⅲ. 以此類推,直到所有元素均排序完畢
選擇排序動圖演示:
代碼:
3、插入排序
算法思想:
從第一個元素開始,該元素可以認爲已經被排序
取出下一個元素,在已經排序的元素序列中從後向前掃描
如果該元素(已排序)大於新元素,將該元素移到下一位置
重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
將新元素插入到該位置後
重複步驟2~5
插入排序動圖演示:
代碼:
4、快速排序
算法思想:
選取第一個數爲基準
將比基準小的數交換到前面,比基準大的數交換到後面
對左右區間重複第二步,直到各區間只有一個數
快速排序動圖演示:
代碼:
5、堆排序
堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
算法思想:
將初始待排序關鍵字序列(R1,R2….Rn)構建成大頂堆,此堆爲初始的無序區;
將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n];
由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,……Rn-1)調整爲新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。
代碼:
#include <iostream>
#include <algorithm>
using namespace std;
// 堆排序:(最大堆,有序區)。從堆頂把根卸出來放在有序區之前,再恢復堆。
void max_heapify(int arr[], int start, int end) {
//建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son <= end) { //若子節點在範圍內才做比較
if (son + 1 <= end && arr[son] < arr[son + 1]) //先比較兩個子節點指標,選擇最大的
son++;
if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完成,直接跳出函數
return;
else { //否則交換父子內容再繼續子節點與孫節點比較
swap(arr[dad], arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(int arr[], int len) {
//初始化,i從最後一個父節點開始調整
for (int i = len / 2 - 1; i >= 0; i--)
max_heapify(arr, i, len - 1);
//先將第一個元素和已經排好的元素前一位做交換,再從新調整(剛調整的元素之前的元素),直到排序完成
for (int i = len - 1; i > 0; i--) {
swap(arr[0], arr[i]);
max_heapify(arr, 0, i - 1);
}
}
int main() {
int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
int len = (int) sizeof(arr) / sizeof(*arr);
heap_sort(arr, len);
for (int i = 0; i < len; i++)
cout << arr[i] << ' ';
cout << endl;
return 0;
}
6、歸併排序
歸併排序是建立在歸併操作上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。
算法思想:
1.把長度爲n的輸入序列分成兩個長度爲n/2的子序列;
2. 對這兩個子序列分別採用歸併排序;
3. 將兩個排序好的子序列合併成一個最終的排序序列。
歸併排序動圖演示:
代碼:
7、希爾排序
希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序算法。先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序.
算法思想:
選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列個數k,對序列進行k 趟排序;
每趟排序,根據對應的增量ti,將待排序列分割成若干長度爲m 的子序列,分別對各子表進行直接插入排序。僅增量因子爲1 時,整個序列作爲一個表來處理,表長度即爲整個序列的長度。
希爾排序動圖演示:
代碼:
8、計數排序
計數排序統計小於等於該元素值的元素的個數i,於是該元素就放在目標數組的索引i位(i≥0)。
計數排序基於一個假設,待排序數列的所有數均爲整數,且出現在(0,k)的區間之內。
如果 k(待排數組的最大值) 過大則會引起較大的空間複雜度,一般是用來排序 0 到 100 之間的數字的最好的算法,但是它不適合按字母順序排序人名。
計數排序不是比較排序,排序的速度快於任何比較排序算法。
算法思想:
找出待排序的數組中最大和最小的元素;
統計數組中每個值爲 i 的元素出現的次數,存入數組 C 的第 i 項;
對所有的計數累加(從 C 中的第一個元素開始,每一項和前一項相加);
向填充目標數組:將每個元素 i 放在新數組的第 C[i] 項,每放一個元素就將 C[i] 減去 1;
計數排序動圖演示:
代碼:
9、桶排序
將值爲i的元素放入i號桶,最後依次把桶裏的元素倒出來。
算法思想:
設置一個定量的數組當作空桶子。
尋訪序列,並且把項目一個一個放到對應的桶子去。
對每個不是空的桶子進行排序。
從不是空的桶子裏把項目再放回原來的序列中。
桶排序動圖演示:
代碼:
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; // 輸入數據的最小值
} else if (arr[i] > maxValue) {
maxValue = arr[i]; // 輸入數據的最大值
}
}
// 桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 設置桶的默認數量爲5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets = new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
// 利用映射函數將數據分配到各個桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 對每個桶進行排序,這裏使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
10、基數排序
一種多關鍵字的排序算法,可用桶排序實現。
算法思想:
取得數組中的最大數,並取得位數;
arr爲原始數組,從最低位開始取每個位組成radix數組;
對radix進行計數排序(利用計數排序適用於小範圍數的特點)
基數排序動圖演示:
代碼:
int maxbit(int data[], int n) //輔助函數,求數據的最大位數
{
int maxData = data[0];///< 最大數
/// 先求出最大數,再求其位數,這樣有原先依次每個數判斷其位數,稍微優化點。
for (int i = 1; i < n; ++i)
{
if (maxData < data[i])
maxData = data[i];
}
int d = 1;
int p = 10;
while (maxData >= p)
{
//p *= 10; // Maybe overflow
maxData /= 10;
++d;
}
return d;
/* int d = 1; //保存最大的位數
int p = 10;
for(int i = 0; i < n; ++i)
{
while(data[i] >= p)
{
p *= 10;
++d;
}
}
return d;*/
}
void radixsort(int data[], int n) //基數排序
{
int d = maxbit(data, n);
int *tmp = new int[n];
int *count = new int[10]; //計數器
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++) //進行d次排序
{
for(j = 0; j < 10; j++)
count[j] = 0; //每次分配前清空計數器
for(j = 0; j < n; j++)
{
k = (data[j] / radix) % 10; //統計每個桶中的記錄數
count[k]++;
}
for(j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j]; //將tmp中的位置依次分配給每個桶
for(j = n - 1; j >= 0; j--) //將所有桶中記錄依次收集到tmp中
{
k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j];
count[k]--;
}
for(j = 0; j < n; j++) //將臨時數組的內容複製到data中
data[j] = tmp[j];
radix = radix * 10;
}
delete []tmp;
delete []count;
}
關注小編,編程學習不迷路!
如果你想要獲取更多C語言、C++、Windows以及QT的知識!
小編有一個免費的C/C++編程學習交流俱樂部,【點擊進入】!
還有編程學習文件(源碼,項目實戰教學視頻以及給小白的零基礎教程),歡迎初學者和正在進階中的小夥伴們!