基數排序法

http://openhome.cc/Gossip/AlgorithmGossip/RadixSort.htm

說明

在之前所介紹過的排序方法,都是屬於「比較性」的排序法,也就是每次排序時 ,都是比較整個鍵值的大小以進行排序。

這邊所要介紹的「基數排序法」(radix sort)則是屬於「分配式排序」(distributionsort),基數排序法會使用到「桶子」(bucket),顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些「桶」中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度為O(nlog(r)m),其中r為所採取的基數,而m為堆數,在某些時候,基數排序法的效率高於其它的比較性排序法。

解法

基數排序的方式可以採用LSD(Least sgnificant digital)或MSD(Most sgnificant digital),LSD的排序方式由鍵值的最右邊開始,而MSD則相反,由鍵值的最左邊開始。

以LSD為例,假設原來有一串數值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81

首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中:
0 1 2 3 4 5 6 7 8 9

81


65


39



43 14 55

28



93







22 73






接下來將這些桶子中的數值重新串接起來,成為以下的數列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39

接著再進行一次分配,這次是根據十位數來分配:
0 1 2 3 4 5 6 7 8 9


2839






14 22
43 55 65 73 81 93

接下來將這些桶子中的數值重新串接起來,成為以下的數列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93

這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數為止。

LSD的基數排序適用於位數小的數列,如果位數多的話,使用MSD的效率會比較好,MSD的方式恰與LSD相反,是由高位數為基底開始進行分配,其他的演算方式則都相同。

原文有作者自己写的各种语言的代码

