基数排序
原理
对每一个数字,先按个位进行排序(个位相同的放在一起,不需要排序),再按十位进行排序,直到最高位。如果某个数字的位数较少,则该位数按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);
}
}
}