【排序算法】基数排序:LSD 与 MSD

【排序算法】基数排序:LSD 与 MSD1.算法原理基数排序是通过“分配”和“收集”过程来实现排序。1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)2)收集,再将放置在0~9号桶中的数据按顺序放到数组中重复(1)(2)过程,从个位到最高位(比如32位无符号整形最大数4294967296,最高位10位)。而这个思想该如何理解呢?请看以下例子。(1)

大家好,又见面了,我是你们的朋友全栈君。1.算法原理

基数排序是通过“分配”和“收集”过程来实现排序。

1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)

2)收集,再将放置在0~9号桶中的数据按顺序放到数组中

重复(1)(2)过程,从个位到最高位(比如32位无符号整形最大数4294967296,最高位10位)。而这个思想该如何理解呢?请看以下例子。

(1)假设有欲排数据序列如下所示:

73 28 93 43 55 14 22 65 26 81

首先根据个位数的数值,在遍历数据时将它们各自分配到编号0至9的桶(个位数值与桶号一一对应)中。

从低位到高位分配收集过程:

【排序算法】基数排序:LSD 与 MSD

观察可以看到,此时原无序数据序列已经排序完毕。如果排序的数据序列有三位数以上的数据,则重复进行以上的动作直至最高位数为止。
基于两种不同的排序顺序,我们将基数排序分为

    LSD(Least significant digital):排序方式由数值的最右边(低位)开始

    MSD(Most significant digital):由数值的最左边(高位)开始。

注意一点:

LSD的基数排序适用于位数少的数列,如果位数多的话,使用MSD的效率会比较好。

MSD的方式由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。

由高位到低位分配收集过程:

     73 28 93 43 55 14 22 65 26 81


【排序算法】基数排序:LSD 与 MSD

(2)我们把扑克牌的排序看成由花色和面值两个数据项组成的主关键字排序。要求如下:

     花色顺序:梅花<方块<红心<黑桃

     面值顺序:2<3<4<…<10<J<Q<K<A

那么,若要将一副扑克牌排成下列次序:

     梅花2,…,梅花A,方块2,…,方块A,红心2,…,红心A,黑桃2,…,黑桃A。

有两种排序方法:

<1>先按花色分成四堆,把各堆收集起来;然后对每堆按面值由小到大排列,再按花色从小到大按堆收叠起来。—-称为”最高位优先”(MSD)法。

<2>先按面值由小到大排列成13堆,然后从小到大收集起来;再按花色不同分成四堆,最后顺序收集起来。—-称为”最低位优先”(LSD)法。

2.算法分析

平均时间复杂度:O(dn)(d即表示整形的最高位数)

空间复杂度:O(10n) (10表示0~9,用于存储临时的序列) 

稳定性:稳定

3.程序实现

(1)LSD法实现

最低位优先法首先依据最低位关键码Kd对所有对象进行一趟排序,

再依据次低位关键码Kd-1对上一趟排序的结果再排序,

依次重复,直到依据关键码K1最后一趟排序完成,就可以得到一个有序的序列。

使用这种排序方法对每一个关键码进行排序时,不需要再分组,而是整个对象组。

因为分配和收集阶段,数字符合先入先出的关系。因此可以用10个队列来保存 0-9 上分配的数字,在收集阶段,按先入先出的顺序取出每个桶中的数字,依次放到原数组中。

/********************************************************
*函数名称:GetNumInPos
*参数说明:num 一个整形数据
*     pos 表示要获得的整形的第pos位数据
*说明:    找到num的从低到高的第pos位的数据
*********************************************************/
int GetNumInPos(int num, int pos)
{
	int temp = 1;
	for (int i = 0; i < pos - 1; i++)
		temp *= 10;

	return (num / temp) % 10;
}

#define MAXPOS 10    //最高位的位数
void RadixSort(vector<int> &A)
{
	int len = A.size();
	vector<vector<int>> radixArray(10);  //分为0~9的序列空间

	for (int pos = 1; pos <= MAXPOS; pos++)    //从个位开始到最高位数
	{
		for (int i = 0; i < len; i++)    //分配过程
		{
			int num = GetNumInPos(A[i], pos);
			radixArray[num].push_back(A[i]);
		}

		for (int i = 0, j = 0; i < 10; i++)    //收集
		{
			while (!radixArray[i].empty())
			{
				A[j++] = radixArray[i].front();  //取首部数据依次插入原数组
				radixArray[i].erase(radixArray[i].begin());    //移除首部元素
			}
		}
	}
}