这里我改写了下作者写的c代码

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <vector>
using namespace std;
void swap(int &a,int&b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
void radix_sort(int *array,int number_of_data,int length)
{
	int counter = 0;
	int n =1;
	while (counter<number_of_data)
	{
		int counter_for_index[10] = {0};//桶中的计数器 表示放了多少个元素
		int tmp[10][10] = {0};
		//将数据放入对应的桶中
		for (int i = 0;i<length;i++)
		{
			int index = (array[i]/n)%10;
			tmp[index][counter_for_index[index]++] = array[i];
		}
		n*=10;
		counter++;//位数计数器
		//将放入桶中的数据写入原数组
		int k = 0;
		for (int i= 0;i<10;i++)
		{
			for (int j = 0;j<counter_for_index[i];j++)
			{
				array[k++] = tmp[i][j];
			}
		}
	}

}

void main()
{
	int a[] = {111,2,9,67,3,5,6,6,7,8};
	radix_sort(a,3,sizeof(a)/sizeof(int));
	for (int i = 0;i<sizeof(a)/sizeof(int);i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
	getchar();
}

 下面是用队列和向量实现的MSD

from http://angels1.0.blog.163.com/blog/static/8458050420099294956971/

采用MSD过程如下:

   第一次:    所有元素按照百位排序          

    0 号:059

    1 号:179

    2 号:232

    3 号:332 361

    4 号:457 405

    5 号:589

    6 号:633 644

    7 号:714

    8 号:825

    9 号:

    形成序列: 059 179 232 332 361 457 405 589 633 644 714 825 (已经基本有序)

 第二次:桶内元素按照十位排序 

   从上面的可以看出只有3号4号和6号需要继续进行排序:  

   3号桶中                  4号桶中                     6号桶中

     0 号:                  0 号: 405                 0 号:

    1 号:                   1 号:                        1 号:

    2 号:                   2 号:                        2 号:

    3 号:332             3 号:                        3 号: 633  

    4 号:                   4 号:                        4 号:

    5 号:                   5 号:457                  5 号:

    6 号:361             6 号:                        6 号: 644

    7 号:                  7 号:                        7 号:

    8 号:                   8 号:                        8 号:

    9 号:                    9 号:                       9 号:

   形成序列: 059 179 232 332 361 405 457 589 633 644 714 825 (其实已经有序了)

第三次:按照各位对各个桶元素排序:

    由于各个桶内只剩下一个元素或没有元素,下面的就不再写了


#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <vector>
#include <queue>
using namespace std;
//dem从0开始计算 获得num的第dem位
int getradix(int num,int dem)
{
	while (dem--)
	{
		num/=10;
	}
	return (num % 10);
}
//dem表示数据的从0开始 如果是两位数 数据最多占据0 1 位置dem初始为1
void radix_sort(vector<int>&data,int dem )
{
	if (data.empty()||dem<0)
	{
		return ;
	}
	vector<queue<int>> buckets(10);
	//将元素放入桶中
	for (int i = 0;i<data.size();i++)
	{
		int index = getradix(data[i],dem);
		buckets[index].push(data[i]);
	}
	//桶中的元素其实放在队列中存储的 所以先将队列中的元素放入向量中
	for (int i = 0;i<10;i++)
	{
		//当元素个数大于1时 进行排序
		if (buckets[i].size()>1)
		{
			vector<int> tmp;
			//将桶中的元素放入向量tmp中
			while (!buckets[i].empty())
			{
				tmp.push_back(buckets[i].front());
				buckets[i].pop();
			}
			//递归进行排序 按dem-1位排序
		    radix_sort(tmp,dem-1);				
			//将排好序的元素放入对于的桶中
			for (int j = 0;j<tmp.size();j++)
			{
				buckets[i].push(tmp[j]);
			}
		}// if num of  elem in bucket is big than 1,sort
	}//for every bucket

	//将桶中排好序的元素写入原向量中
	int data_index = 0;
	for (int i = 0;i<10;i++)
	{
		while (!buckets[i].empty())
		{
			data[data_index++] = buckets[i].front();
			buckets[i].pop();
		}
	}
}
void main()
{
	vector<int> a;
	srand(time(NULL));
	
	for (int i = 0;i<600;i++)
	{
		a.push_back(rand()%100);
	}
	radix_sort(a,1);
	for (int i = 0;i<a.size();i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
	getchar();
}



下载方式:https://pan.quark.cn/s/a4b39357ea24 布线问题(分支限界算法)是计算机科学和电子工程领域中一个广为人知的议题,它主要探讨如何在印刷电路板上定位两个节点间最短的连接路径。 在这一议题中,电路板被构建为一个包含 n×m 个方格的矩阵,每个方格能够被界定为可通行或不可通行,其核心任务是定位从初始点到最终点的最短路径。 分支限界算法是处理布线问题的一种常用策略。 该算法与回溯法有相似之处,但存在差异,分支限界法仅需获取满足约束条件的一个最优路径,并按照广度优先或最小成本优先的原则来探索解空间树。 树 T 被构建为子集树或排列树,在探索过程中,每个节点仅被赋予一次成为扩展节点的机会,且会一次性生成其全部子节点。 针对布线问题的解决,队列式分支限界法可以被采用。 从起始位置 a 出发,将其设定为首个扩展节点,并将与该扩展节点相邻且可通行的方格加入至活跃节点队列中,将这些方格标记为 1,即从起始方格 a 到这些方格的距离为 1。 随后,从活跃节点队列中提取队首节点作为下一个扩展节点,并将与当前扩展节点相邻且未标记的方格标记为 2,随后将这些方格存入活跃节点队列。 这一过程将持续进行,直至算法探测到目标方格 b 或活跃节点队列为空。 在实现上述算法时,必须定义一个类 Position 来表征电路板上方格的位置,其成员 row 和 col 分别指示方格所在的行和列。 在方格位置上,布线能够沿右、下、左、上四个方向展开。 这四个方向的移动分别被记为 0、1、2、3。 下述表格中,offset[i].row 和 offset[i].col(i=0,1,2,3)分别提供了沿这四个方向前进 1 步相对于当前方格的相对位移。 在 Java 编程语言中,可以使用二维数组...
源码来自:https://pan.quark.cn/s/a4b39357ea24 在VC++开发过程中,对话框(CDialog)作为典型的用户界面组件,承担着与用户进行信息交互的重要角色。 在VS2008SP1的开发环境中,常常需要满足为对话框配置个性化背景图片的需求,以此来优化用户的操作体验。 本案例将系统性地阐述在CDialog框架下如何达成这一功能。 首先,需要在资源设计工具中构建一个新的对话框资源。 具体操作是在Visual Studio平台中,进入资源视图(Resource View)界面,定位到对话框(Dialog)分支,通过右键选择“插入对话框”(Insert Dialog)选项。 完成对话框内控件的布局设计后,对对话框资源进行保存。 随后,将着手进行背景图片的载入工作。 通常有两种主要的技术路径:1. **运用位图控件(CStatic)**:在对话框界面中嵌入一个CStatic控件,并将其属性设置为BST_OWNERDRAW,从而具备自主控制绘制过程的权限。 在对话框的类定义中,需要重写OnPaint()函数,负责调用图片资源并借助CDC对象将其渲染到对话框表面。 此外,必须合理处理WM_CTLCOLORSTATIC消息,确保背景图片的展示不会受到其他界面元素的干扰。 ```cppvoid CMyDialog::OnPaint(){ CPaintDC dc(this); // 生成设备上下文对象 CBitmap bitmap; bitmap.LoadBitmap(IDC_BITMAP_BACKGROUND); // 获取背景图片资源 CDC memDC; memDC.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = m...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值