以前也零零碎碎發過一些排序算法,但排版都不太好,又重新整理一次,排序算法是數據結構的重要部分,系統地學習很有必要。
算法思想: 算法思想: 算法思想: 算法思想: 堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。 算法思想: 歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為2-路歸并。 算法思想:1.把長度為n的輸入序列分成兩個長度為n/2的子序列;2. 對這兩個子序列分別采用歸并排序;3. 將兩個排序好的子序列合并成一個最終的排序序列。 希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序算法。先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序. 算法思想: 計數排序統計小于等于該元素值的元素的個數i,于是該元素就放在目標數組的索引i位(i≥0)。 算法思想: 將值為i的元素放入i號桶,最后依次把桶里的元素倒出來。 算法思想: 一種多關鍵字的排序算法,可用桶排序實現。 算法思想:
時間、空間復雜度比較
排序算法
平均時間復雜度
最差時間復雜度
空間復雜度
數據對象穩定性
冒泡排序
O(n2)
O(n2)
O(1)
穩定
選擇排序
O(n2)
O(n2)
O(1)
數組不穩定、鏈表穩定
插入排序
O(n2)
O(n2)
O(1)
穩定
快速排序
O(n*log2n)
O(n2)
O(log2n)
不穩定
堆排序
O(n*log2n)
O(n*log2n)
O(1)
不穩定
歸并排序
O(n*log2n)
O(n*log2n)
O(n)
穩定
希爾排序
O(n*log2n)
O(n2)
O(1)
不穩定
計數排序
O(n+m)
O(n+m)
O(n+m)
穩定
桶排序
O(n)
O(n)
O(m)
穩定
基數排序
O(k*n)
O(n2)
穩定
1 冒泡排序
冒泡排序動圖演示
代碼:
void bubbleSort(int a[], int n)
{
?for(int i =0 ; i ?{
? ?for(int j = 0; j ? ?{
? ? ?if(a[j] > a[j+1])
? ? ?{
? ? ? ?int tmp = a[j] ; ?//交換
? ? ? ?a[j] = a[j+1] ;
? ? ? ?a[j+1] = tmp;
? ? ?}
? ?}
?}
}
2 選擇排序
選擇排序動圖演示
代碼:
function selectionSort(arr) {
? ?var len = arr.length;
? ?var minIndex, temp;
? ?for (var i = 0; i ? ? ? ?minIndex = i;
? ? ? ?for (var j = i + 1; j ? ? ? ? ? ?if (arr[j] ? ? ? ? ? ? ? ?minIndex = j; ? ? ? ? ? ? ? ? // 將最小數的索引保存
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?temp = arr[i];
? ? ? ?arr[i] = arr[minIndex];
? ? ? ?arr[minIndex] = temp;
? ?}
? ?return arr;
}
3 插入排序
插入排序動圖演示
代碼:
void print(int a[], int n ,int i){
?cout ?for(int j= 0; j ? ?cout ?}
?cout}
void InsertSort(int a[], int n)
{
?for(int i= 1; i
? ? ?int x = a[i]; ? ? //復制為哨兵,即存儲待排序元素
? ? ?a[i] = a[i-1]; ? ? ? ? ? //先后移一個元素
? ? ?while(x ? ? ? ?a[j+1] = a[j];
? ? ? ?j--; ? ? //元素后移
? ? ?}
? ? ?a[j+1] = x; ? ? //插入到正確位置
? ?}
? ?print(a,n,i); ? ? ?//打印每趟排序的結果
?}
}
int main(){
?int a[15] = {2,3,4,5,15,19,16,27,36,38,44,46,47,48,50};
?InsertSort(a,15);
?print(a,15,15);
}
4 快速排序
快速排序動圖演示
代碼:
void QuickSort(vector
if (low >= high) // 結束標志
return;
int first = low; // 低位下標
int last = high; // 高位下標
int key = v[first]; // 設第一個為基準
while (first {
// 將比第一個小的移到前面
while (first = key)
last--;
if (first v[first++] = v[last];
// 將比第一個大的移到后面
while (first first++;
if (first v[last--] = v[first];
}
//
v[first] = key;
// 前半遞歸
QuickSort(v, low, first - 1);
// 后半遞歸
QuickSort(v, first + 1, high);
}
5 堆排序
代碼:
#include
#include
using namespace std;
// 堆排序:(最大堆,有序區)。從堆頂把根卸出來放在有序區之前,再恢復堆。
void max_heapify(int arr[], int start, int end) {
//建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son if (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 cout cout return 0;
}
6 歸并排序
歸并排序動圖演示
代碼:
void print(int a[], int n){
?for(int j= 0; j
?cout}
//將r[i…m]和r[m +1 …n]歸并到輔助數組rf[i…n]
void Merge(ElemType *r,ElemType *rf, int i, int m, int n)
{
?int j,k;
?for(j=m+1,k=i; i ? ?if(r[j] ? ?else rf[k] = r[i++];
?}
?while(i ?while(j ?print(rf,n+1);
}
void MergeSort(ElemType *r, ElemType *rf, int lenght)
{
?int len = 1;
?ElemType *q = r ;
?ElemType *tmp ;
?while(len ? ?int s = len;
? ?len = 2 * s ;
? ?int i = 0;
? ?while(i+ len
? ? ?i = i+ len;
? ?}
? ?if(i + s ? ? ?Merge(q, rf, ?i, i+ s -1, lenght -1); //對不等長的兩個子表合并
? ?}
? ?tmp = q; q = rf; rf = tmp; //交換q,rf,以保證下一趟歸并時,仍從q 歸并到rf
?}
}
int main(){
?int a[10] = {2,3,4,5,15,19,26,27,36,38,44,46,47,48,50};
?int b[10];
?MergeSort(a, b, 15);
?print(b,15);
?cout ?print(a,10);
}
7 希爾排序
希爾排序動圖演示
代碼:
void shell_sort(T array[], int length) {
? ?int h = 1;
? ?while (h ? ? ? ?h = 3 * h + 1;
? ?}
? ?while (h >= 1) {
? ? ? ?for (int i = h; i ? ? ? ? ? ?for (int j = i; j >= h && array[j] ? ? ? ? ? ? ? ?std::swap(array[j], array[j - h]);
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?h = h / 3;
? ?}
}
8 計數排序
計數排序動圖演示
代碼:
#include
#include
#include
using namespace std;
// 計數排序
void CountSort(vector
{
// 確保待排序容器非空
if (vecRaw.size() == 0)
return;
// 使用 vecRaw 的最大值 + 1 作為計數容器 countVec 的大小
int vecCountLength = (*max_element(begin(vecRaw), end(vecRaw))) + 1;
vector
// 統計每個鍵值出現的次數
for (int i = 0; i vecCount[vecRaw[i]]++;
// 后面的鍵值出現的位置為前面所有鍵值出現的次數之和
for (int i = 1; i vecCount[i] += vecCount[i - 1];
// 將鍵值放到目標位置
for (int i = vecRaw.size(); i > 0; i--) // 此處逆序是為了保持相同鍵值的穩定性
vecObj[--vecCount[vecRaw[i - 1]]] = vecRaw[i - 1];
}
int main()
{
vector
vector
CountSort(vecRaw, vecObj);
for (int i = 0; i cout cout
return 0;
}
9 桶排序
桶排序動圖演示
代碼:
function bucketSort(arr, bucketSize) {
? ?if (arr.length === 0) {
? ? ?return arr;
? ?}
? ?var i;
? ?var minValue = arr[0];
? ?var maxValue = arr[0];
? ?for (i = 1; i ? ? ?if (arr[i] ? ? ? ? ?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[i] = [];
? ?}
? ?// 利用映射函數將數據分配到各個桶中
? ?for (i = 0; i ? ? ? ?buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
? ?}
? ?arr.length = 0;
? ?for (i = 0; i ? ? ? ?insertionSort(buckets[i]); ? ? ? ? ? ? ? ? ? ? ?// 對每個桶進行排序,這里使用了插入排序
? ? ? ?for (var j = 0; j ? ? ? ? ? ?arr.push(buckets[i][j]);
? ? ? ?}
? ?}
? ?return arr;
}
10 基數排序
基數排序動圖演示
代碼:
int maxbit(int data[], int n) //輔助函數,求數據的最大位數
{
? ?int maxData = data[0]; /// ? ?/// 先求出最大數,再求其位數,這樣有原先依次每個數判斷其位數,稍微優化點。
? ?for (int i = 1; i ? ?{
? ? ? ?if (maxData ? ? ? ? ? ?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 ? ?{
? ? ? ?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 ? ?{
? ? ? ?for(j = 0; j ? ? ? ? ? ?count[j] = 0; //每次分配前清空計數器
? ? ? ?for(j = 0; j ? ? ? ?{
? ? ? ? ? ?k = (data[j] / radix) % 10; //統計每個桶中的記錄數
? ? ? ? ? ?count[k]++;
? ? ? ?}
? ? ? ?for(j = 1; 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 ? ? ? ? ? ?data[j] = tmp[j];
? ? ? ?radix = radix * 10;
? ?}
? ?delete []tmp;
? ?delete []count;
}
? 2020 GitHub, Inc.
參考資料