排序算法(二)

基数排序

原理

        参考

        对每一个数字,先按个位进行排序(个位相同的放在一起,不需要排序),再按十位进行排序,直到最高位。如果某个数字的位数较少,则该位数按0处理。如(11,1)按十位排序时,把1的十位当作0处理。

        在处理时,可以将该位上相同的数字按链表进行处理。类似于Java中HashMap的数组结果。如[10,100,1000,301]按十位进行排序时,100,1000,301是相同的,应该放在一个链表中。表头记录其中一个数的内存地址,用next表示下一个数的内存地址。因此,需要一个结构体,用来存储当前的数值以及下一个结构体的内存地址。

代码

首先是结构体定义,如下:

struct Entry{
    int value;//记录当前的数值
    struct Entry* next;//记录下一个位置的内存地址,没有时为0
};
其次,需要能获取某个数指定位上的数字的方法(来源于参考中的代码):
int GetNumInPos(int num,int pos)
{
    int temp = 1;
    for (int i = 0; i < pos - 1; i++)
        temp *= 10;
    
    return (num / temp) % 10;
}
最后,就是进行排序。如下:
void radix_sort(int a[],int count){
    struct Entry* b[10] = {0};//记录十个链表(0到9,每一个数字是一个链表)的起始位置
    int x = 0;
    int next = 1;//是否需要再进行循环,也就是说有没有处理到所有数字中的最高位数
    int pos = 0;//本次应该获取一个数字中的第几位(个位为1,十位为2,依次类推)
    while(next){
        pos++;
        next = 0;//假设本次循环完成后,已经到了最高位。再for循环中会进行修改
        for(x = 0;x<count;x++){//对每一个数进行循环
            int num = a[x];
            int bit = GetNumInPos(num, pos);//获取该数当前位上的数字
            struct Entry * entry = (struct Entry*)malloc(sizeof(struct Entry));
            entry->next = 0;//因为该数是新出来的,所以它肯定没有后继节点,即next为0
            entry->value = num;//将该数封装成一个结构体
            if(!b[bit]){//如果该链表没有出现过,则将新生成的结构体当作链表头
                b[bit] = entry;
            }else{
                struct Entry* start = b[bit];//如果链表已经存在,则获取表头
                while(start->next){//一直进行循环,直到链表的最后一个节点,此时它的next为0
                    start = start->next;
                }
                start->next = entry;//将新生成的链表挂载到链表的最后一个节点上
            }
            if(GetNumInPos(num, pos+1) || next){//判断该数是否还有更高位。如果有或者之前有数还有更高位,则还会再进行while循环。
                next = 1;
            }else{
                next = 0;
            }
        }//按某pos位的排序已经结束,下面需要调整a数组中的位置
        int y = 0;
        int index = 0;
        for(y = 0;y<count && index < count;y++){
            struct Entry* tem = b[y];
            while(tem != 0){
                a[index++] = tem->value;
                struct Entry* pre = tem->next;
                free(tem);//释放tem所代表的内存,因为它们都通过malloc()分配的
                tem = pre;
            }
            b[y] = 0;
        }
        printarray(a, count);//输出新调整后数组的内容,方法自已写
        printf("%d \n",next);//输出是否需要进行下次循环
    }
}
       上述代码只是粗略的仿制了java中hashmap的实现原理,实际上还有一些优化需要进行。如:有可能数据非常多,导致每一个链表过长,进而遍历到链表最后一个节点时太消耗时间,此时就需要扩充b数组的容量;还有可能在某个位时,数据大量集中在一个链表上(即该位上大量数据是相同的),导致链表分布不均匀,这就需要一定的算法将这些数据分开。

计数排序

原理

        遍历序列中的元素,找出元素的范围(即最大值与最小值),后建立一个专门用来存储某个元素出现几次的数组(因为序列中的元素的个数已经确定了,所以这个数组的大小也就确定了)。最后根据数组中每一个元素对原数组进行调整。

代码

void count_sort(int a[],int count){
    int min = a[0];
    int max = a[0];
    int x = 0;
    for(x = 0;x<count;x++){//获取数组中最小值与最大值
        min = MIN(min,a[x]);
        max = MAX(max,a[x]);
    }
    int numcount = max - min +1;
    int countarray[numcount];
    for(x = 0;x<numcount;x++){
        countarray[x] = 0;
    }
    for(x = 0;x<count;x++){
        countarray[a[x]-min]++;
    }
    max = 0;
    for(x = 0;x<numcount;x++){
        int total = countarray[x];//代表该数字出现几次
        for(min = 0;min < total;min++){//出现几次就需要往a数组中添加几次
            a[max++] = x;
        }
    }
}
        从上面代码可以得出,首先需要一个数组,其个数为原序列中最大值到最小值的个数,因此如果原序列的元素跨度较大,就非常浪费空间。如果跨度较小,该种排序方式就非常迅速。

二叉树排序

原理

        参考,这篇文章是Java代码的。但看思想就行。

        由于二叉树的左子树中所有节点都比根节点小,右子树的所有节点都比根节点大。所以最左边的叶子节点(从根节点沿着左子树往下寻找,走到某个节点的左子树为NULL,则该左子树就是最左边的叶子节点)一定是最小的,它的父节点是次小的,其次才是父节点的右节点。

        同理,最右边的叶子节点是最大的。

        在输出时,只要先输出某个节点的左节点,再输出自身,最后输出它的右子树,那么顺序就是从小到大了。当然,也可按照右-中-左的顺序输出,得到的就是从大往小了。

代码

首先,将值构建成一个结构体。如下:

struct TreeNode{
    int value;
    struct TreeNode* left;
    struct TreeNode* right;
};
其次为每一个value值封装成TreeNode结构体,并将新生成的TreeNode结构体添加到二叉树中。如下:
//二叉树排序
void treesort(int a[],int count){
    struct TreeNode* root = NULL;
    int x = 0;
    for (x = 0; x<count; x++) {
        struct TreeNode* node = createNode(a[x]);//封装成结构体
        if(root == NULL){
            root = node;
        }else{
            addnode(&root,node);//将结构体添加到二叉树中,root为二叉树的根节点
        }
    }
    PrintTree(root);//输出生成的树的节点信息,此时就是小从到大的顺序
}
最后,就是定义createNode(),addnode(),PrintTree()的方法了,如下:
static bool ToLeft(struct TreeNode* root,struct TreeNode* new){
    return new->value < root->value;
}

void PrintTree(struct TreeNode* root){
    if(root == NULL){
        return;
    }
    PrintTree(root->left);//先输出左节点
    struct TreeNode* right = root->right;
    printf("%d ",root->value);//再输出节点自身
    free(root);//释放根节点,因为这是通过malloc()申请的内存
    PrintTree(right);//最后输出右节点
}

struct TreeNode* createNode(int value){
    struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    node->left = NULL;
    node->right = NULL;
    node->value = value;
    return node;
}
//因为要修改父节点的值,所以用指针的指针
void addnode(struct TreeNode** root,struct TreeNode* node){
    struct TreeNode* r = *root;
    if(ToLeft(r, node)){
        if(r->left == NULL){//左子树为NULL,直接添加到左子树上
            r->left = node;
        }else{//左子树不为空,那么新节点应该在左子树的某个位置。递归该方法即可
            addnode(&r->left, node);
        }
    }else{//如果两者相同,则将新节点放在原节点右边,这样输出时能保证两者的顺序
        if(r->right == NULL){
            r->right = node;
        }else{
            addnode(&r->right, node);
        }
    }
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值