排序4—归并排序与基数排序

前言:

     这篇博客应该是排序算法的最后一篇了,由于归并排序内容较少也比较好理解,基数排序也是很好理解的,所以就将它们一起写在一篇博客上了。另附一点外部排序的知识嘻嘻。

一、归并排序

概念归并排序就是将两个或两个以上的有序表合成一个有序表的过程。将两个有序表合成一个有序表的过程称为2-路归并,当然啦,还有3-路归并以及其他的多路归并,只是这里我们就说2-路归并,因为比较简单和常用。
     这个归并的算法看看图就特别好理解:
在这里插入图片描述
     你看第一趟归并之后就得到了四个顺序表,然后这四个又继续归并排序,又得到了第二趟的两个顺序表,直到最后一次归并排序就完成了。
代码就有:

//用算法 相邻两个有序子序列的归并
void Merge(RedType R[],RedType T[],int low,int mid,int high)
{ 
   //将有序表R[low..mid]和R[mid+1..high]归并为有序表T[low..high] 
	int i,j,k;
	i=low; j=mid+1;k=low; 
    while(i<=mid&&j<=high)
	{                 	
		//将R中记录由小到大地并入T中 
		if(R[i].key<=R[j].key) T[k++]=R[i++]; 
        else T[k++]=R[j++]; 
	} 
	while(i<=mid)                            		//将剩余的R[low..mid]复制到T中 
		T[k++]=R[i++];                 
	while(j<=high)                           		//将剩余的R[j.high]复制到T中 
		T[k++]=R[j++];                       
}//Merge 

二、基数排序

     之前说过的各类排序方法都是建立在关键字比较的基础上,而分配排序不需要比较关键字的大小,它是根据关键字中各位的值,通过对待排序记录进行若干趟“分配”与“收集”来实现排序的,是一种借助于多关键字排序的思想对关键字排序的方法。
     基数排序无论是多关键字的排序还是链式基数排序其实都是一样的。比如说扑克牌,我们排序的方式就有花色优先(最高位优先),或者最低位优先的方法。比如说数字的排序有图:
在这里插入图片描述
这样就很好理解了吧。
算法代码演示:

//算法 基数排序
#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define MAXNUM_KEY 8                	//关键字项数的最大值 
#define RADIX 10                        //关键字基数,此时是十进制整数的基数 
#define MAX_SPACE 10000 
typedef char KeysType;					//定义关键字类型为字符型
typedef int InfoType;					//定义其它数据项的类型
typedef struct
{ 
	KeysType keys[MAXNUM_KEY];          //关键字 
	InfoType otheritems;               	//其他数据项 
	int next; 
}SLCell;						    	//静态链表的结点类型 
typedef struct
{ 
	SLCell r[MAX_SPACE];		        //静态链表的可利用空间,r[0]为头结点 
	int keynum;				         	//记录的当前关键字个数 
	int recnum;					     	//静态链表的当前长度 
}SLList;							    //静态链表类型 
typedef int ArrType[RADIX];	          	//指针数组类型

void InitList(SLList *L)
{ 
	//初始化静态链表L(把数组D中的数据存于L中)
	char c[MAXNUM_KEY],c1[MAXNUM_KEY];
    int i,j,n,max;						//max为关键字的最大值 
    max=-10000;
    cout<<"请输入数据个数,不超过"<<MAX_SPACE<<"个。\n";
    cin>>n;
    while(n>MAX_SPACE)
    {
		cout<<"您输入的个数超过上限,请重新输入,不超过"<<MAX_SPACE<<"个。\n";
	    cin>>n;
    }
    int *D=new int[n];
	cout<<"请输入"<<n<<"个排排序的数据:\n";
    for(i=0;i<n;i++)
    {
	    cin>>D[i];
	    if(max<D[i])
			max=D[i];
    }
    (*L).keynum=(int)(ceil(log10(max)));
    (*L).recnum=n;
    for(i=1;i<=n;i++)
    {
        itoa(D[i-1],c,10);					//将10进制整型转化为字符型,存入c 
		for(j=strlen(c);j<(*L).keynum;j++)  //若c的长度<max的位数,在c前补'0' 
		{
		   strcpy(c1,"0");
		   strcat(c1,c);
		   strcpy(c,c1);
		}
		for(j=0;j<(*L).keynum;j++)
			(*L).r[i].keys[j]=c[(*L).keynum-1-j];
    }
}

