排序算法之链式基数排序(详解)

所谓链式基数排序,就是实现基数排序时,为减少所需的辅助存储空间,应采用链表作存储结构,即链式基数排序。而基数排序也叫做多关键字排序,基数排序是一种借助“多关键字排序”的思想来实现“单关键字排序”的内部排序算法。

链式的基数排序算法解法思路(默认从小到大):
1、以静态链表存储待排记录,并令表头指针指向第一个记录;
2、“分配” 时,按当前“关键字位”所取值,将记录分配到不同的 “链队列” 中,每个队列中记录的 “关键字位” 相同;
3、“收集”时,按当前关键字位取值从小到大将各队列首尾相链成一个链表;
4、对每个关键字位均重复 2 和 3 两步。
例:链式基数排序,下面以静态链表存储待排记录,并令表头指针指向第一个记录。
在这里插入图片描述
“分配” 时,按当前“关键字位”所取值,将记录分配到不同的“链队列”中,每个队列中记录的 “关键字位” 相同。 因为是 LSD,故从地位开始 ,也就是kd-1位开始,进行一趟分配:
在这里插入图片描述
这里的e[i]表示按哪个位进行排序变成f[i]。分别为这个队列的队头和队尾,先入先出的“链队列”或者是一个个的“桶”。
然后xx9,xx3,xx0
在这里插入图片描述
又遇到了 xx9,那么按照链式队列的存储方式,先进先出的入队(类似一个桶,数据从上面进入,从下面露出)
在这里插入图片描述
第一趟收集:按当前关键字位取值从小到大将各队列首尾相链成一个链表;(从队列的下面出去,先进先出)
在这里插入图片描述
进行第二趟分配:
在这里插入图片描述
进行第二题收集
在这里插入图片描述
进行第三趟分配:
在这里插入图片描述
进行第三趟收集
在这里插入图片描述
这样的话序列按照多关键字从小到大的排序有序了。
具体代码如下:

