八大排序之歸併、基數排序

歸併排序:將兩個或兩個以上的有序表組合一個新的有序表稱爲“歸併”。先使每個子序列有序,再歸併使子序列段有序,最後得到完全有序的序列。

算法思想:我們通常用遞歸實現,先把待排序區間[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] = {3145760879};
    
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爲基數。

穩定性:穩定。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章