int ord(char c)
{	
	//返回k的映射(个位整数)
	return c-'0';
}
void Distribute(SLCell *r,int i,ArrType &f,ArrType &e)
{ 
	//静态链表L的r域中记录已按(keys[0], …, keys[i-1])有序 
	//本算法按第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同。 
	//f[0..RADIX-1]和e[0..RADIX-1]分别指向各子表中第一个和最后一个记录
	int j,p;
	for(j=0;j<RADIX;++j)  f[j]=0;        	//各子表初始化为空表 
	for(p=r[0].next;p;p=r[p].next)
	{ 
		j=ord(r[p].keys[i]);                //ord将记录中第i个关键字映射到[0..RADIX-1] 
		if(!f[j])  f[j]=p; 
		else  r[e[j]].next=p; 
		e[j]=p;                          	//将p所指的结点插入第j个子表中 
	}//for 
}//Distribute 

int succ(int i)
{ 
	//求后继函数
    return ++i;
}
void Collect (SLCell *r,int i,ArrType &f,ArrType &e)
{ 
	//本算法按keys[i]自小至大地将f[0..RADIX-1]所指各子表依次链接成一个链表 
    //e[0..RADIX-1]为各子表的尾指针
	int j,t;
    for(j=0;!f[j];j=succ(j));  			//找第一个非空子表,succ为求后继函数 
    r[0].next=f[j];t=e[j];   			//r[0].next指向第一个非空子表中第一个结点 
    while(j<RADIX-1)
	{ 
		for(j=succ(j);j<RADIX-1&&!f[j];j=succ(j)) ;       	//找下一个非空子表 
		if(f[j])  {r[t].next=f[j];t=e[j];}		        	//链接两个非空子表 
	}//while 
	r[t].next=0;                		//t指向最后一个非空子表中的最后一个结点 
}//Collect 

void RadixSort(SLList &L)
{ 
	//L是采用静态链表表示的顺序表 
    //对L做基数排序,使得L成为按关键字自小到大的有序静态链表,L.r[0]为头结点
	int i;
	ArrType f,e;
    for(i=0;i<L.recnum;++i)  L.r[i].next=i+1; 
    L.r[L.recnum].next = 0;             	//将L改造为静态链表 
	for(i=0;i<L.keynum;++i) 
	{       	
		//按最低位优先依次对各关键字进行分配和收集 
		Distribute(L.r,i,f,e);		     	//第i趟分配 
		Collect(L.r,i,f,e);					//第i趟收集 
	}//for 
} // RadixSort
 
void print(SLList L)
{  
	//按数组序号输出静态链表
    int i,j;
    for(i=1;i<=L.recnum;i++)
    {
		for(j=L.keynum-1;j>=0;j--)
			cout<<L.r[i].keys[j];
	    cout<<endl;
    }
}
void Sort(SLList L,int adr[]) 
{ 
	//求得adr[1..L.length],adr[i]为静态链表L的第i个最小记录的序号
    int i=1,p=L.r[0].next;
    while(p)
    {
		adr[i++]=p;
		p=L.r[p].next;
	}
}
void Rearrange(SLList *L,int adr[])
{ 
	//adr给出静态链表L的有序次序,即L.r[adr[i]]是第i小的记录。
    //本算法按adr重排L.r,使其有序。算法10.18(L的类型有变) 
	int i,j,k;
    if(adr[i]!=i)
    {
		j=i;
		(*L).r[0]=(*L).r[i]; //暂存记录(*L).r[i]
		while(adr[j]!=i)
		{ 
			//调整(*L).r[adr[j]]的记录到位直到adr[j]=i为止
			k=adr[j];
			(*L).r[j]=(*L).r[k];
			adr[j]=j;
			j=k; //记录按序到位 
		}
		(*L).r[j]=(*L).r[0];
		adr[j]=j;
    }
}

void main()
{
	SLList l;
	int *adr;
	InitList(&l);
	RadixSort(l);
	adr=new int[l.recnum];
	Sort(l,adr);
	Rearrange(&l,adr);
	cout<<"排序后(重排记录):\n";
	print(l);
} 

三、外部排序

     之前所有的排序方法都是内部排序,整个排序过程都是在内存中完成的,并不涉及数据内外交换问题。但如果待排序的记录数目很大,无法一次性调入内存,整个排序过程就必须借用外存分批调入内存才能完成。
     这里的其它知识需要的话就自己探索哦,也可以和我交流。

后记:

     那么排序知识就到这里结束了,本专栏的数据结构的知识,从线性表、栈、队列、串、数组、广义表、树、二叉树、图到现在的查找排序基础知识就结束啦,后面可以做一篇总结吧。冲鸭!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

threecat.up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值