归并排序和基数排序

150 阅读2分钟

归并排序

  • 基本思想 归并排序采用分而治之的思想,将数组递归的划分成小的子表,再逐趟的将个子表排序后归并成一个新的有序表。
  • 时间复杂度 每趟归并的时间复杂度是O(n),共需进行log(n)趟,所以时间复杂度是nlog(n)
  • 空间复杂度 O(n)
  • 稳定性 归并排序是稳定的排序算法

代码实现

void merge(int *a, int low, int mid, int high){
	int i = 0, j = 0, k = low;
    int llen = mid - low + 1;
    int rlen = high - (mid + 1) + 1;
    int len = high - low + 1;
	int b[len]; //辅助空间
   	
    //将元素赋值到辅助数组
    for(m = 0; m < len; m ++){
    	b[m] = a[low + m];
    }
    
    while(i < llen && j < rlen){
    	if(b[i] <= b[j + mid + 1 - low]){
        	a[k] = b[i++];
        else{
        	a[k] = b[(j++) + mid + 1 - low];
        }
        k ++;
	}
    
    while(i < llen) a[k++] = b[i ++];
    while(j < rlen) a[k++] = b[(j ++) + mid + 1 - low];
}

// 哨兵版
void merge(int *a, int low, int mid, int high){
	int i = 0, j = 0, k = low;
    int llen = mid - low + 1;
    int rlen = high - (mid + 1) + 1;
    
    //辅助空间,对分配的一个元素存放哨兵
	int larr[llen + 1]; 
    int rarr[rlen + 1];
    
    //将元素复制到辅助空间
   	for(i = 0; i < llen; i ++) larr[i] = a[i + low];
    for(j = 0; j < rlen; j ++) rarr[j] = a[j + mid + 1 - low];
    
    //哨兵就位
    larr[llen] = rarr[rlen] = INT_MAX;
    
	i = 0, j = 0;
    while(larr[i] != INT_MAX){
    	if(larr[i] <= rarr[j]){
        	a[k] = larr[i ++];
        }else{
        	a[k] = rarr[j++];
        }
        k ++;
    }    
}
// 合并操作
void mergeSort(int *a, int low, int high){
	if(low >= high) return;
    mid = low + ((high - low) >> 1);
    mergeSort(a, low, mid);
    mergeSort(a, mid + 1, high);
    merge(a, low, mid, high);
}

基数排序

使用LSD的情况下,基数排序通常是是稳定的非比较排序

  • 时间复杂度 d(n + rd)
  • 空间复杂度 r r表示基数,比如,当基数为0-9时,r为10

链式基数排序

基于静态链表的数据结构

#include <stdio.h>
#define MAX_KEY_NUM 8
#define MAX_SPACE 100
#define RADIX 10
#define ord(c) ((c) - ('0'))

typedef char keyType;
typedef int ArrType[RADIX]; //指针数组
typedef struct {
    keyType keys[MAX_KEY_NUM];
    int next;
}SLCell;
typedef struct {
    SLCell elem[MAX_SPACE];
    int keyNum;
    int num;
}SLList;

//分配
void distribute(SLList* sl, int i, ArrType f, ArrType e) {
    int j, p = 0;
    //将辅助数组置0
    for (int i = 0; i < RADIX; i++) {
        f[i] = e[i] = 0;
    }
    while (p = (*sl).elem[p].next) {
        j = ord((*sl).elem[p].keys[i]); //将数字字符映射到[0....RADIX - 1]
        if (!f[j])
            f[j] = p; //指向链表首结点
        else
            (*sl).elem[e[j]].next = p;
        e[j] = p; //e[j] 指向链表末结点
    }
}

void collect(SLList* sl, ArrType f, ArrType e) {
    int i = 0, tail;
    while (i < RADIX && f[i] == 0) i++;
    if (i < RADIX) {
        (*sl).elem[0].next = f[i];
        tail = e[i];
        while (i < RADIX) {
            i++;
            while (i < RADIX && f[i] == 0) i++; //找到下一个非空子表
            if (i < RADIX) { //链接两个非空子表
                (*sl).elem[tail].next = f[i];
                tail = e[i];
            }
        }
    }
    (*sl).elem[tail].next = 0; //保持循环链表
}

//基数排序
void radixSort(SLList* sl) {
    ArrType f, e;
    for (int i = (*sl).keyNum - 1; i >= 0; i--) {
        distribute(sl, i, f, e);
        collect(sl, f, e);
    }
}

void traverse(SLList* sl) {
    int p = 0;
    while (p = (*sl).elem[p].next) {
        for (int i = 0; i < (*sl).keyNum; i++) {
            printf("%c", (*sl).elem[p].keys[i]);
        }
        printf("\n");
    }
}

int main() { //测试方法写的不够严谨,仅供测试
    int k = 0, c = 1;
    SLList sl;
    int keyNum = 3;
    char str[] = "278,109,063,930,589,184,505,269,008,083"; 
    int i = 0;
    while (str[i] != '\0') {
        k = 0;
        while (str[i] != ',' && str[i] != '\0') {
            sl.elem[c].keys[k++] = str[i++];
        }

        if (str[i] == ',') {
            c++;
            i++;
        }
    }

    sl.keyNum = keyNum;
    sl.num = c;

    for (k = 0; k < c; k++) { //链接结点
        sl.elem[k].next = k + 1;
    };
    
    sl.elem[k].next = 0; //设置为循环链表
    radixSort(&sl);
    traverse(&sl);
    return 0;
}