一天一练之基数排序

一.算法介绍。
        基数排序所用到的思想和前几练所讲到的排序思想完全不同。前几练的排序只是在进行元素之间大小的比较,然后交换位置来进行的排序。而基数排序不需要进行元素之间的比较和位置的交换。基数排序是一种借助多关键字进行排序的思想。我们可以对单一关键字进行逻辑划分为多个关键字。比如对与一组没有操作3位的自然数,我们可以将每个自然数划分为3个逻辑关键字,分别为百位数字为k1关键字,十位数字为K2关键字,个位数字为k3关键字,这样我们就把一个数字分为了三个关键字来表示。我们就可以通过这三个关键字来对一组数据进行“分配”和“收集”。

  所谓的分配,就是按照某一个关键字如按照k1关键字,来给这组数据进行分配,将元素在k1关键字上相同的元素放在一起,对于自然数自然可以分为0,1,2,3,4,5,6,7,8,9这十组不同的K1关键字,也就是说将一组数据按照k1关键字可以分为10组(有可能出现某一组或几组没有任何数据的情形)。这就是所谓的分配。

  收集的意思,就是在一次分配的基础上,在次按照某种顺序将这组数据进行排列,然后构造成为一个序列,当然如果只是进行了一次分配自然不可能收集到一组有序的序列,我们接下来的任务就是再次按照某个关键字如k2来进行分配,然后在收集,直到将所有的关键字都分配的一次,那么最后一次的收集到的数据将是排序后的序列了。

   看到这里,各位可能有一点不清楚的地方,按照关键字来进行分配,那么按照怎么样的顺序来选择关键字了,是先按照k1,k2,k3还是k3,k2,k1的顺序来分配了?还是根本不需要对关键字的选择顺序进行考虑。当然,我写这么多不可能跟你说不需要考虑顺序。so?那顺序到底是怎么样的了?

               对关键字的选择可以有两种不同的方式,一种是MSD(最高位优先),也就是说按照k1,k2,k3来进行关键字的选择,还有一种就是LSD(最低位优先),也就是按照k3,k2,k1的顺序来进行关键字的选择。由于对自然数来说,每个关键字所在的范围都是0-9,所以选择LSD方法,更为的方便。

二.源码分析
由于在分配的时候,需要将元素像扑克牌一样的一一堆在一起,然后收集的时候按照先进先出的特定来收集,所以自然而然的想到了要使用队列来保存每一个关键字的数据元素。而对于自然数关键字有10个,所以需要用10个队列来保存。

#include<iostream>

using namespace std ;

#define MAX_SIZE 100
#define QUEUECOUNT 10
//链表结点
struct node
{
 int data;
 struct node* next ;
};

//队列设计
struct Queue
{
 int data[MAX_SIZE];
 int front,rear;
};

//函数定义
//初始化单一队列
void InitQueue(Queue & _queue)
{
 _queue.front = 0 ;
 _queue.rear = 0 ;
 memset(_queue.data,0,MAX_SIZE);
}

//初始化所有队列
void Init(Queue queue[QUEUECOUNT])
{
 for (int i = 0 ; i< QUEUECOUNT ; i++)
 {
  InitQueue(queue[i]);
 }
}

//判断队列是否为空
bool IsEmpty(Queue&_queue)
{
 if (_queue.front == _queue.rear)
 {
  return true;
 }
 else
  return false;
}

//入队列
void EnQueue(Queue&_queue,int data)
{
 _queue.data[_queue.rear++]=data;
}

//出队列
int DeQueue(Queue&_queue)
{
 if (!IsEmpty(_queue))
 {
  return _queue.data[_queue.front++];
 }
 else
  return INT_MAX;
}
//进行分配的函数
void Distribute(node * head,Queue queue[QUEUECOUNT],int key)//元素是保存在一个链表中的,进行分配的意思就是将链表
{          //中的数据按照关键字0-9来聚簇       
 node * p = head->next ;
 switch (key)//key代表指定的关键字,如3代表上文中的k3关键字
 {
 case 3:
  for (;p != NULL;p=p->next)
  {
   int index = p->data%10;//计算出个位数是多少,这个数就是关键字,然后入在相对应的队列中
   EnQueue(queue[index],p->data);
  }
  break;
 case 2:
  for (;p!=NULL;p = p->next)
  {
   int index = (p->data/10)%10;//同上,这里计算的是十位数
   EnQueue(queue[index],p->data);
  }
  break;
 case 1:
  for (;p!=NULL;p=p->next)//这里计算的是百位数
  {
   int index = p->data/100;
   EnQueue(queue[index],p->data);
  }
  break;
 }
}

//收集函数
void Collect(node * head,Queue queue[QUEUECOUNT])//收集函数的意思是将10个关键字队列中的元素在此组合成一个链表,按照从0关键字开始,至9关键字结束 {
 node * temp = head ;
 for (int i = 0 ;i<QUEUECOUNT ; i++)
 {
  while (!IsEmpty(queue[i]))
  {
   node * p = new node ;
   p->next  = NULL ;
   p->data = DeQueue(queue[i]);
   temp->next = p ;
   temp = p ;
  }
 }
}

//基数排序函数
void RadixSort(node *head,Queue queue[QUEUECOUNT])
{
 for (int i =3 ; i >=1 ; i--)
 {
  Distribute(head,queue,i);
  Collect(head,queue);
 }

 node * p = head->next ;//将排序后的元素输出查看
 for (;p!=NULL;p=p->next)
 {
  cout<<p->data<<endl;
 }
}

//测试程序
int main()
{
 Queue queue[QUEUECOUNT];
 Init(queue);
 node * head = new node ;
 node * temp = head ;
 cout<<"请输入将要排序的元素的个数"<<endl;
 int i = 0 ;
 cin>>i ;
 for (int j = 0 ; j<i ;j++)
 {
  node * p =new node ;
  p->next = NULL ;
  cout<<"请输入元素:"<<endl;
  cin>>p->data;
  temp->next=p;
  temp=p;
 }

 RadixSort(head,queue);
 cin>>i;

 return 0 ;


}

 

  上述过程不清楚的可以查阅《c语言版数据结构》严蔚敏。上面有更加详细的解释。

三.总结
    基数排序时效率较高的排序方式,它拥有的思想是一种分配和收集的思想,掌握这种排序的方式,有助于我们思维的开阔。今天的总结就到这里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值