八大排序之归并、基数排序

归并排序:将两个或两个以上的有序表组合一个新的有序表称为“归并”。先使每个子序列有序,再归并使子序列段有序,最后得到完全有序的序列。

算法思想:我们通常用递归实现,先把待排序区间[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为基数。

稳定性:稳定。


 

猜你喜欢

转载自blog.csdn.net/qq_19525389/article/details/79378786