(2)MSD法实现

最高位优先法通常是一个递归的过程:

<1>先根据最高位关键码K1排序,得到若干对象组,对象组中每个对象都有相同关键码K1。

<2>再分别对每组中对象根据关键码K2进行排序,按K2值的不同,再分成若干个更小的子组,每个子组中的对象具有相同的K1和K2值。

<3>依此重复,直到对关键码Kd完成排序为止。

<4> 最后,把所有子组中的对象依次连接起来,就得到一个有序的对象序列。

/********************************************************
*函数名称:GetNumInPos
*参数说明:num 一个整形数据
*     pos 表示要获得的整形的第pos位数据
*说明:    找到num的从低到高的第pos位的数据
*********************************************************/
int GetNumInPos(int num, int pos)
{
 int temp = 1;
 for (int i = 0; i < pos - 1; i++)
  temp *= 10;
 return (num / temp) % 10;
}
//MSD,调用时指定最高位数d
void RadixSort(vector<int> &A, int d)
{
 int len = A.size();
 vector<vector<int>> radixArray(10);  //分为0~9的序列空间,用队列保存每个桶分配的数据
 //位数大于0,且数组长度大于1
 if (d >= 1 && len > 1)
 {
  for (int i = 0; i < len; i++)    //分配过程
  {
   int num = GetNumInPos(A[i], d);
   radixArray[num].push_back(A[i]);
  }
  cout << endl;
  for (int i = 0, j = 0; i < 10; i++)    //收集
  {
   RadixSort(radixArray[i], d-1);  //递归,对每个子桶从次高位开始分配
   while (!radixArray[i].empty())
   {
    A[j++] = radixArray[i].front();  //取队列首部数据依次插入原数组
    radixArray[i].erase(radixArray[i].begin());
   }
  }
 }
}

参考:http://blog.csdn.net/cjf_iceking/article/details/7943609

http://www.cnblogs.com/Braveliu/archive/2013/01/21/2870201.html





版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/132967.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Thinking in Java 系列 —(一)基本语法和操作

    Thinking in Java 系列 —(一)基本语法和操作前言本系列由阅读Thinkinjava4th英文原版完成。Thinkinjava作为最权威的java书籍之一,读起来其实并不通俗易懂,并不适合初学者。但是他的解释和语言是java运行的标准。当我读的时候有一些语句是非常直接且到位的表达了一种机制和他最简单的存在的意义。相信只有清楚的描述了每种机制或语法是如何发明出来的,才能够了解他如何使用。这也是本书的精髓。本系列会陆续进行更新。…

    2022年7月8日
    19
  • fedora14下载_乡村爱情12部下载

    fedora14下载_乡村爱情12部下载Fedora12正式版已于2009年11月18日正式发布,下面提供官方网站下载地址:下载主页:http://fedoraproject.org/zh_CN/get-fedora种子:http://torrent.fedoraproject.org/镜像:http://mirrors.fedoraproject.org/publiclist/Fedora/12/ …

    2022年9月20日
    0
  • 函数c()_函数的调用

    函数c()_函数的调用__builtin_系列函数

    2022年10月30日
    0
  • 【目录】python全栈工程师

    【目录】python全栈工程师第一阶段:Python语言核心编程1.Python核心–2048游戏核心算法2.面向对象–天龙八部游戏技能系统3.Python高级–集成操作框架项目:2048游戏第二阶段:

    2022年7月5日
    23
  • 【JAVA 课程设计 之 万年历】「建议收藏」

    距离2017年还有30多个小时~转眼间2016只剩一个尾巴了,大学生活也过了快一半了,自己却依旧那么笨手笨脚,不会的知识永远那么多,该看的书永远没机会去看,2017愿一切如昨天抽的签:远方不一定有诗,但有更好的自己~明天你好,请多关照~2017希望我的家人们,小伙伴们,以及所有帮助过我的朋友们都能健健康康,万事如意~Java课设远没有自己想的难,万年历,不用做显示面~也算2016JAVA的最后一

    2022年4月10日
    48
  • linux定时执行shell脚本「建议收藏」

    linux定时执行shell脚本「建议收藏」写一个shell脚本,定时执行简单示例很多时候我们有希望服务器定时去运行一个脚本来触发一个操作,比如说定时去备份服务器数据、数据库数据等不适合人工经常做的一些操作这里简单说下 Shell俗称壳,类似于DOS下的command和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;作为程…

    2022年9月4日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号