内部排序(八)主位优先基数排序

       上一篇日志搞定了次位优先基数排序,就是把待排序列先按照最低位次位开始插入对应的桶中,然后收集所有的桶,完成一遍排序;接着继续获得次位,继续排序,直到数位达到最大关键字后完成排序。次位优先就是这样,先根据基数创建基数个桶,然后做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中的下一个桶的起点开始。

      主位优先就是这样,在根据主位排序后,再按顺序对每一个主位序列里的元素进行排序,最后把主位序列按顺序收集起来就可以了!

内部排序(八)主位优先基数排序_第1张图片

完整代码已上传GitHub:

https://github.com/justinzengtm/Internal-Sorting/blob/master/Most_Significant_Digit_First_Sort.c

https://gitee.com/justinzeng/codes/9bvalwgt51kxr6jizhd7o65

你可能感兴趣的:(内部排序,数据结构,C/C++)