上一篇日志搞定了次位优先基数排序,就是把待排序列先按照最低位次位开始插入对应的桶中,然后收集所有的桶,完成一遍排序;接着继续获得次位,继续排序,直到数位达到最大关键字后完成排序。次位优先就是这样,先根据基数创建基数个桶,然后做MaxDigit(关键字)次的桶排序和收集桶操作。
有次位优先,也有主位优先。主位优先,就是把待排序列先按其主位排好序,然后对每一个主位桶再进行内部的次位优先基数排序。主位优先基数排序是这样的过程,先拿最经典的扑克牌来举例,然后我自己再用一个待排序列来说明。
扑克牌除了按牌面上的数字排序外,还要从花色上进行排序,例如梅花7比梅花5大,但是梅花7比黑桃4小,就是这个道理,扑克牌的花色我们看作是主位,牌面数字认为是次位。所以假设对扑克牌进行排序的话,我们通常是先按花色进行排序的,黑桃>红心>梅花>方、块,当按花色排好序后,再分别对每一个花色里面进行数字排序,最后统一收集即可。
再举个例子,假设我们的待排序列是:
44、12、59、36、62、43、94、7、35、52、85.。
按照主位排序后,每一个桶里面是这样的:
B[ 0 ]:7
B[ 1 ]:12
B[ 2 ]:NULL
B[ 3 ]:36、35
B[ 4 ]:44、43
B[ 5 ]:59、52
B[ 6 ]:62
B[ 7 ]:NULL
B[ 8 ]:85
B[ 9 ]:94
然后对每一个桶里面进行次位优先基数排序,桶内就会更新为:
B[ 0 ]:7
B[ 1 ]:12
B[ 2 ]:NULL
B[ 3 ]:35、36
B[ 4 ]:43、44
B[ 5 ]:52、59
B[ 6 ]:62
B[ 7 ]:NULL
B[ 8 ]:85
B[ 9 ]:94
最后把所有的桶按顺序收集起来即可。
好了,思路有了之后,开始用代码实现,首先是按主位排序,其实主位排序也是用桶排序实现,所以前部分代码其实和次位优先一样的:
//MSD主位优先基数排序
void Most_Significant_Digit_Sort(PtrlSqlList P, int L, int R, int N)
{
//递归函数对P->arr[L]到P->arr[R]的第N位数进行排序
int Di, i, j;
Bucket B; //创建一个桶数组
PtrlBucketNode tmp, p, List=NULL; //三个临时桶
//如果位数等于0了,就终止递归
if (N==0) {
return;
}
//先初始化每一个桶为一个空链表
for (i=0; iarr[i]);
}
List=tmp->Next; //!!!!
//然后开始排序过程
p=List;
//每一次排序后要回收到List中,所以用p来操作
while(p) {
Di=GetDigit(p->key, N); //获得当前元素的位数字
//printf("位数=%d\n", Di);
//然后把这个数字从List中删除
tmp=p; //然后把tmp插入到相应的桶中
p=p->Next;
tmp->Next=NULL; //把tmp弄成单独一个结点
//根据位数字把这个元素插入到相应的桶中
//特殊情况判断如果桶B[Di]为空
if (B[Di].Head==NULL) {
B[Di].Head=B[Di].Tail=tmp;
} else {
B[Di].Tail->Next=tmp;
B[Di].Tail=tmp;
}
}
i=j=L; //用i和j记录当前要处理的序列P的左右端点的下标
printf("\ni=%d j=%d N=%d\n", i, j, N);
//收集每一个桶,也就是收集Radix基数个桶,Di计数用
for (Di=0; DiNext;
P->arr[j]=tmp->key;
j++; //j最终回到R,也就是当前待排序列的长度
free(tmp);
}
printf("收集桶:");
PrintList(P, 0);
printf("\n");
//递归对该桶的元素进行排序,位数减1
Most_Significant_Digit_Sort(P, i, j, N-1);
i=j;
}
}
return;
}
创建一个桶数组,和三个临时桶方便后续操作。第225行首先把用户序列存入List链表中,然后235行把List表中的每一个元素按照主位Di来插入到相应的桶中,做完主位排序操作后,先让i和j同时从当前待排序列的开始位置L开始,对每一个桶进行排序并收集,也就是第255开始。对每一个桶进行排序,先判断桶里面有没有元素,有就把它按顺序收集回序列中,对主位桶内进行次位排序。每收集进来一个元素后j++,最后j会等于当前的桶里面的元素个数,然后我们把i作为起点,j做为终点对桶内的i到j元素进行递归排序即可,别忘了关键字要-1后再传进去哦!因为我们是对主位的下一位进行排序嘛。
为什么可以递归解决?因为主位排序时我们用的也是桶排序的,当我们对某一个主位桶内进行次位排序时,也是用的桶排序方法,所以递归调用即可。
在处理下一个桶前,我们先让i=j(第270行),为什么?因为在主位排序后的序列p中,对B[ 0 ]桶只有7这个元素,所以我们是做了一次排序,j=1。下一趟对B[ 1 ]桶做排序时,我们就是对j=2开始了,所以在每做完一个桶的排序后,都让i从p中的下一个桶的起点开始。
主位优先就是这样,在根据主位排序后,再按顺序对每一个主位序列里的元素进行排序,最后把主位序列按顺序收集起来就可以了!
完整代码已上传GitHub:
https://github.com/justinzengtm/Internal-Sorting/blob/master/Most_Significant_Digit_First_Sort.c
https://gitee.com/justinzeng/codes/9bvalwgt51kxr6jizhd7o65