排序 - 次位优先实现基数排序(C++、静态链表)

基数排序:

假如直到某个长度为n的序列中所有记录的值都在0~m-1之间。当值域m很大时可以对桶式排序做一些改进:将排序码(待排数据)拆分成多个部分进行比较。例如,如果要对0~9999之间的整数进行排序,可以先按千、百、十、个位拆分。这种将排序码按照其进制数的基数进行拆分排序的方法就是基数排序,是分配排序的一种特例。

低位优先法(least significant digit first,LSD)分配排序

从最低位K0开始排序,对于排好的序列再用次低位K1排序,依次重复,直至对最高位Kd-1排好序后,整个序列成为有序的。这是一个分、收;分、收;......;分,收的过程。

链式存储的LSD

这部分要用文字表述过于复杂,SO,直接上图.....

 

初始序列

64

8

216

512

27

729

0

1

343

125

Bucket

0

1

2

3

4

5

6

7

8

9

个位

0

1

512

343

64

125

216

27

8

729

十位

0

512

125

 

343

 

64

 

 

 

 

1

216

27

 

 

 

 

 

 

 

 

8

 

729

 

 

 

 

 

 

 

百位

0

125

216

343

 

512

 

729

 

 

 

1

 

 

 

 

 

 

 

 

 

 

8

 

 

 

 

 

 

 

 

 

 

27

 

 

 

 

 

 

 

 

 

 

64

 

 

 

 

 

 

 

 

 

 

完整代码如下:(基于次位优先的链式LSD)

#pragma once
//RadixSort.h - 基数排序(静态链表)

#include <iostream>
using namespace std;

const int RADIX = 10;  //进制数
const int MaxDigit = 4;  //单个元素所具有的的最大位数

//桶元素节点
struct Node
{
	int key;  //待排数据
	struct Node* next;  //指向下一个桶节点的指针
};
//桶头节点
struct HeadNode
{
	struct Node *head, *tail;
};
typedef struct HeadNode Bucket[RADIX];

//Radix类定义
template <class T>
class Radix
{
	private:
		//桶数组,每一个元素都是一个结构体
		//该结构体用来形成一条单链表
		Bucket B;  //桶
		T* A;  //存储待排元素的数组
		int N;  //待排元素个数

	public:
		Radix(int size);  //构造函数
		~Radix() { delete[] A; }  //析构函数
		int GetDigit(int X, int D);  //获得当前元素的当前位
		void LSD_RadixSort();  //基于次位优先的基数排序
		void Print();  //输出结果
};

//Radix类的实现
//构造函数
template <class T> Radix<T>::Radix(int size)
{
	N = size;
	A = new T[N];

	for (int i = 0; i < N; i++)
		cin >> A[i];
}

//获得当前元素的当前位
template <class T> int Radix<T>::GetDigit(int X,int D)
{
	int d, i;
	for (i = 1; i <= D; ++i)
	{
		d = X % RADIX;  //求余
		X /= RADIX;  //缩小RADIX倍
	}

	return d;
}

//基于次位优先的基数排序
template <class T> void Radix<T>::LSD_RadixSort()
{
	int D, Di, i;  //D、Di - 当前位、i - 计数变量
	struct Node *tmp, *p, *List = NULL;  //初始链表表头List,采用头插法

	//初始化每个桶为空链表
	for (i = 0; i < RADIX; ++i)
		B[i].head = B[i].tail = NULL;

	//List就是用来存储数组元素的链表
	//List采用头插法生成链表
	for (i = 0; i < N; ++i)
	{
		tmp = new Node;
		tmp->key = A[i];
		tmp->next = List;
		List = tmp;
	}

	//下面次用次位优先的方法对数据进行排序
	for (D = 1; D <= MaxDigit; ++D)
	{
		//下面是分配的过程,用p来暂时接管这趟排序用到的List链表
		p = List;
		while (p)  //当链表不为空
		{
			Di = GetDigit(p->key, D);  //获得当前元素的当前位
		    //从List中摘除,从list链表头拿走一个节点 
            //这里的摘除只是从p链表中摘除,但是最初的List链表一直到在
		    //因为List要经历一次又一次的扫描,直到最高位 
			tmp = p;
			p = p->next;
			//插入B[Di]号桶尾,这里又采用尾插法
			tmp->next = NULL;
			if (B[Di].head == NULL)  //无头节点、链表为空
				B[Di].head = B[Di].tail = tmp;
			else
			{
				B[Di].tail->next = tmp;  //原先的尾节点连接上新加入进来的节点
				B[Di].tail = tmp;  //更新尾节点
			}
		}
		//一趟排序完成,下面扫描桶中的元素生成新的List链表
		List = NULL;
		for (Di = RADIX - 1; Di >= 0; --Di)
		{
			//这里建立的List链表也是采用头插法,倒着插
			if (B[Di].head)  //如果当前桶不为空
			{
				//将整桶插入List表头
				B[Di].tail->next = List;
				List = B[Di].head;  //更新表头
				B[Di].head = B[Di].tail = NULL;  //清空桶
			}
		}
	}

	//将List倒入A[]并释放空间
	for (i = 0; i < N; ++i)
	{
		tmp = List;
		List = List->next;
		A[i] = tmp->key;
		delete tmp;  //释放空间
		tmp = NULL;  //避免野指针
	}
}

//输出结果
template <class T> void Radix<T>::Print()
{
	for (int i = 0; i < N; ++i)
	{
		cout << A[i] << " ";
	}
}

int main()
{
	int N;
	cin >> N;  //待排元素个数 

	Radix<int> R(N);  //实例化对象 

	R.LSD_RadixSort();  //基数排序 

	R.Print();  //输出结果 

	return 0;
}

运行结果:

猜你喜欢

转载自blog.csdn.net/y_16041527/article/details/80463877