歸併排序:將兩個或兩個以上的有序表組合一個新的有序表稱爲“歸併”。先使每個子序列有序,再歸併使子序列段有序,最後得到完全有序的序列。
算法思想:我們通常用遞歸實現,先把待排序區間[startindex,endindex]以中點二分,接着把左邊子區間排序,再把右邊子區間排序,最後把左區間和右區間用一次歸併操作合併成有序的區間[startindex,endindex]。
歸併過程:比較a[i]和b[j]的大小,若a[i]≤b[j],則將第一個有序序列中的元素a[i]複製到r[k]中,並令i和k分別加上1;否則將第二個有序序列中的元素a[j]複製到r[k]中,並令j和k分別加上1,如此循環下去,直到其中一個有序序列取完,然後再將另一個有序序列中剩餘的元素複製到r中從下標k到下標endindex的單元。
如圖所示:
上半部分爲2分法使數據爲有序序列,下半部分爲歸併有序序列操作,最後爲完全有序。
示例代碼如下:(測試代碼自己寫)
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
|
void Merge(int *arr, int *tmp, int startindex, int midindex, int endindex)//歸併操作
{
int k = startindex;
int i = startindex;
int j = midindex + 1;
while(i != midindex + 1 && j != endindex + 1)
{
if(arr[i] > arr[j])
{
tmp[k++] = arr[j++];
}
else {
tmp[k++] = arr[i++];
}
}
while(i != midindex + 1)
{
tmp[k++] = arr[i++];
}
while(j != endindex + 1)
{
tmp[k++] = arr[j++];
}
for(int i = startindex; i <= endindex; i++)
{
arr[i] = tmp[i];
}
}
void Guib(int *arr, int *tmp, int startindex, int endindex)//歸併排序
{
if(startindex < endindex)
{
int midindex = (startindex + endindex) / 2;
Guib(arr, tmp, startindex, midindex);
Guib(arr, tmp, midindex + 1, endindex);
Merge(arr, tmp, startindex, midindex, endindex);
}
}
|
注:測試只需在main函數中建立兩個數組,調用Guib函數即可。
時間複雜度:一趟歸併排序調用n/2h次算法merge將arr【1..n】中前後相鄰且長度爲h的有序段兩兩合併得到2h的有序段,並存放到tmp【1..n】中。整個歸併排序需要log2^n趟。所以需要移動操作2*2h*(n/2h)*log2^n=2log2^n;比較操作是(n/2h)*h*log2^n=n/2log2^n;此算法時間複雜度爲nlgn.
空間複雜度:O(1)。
穩定性:每次歸併若元素相同,不改變前後順序,所以爲穩定的。
續:非遞歸實現代碼
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
|
#include #include void MergeSort(int *arr, int *tmp, int length)
{
int leftmin = 0;
int leftmax = 0;
int rightmin = 0;
int rightmax = 0;
int k = 0;
for(int i = 1; i < length; i *= 2)
{
for(int leftmin = 0; leftmin < length - i; leftmin = rightmax + 1)
{
leftmax = leftmin + i;
rightmin = leftmax + 1;
rightmax = rightmin + i - 1;
if(rightmax > length)
{
rightmax = length;
}
while(leftmin < leftmax + 1 && rightmin < rightmax + 1)
{
tmp[k++] = arr[leftmin] > arr[rightmin] ? arr[rightmin++] : arr[leftmin++];
}
while(leftmin < leftmax + 1)
{
tmp[k++] = arr[leftmin];
}
while(rightmin < rightmax + 1)
{
tmp[k++] = arr[rightmin];
}
for(int j = leftmin; j <= rightmax; j++)
{
arr[j] = tmp[j];
}
}
}
} #define N 10 int main()
{
int a[N] = {3, 1, 4, 5, 7, 6, 0, 8, 7, 9};
int t[N];
MergeSort(a, t, N);
for(int i = 0; i < N; i++)
{
printf("%d", a[i]);
}
}
|
基數排序:又稱“桶排序”,通過鍵值的部分的資訊,將要排序的元素分配到某些“桶”中,以此達到排序的目的。
算法思想:從最次位的關鍵字K^(d-1)起進行排序。然後再對高一位的關鍵字K^(d-2)進行排序,依次重複,直至對K^0進行排序後成爲一個有序序列。叫“最低位優先”,簡稱LSD法。
排序過程如圖所示:
代碼如下:
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
|
//數據最大值的位數 即排序躺數 (543)3 int FindMaxFigure(int *arr, int len)
{
int max = arr[0];
for(int i = 1; i < len; i++)
{
if(max < arr[i])
{
max = arr[i];
}
}
int count = 0;
do {
max /= 10;
count++;
}
while(max != 0);
return count;
} //數據的位上的數字 (45 0)1、(54 1)5 int FindNums(int num, int figure)
{
return num / pow(10.0, figure) % 10;
} //根據數據每一位進行入桶和出桶操作 void Radix(int *arr, int len, int figures)
{
int tmp[10][N] = {};
int count[10] = {};
for(int i = 0; i < len; i++)
{
int num_figures = FindNums(arr[i], figures);
tmp[num_figures][count[num_firures]] = arr[i];
count[num_figures]++;
}
int k = 0;
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < count[i]; j++)
{
arr[k++] = tmp[i][j];
}
}
} //基數排序(桶排序) void RadixSort(int *arr, int len)
{
int num = FindMaxFigure(arr, len);
for(int i = 0; i < num; i++)
{
Radix(arr, len, i);
}
}
|
注:測試只需在main函數中調用void RadixSort(int *arr, int len)就行。
時間複雜度: 可知排序躺數爲count次,入桶和出桶操作爲n,賦值給原始數組爲r*n,r爲基數。所以時間複雜度爲 O(n(count+r))。
空間複雜度:輔助存儲數組tmp、count,所以爲O(r*n+r),r爲基數。
穩定性:穩定。