#include<iostream>
#include<malloc.h>
using namespace std;
//链式队列的节点结构,模拟桶
struct Node
{
	int data;//数据域
	Node *next;//指针域
};
//定义程序所需的特殊队列
class Queue
{
private:
	Node *front;//链式对列的头指针
	Node *rear;//链队的尾指针
public:
	//构造函数,初始化队列(带头结点的链式队列)
	Queue()
	{
		//开始先构造一个空结点,没有数据元素存储
		Node *p = new Node;
		p->data = NULL;
		p->next = NULL;
		//开始是空链队,首尾指针分别去指向队头结点
		front = p;
		rear = p;
	}
	//析构函数,销毁链队的结点占据的内存
	~Queue()
	{
		//标记指针
		Node *p = front;
		//辅助的标记指针,作用是删除结点
		Node *q;
		//循环遍历整个队列,直到标记指针 p 为 null
		while (p != NULL)
		{
			//比较常见的删除结点内存的写法
			q = p;
			//指向队列的下一个结点
			p = p->next;
			//销毁之
			delete q;
		}
	}
	//入队方法,从尾进入,节点不存在,需要自行创建结点的方法
	void push(int e)
	{
		Node *p = new Node;
		p->data = e;
		//本结点作为了队列的尾结点
		p->next = NULL;
		//然后连接结点到队尾
		rear->next = p;
		//最后尾指针指向新的末位结点
		rear = p;
	}
	//入队方法,尾进入,节点原来就存在的方法,不需要再新建结点和存储结点的内容
	void push(Node *p)
	{
		//设置此结点为尾结点
		p->next = NULL;
		//链接结点
		rear->next = p;
		//尾指针指向新的尾结点
		rear = p;
	}
	//求数据元素的最大位数的方法,也就是求出需要分配和收集的次数
	int lengthData()
	{
		int length = 0;//保存数据元素的 最大位数
		int n = 0;   //单个数据元素具有的位数
		int d;      //用来存储待比较的数据元素
		//指示指针
		Node *p = front->next;
		//遍历
		while (p != NULL)
		{
			//取出结点的数据,也就是代比较的数据元素
			d = p->data;
			//如果 d 为正数,很重要的一个技巧,必须是 d 大于 0 的判断
			while (d > 0)
			{
				//数据位数分离算法
				d /= 10;
				//单个数据元素的位数存储在此
				n++;
			}
			//沿着链队后移一个元素
			p = p->next;
			//找出数据元素的最大位数
			if (length < n)
			{
				length = n;
			}
			//重新循环往复,n 设置为0
			n = 0;
		}
		//返回最终位数
		return length;
	}
	//判断队列是否为空
	bool empty()
	{
		//队头指针和队尾指针重合,说明空
		if (front == rear)
		{
			return true;
		}
		//否则为不空
		return false;
	}
	//清除队列中的元素
	void clear()
	{
		//直接把头结点之后的链接断开
		front->next = NULL;
		//设置尾指针指向头结点即可,回到了构造函数初始化的情景
		rear = front;
	}
	//输出队列中的元素,传入引用参数比较好
	void print(Queue &que)
	{
		//第一个结点是头结点,next 才是第一个存储元素的结点
		Node *p = que.front->next;
		//直到尾结点为止
		while (p != NULL)
		{
			cout << p->data << " ";
			//遍历所有结点
			p = p->next;
		}
	}
	//基数排序过程
	void RadixSort(Queue& que)
	{
		//声明一个指针数组,该指针数组中存放十个指针,这十个指针需要分别指向十个队列,这是模拟10个桶,因为是0-9的数字,取值范围为10
		Queue *arr[10];
		//初始化这十个队列
		for (int i = 0; i < 10; i++)
		{
			//初始化建立头结点
			arr[i] = new Queue;
		}
		//取得待排序数据元素中的最大位数
		int maxLen = que.lengthData();
		//因为是 LSD 方式,从后到前,开始比较关键字,然后分配再收集,故开始设置数据分离算法中的除数为 1
		int d = 1;
		//将初始队列中的元素分配到十个队列中,maxlen 代表了需要分配和收集的次数
		for (int i = 0; i < maxLen; i++)
		{
			Node *p = que.front->next;
			//辅助指针 q
			Node *q;
			//余数为k,则存储在arr[k]指向的链式队列(桶)中
			int k;
			//遍历原始序列
			while (p != NULL)
			{
				//重要的技巧,数据分离算法过程,最后勿忘模10,取余数,分离出需要的关键字位
				k = (p->data / d) % 10;
				q = p->next;
				//把本结点 p 加入对应的队列中
				arr[k]->push(p);
				//指针后移,指向下一个结点
				p = q;
			}
			//清空原始队列
			que.clear();
			//分配完毕,马上将十个队列中的数据收集到原始队列中
			for (int i = 0; i < 10; i++)
			{
				if (!arr[i]->empty())
				{
					//从首节点开始遍历,不是头结点开始
					Node *p = arr[i]->front->next;
					//辅助指针 q
					Node *q;
					while (p != NULL)
					{
						q = p->next;
						//收集到原始队列中,这就是为什么每次分配完毕,需要清除原始队列
						que.push(p);
						p = q;
					}
				}
			}
			//一趟的分配收集完毕,最后要清空十个队列
			for (int i = 0; i < 10; i++)
			{
				arr[i]->clear();
			}
			//进行下一趟的分配和收集
			d *= 10;
		}
		//输出队列中排好序的元素
		print(que);
	}
};

int main(void)
{
	Queue oldque;
	int i;

	cout << "输入 int 类型的待排序的整数序列:输入 ctrl+z 结束输入,再回车即可" << endl;
	//顺序输入元素
	while (cin >> i)
	{
		oldque.push(i);
	}
	//基数排序
	oldque.RadixSort(oldque);

	return 0;
}

程序运行结果:
在这里插入图片描述
原文链接: https://www.cnblogs.com/kubixuesheng/p/4374225.html

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值