数据结构——表(3)链表的应用

注:本文为根据《数据结构与算法分析》一书所做笔记与理解

前两节(《数据结构——表(1)》《数据结构——表(2)》)介绍了链表这一数据结构如何实现,这一节用C++语言介绍如何实现《数据结构与算法分析》一书中所提到的使用链表的例子。

1 多项式

1.1 多项式的表示

在这里插入图片描述
我们使用单链表来实现多项式。多项式由多个单项式组成,因此对于多项式来说,它最重要的有两个部分:(1)每个单项式的次数。(2)每个单项式的系数。
我们用一个多项式节点(PolyNode)来表示其中的一个单项式。这个节点的数据成员需要有(1)系数;(2)次数;(3)指向下一个单项式的next指针。

class PolyNode{
public:
	int coeffi;	//系数
	int expon; 	//次数
	PolyNode* next; 
	
	//构造函数
	PolyNode(){
		coeffi = 0;
		expon = 0;
		next = NULL;
	} 
	PolyNode(int c , int e , PolyNode* n = NULL){
		coeffi = c;
		expon = e;
		next = n;
	}
};

我们将多项式的每一项含在一个节点中,并且这些节点以次数递减的顺序排序,这样形成一个单链表。同样的,链表的表头不存储内容,我们将指向表头的指针用来表示这个多项式。比如多项式 ( 10x1000 + 5x14 + 1)就可以表示为下图所示的单链表:
在这里插入图片描述

1.2 多项式的加法

对于多项式的加法,就是将两个多项式次数相同的部分系数相加,得到一个新的多项式。
对于如何生成一个长的多项式,其实就是往初始多项式中不断加入新的单项式或者多项式。用加法也就可以实现,不用专门写一个函数来实现。当然,为了方便用户的使用,也可以写一个专门生成多项式的函数。
我在这里就写两种加法的实现吧,一种是输入两个多项式,返回它们的和;另一种是输入两个多项式A和B,将B加到A里面。它们实现原理其实是一样的,第二个就可以用来作为生成多项式的方法。

//往多项式1中添加单项式2(注意:poly2是单项式) 
void insert(PolyNode* poly1 , PolyNode* poly2){
	PolyNode* cur = poly1;
	int c = poly2->coeffi , e = poly2->expon;
	while(cur->next != NULL){
		if(cur->next->expon == e){
			cur->next->coeffi = cur->next->coeffi+c;
			return;
		}else if(cur->next->expon < e){
			poly2->next = cur->next;
			cur->next = poly2;
			return;
		}else
			cur = cur->next;
	}
	cur->next = poly2;
}

//两个多项式相加,返回相加之和的新的多项式 
PolyNode* addPoly(PolyNode* poly1 , PolyNode* poly2){
	PolyNode* sum = new PolyNode();
	PolyNode *p1 = poly1->next , *p2 = poly2->next , *s = sum;
	while(p1 != NULL && p2 != NULL){
		if(p1->expon > p2->expon){
			s->next = p1;
			s = s->next;
			p1 = p1->next;
		}else if(p1->expon < p2->expon){
			s->next = p2;
			s = s->next;
			p2 = p2->next; 
		}else{
			int c = p1->coeffi + p2->coeffi;
			s->next = new PolyNode(c , p1->expon);
			p1 = p1->next;
			p2 = p2->next;
		}
	}
	
	if(p1 != NULL)
		s->next = p1;
	if(p2 != NULL)
		s->next = p2;
		
	return sum;
}

2 基数排序

基数排序有时也称作卡式排序。因为直到现代计算机出现之前,它一直用于对老式穿孔机的排序。

2.1 桶式排序

先介绍桶式排序。
假设有N个整数,这些整数的范围在1到M之间(也就是说最小的不小于1,最大的不大于M)。
于是,我们留出一个数组,暂且称为count。count数组的大小为M,初始化为M个0.
这样一来,count就有M个单元,或者说有M个桶。刚开始,这些桶都是空的。
在这里插入图片描述
然后挨个读取N个整数,读到Ai的时候,Ai的值是多少,就把它放进相应的桶里。等到所有输入都被读进去以后,再扫描这M个桶,就可以打印出来排好序的表。
这个算法的花费为O(M+N)。
举例:这样一串整数:

1,8,6,5,7,3,4,2,1,9

放进桶里的情况如下:
在这里插入图片描述
然后再扫描所有桶,打印出排序后的表:

1,1,2,3,4,5,6,7,8,9

2.2 多趟桶式排序

上面介绍的桶式排序,效率很高。但是考虑这种情况:只有九个数需要排序,这九个数的范围在0~9999之间。这样我们就需要10000个桶给九个数排序,显得很多余,很浪费空间。于是提出使用多趟桶式排序。
我们用最低有效位先进行桶式排序,这样每个桶内可能有一个或多个不同的数字,也可能为空。然后再对次最低有效位进行桶式排序,以此类推。
由于每个桶都有可能有0~N个数,因此如果使用简单的数组来表示,那每个数组的大小都将是N,空间需求将会特别大。因此,我们使用链表来实现。
感觉这样说很抽象,还是用一个例子来解释:

64,8,216,512,27,729,0,1,343,125

为了简单,这里桶的个数设置为10,也就是说以10为基进行桶式排序。但是一般来说,会根据实际情况选择基。
先根据每个整数的最低有效位,把待排序的数放入桶中:
在这里插入图片描述
然后,再根据次最低有效位进行第二趟排序。
在前一趟排序的基础上,根据每个数的十位,将数重新放入桶中。
比如:
0号桶中的数0,十位是0,因此把0放入桶0中。
1号桶中的数1,十位是0,因此把1放入桶0中。
2号桶中的数512,十位是1,因此把512放入桶1中。
3号桶中的数343,十位是4,因此把343放入桶4。
……
从而得到新的桶的分布:
在这里插入图片描述
再根据百位(最高位,这是最后一趟了),进行第三趟排序:
在这里插入图片描述
最后,扫描目前的所有桶内的数字,即可得到排序后的序列:

0,1,8,27,64,125,216,343,512,729

要用链表实现桶式排序,需要给链表新增一些成员函数用于辅助:

//移动到表尾 
void toEnd(){
	while(!isLast()){
		cur = cur->next;
	}
}

//在表尾插入值为k的元素
void append(int k){
	toEnd();
	insert(k);
} 

//在表尾插入新的链表
void append(List list){
	toEnd();
	cur->next = list.head->next;
} 

//获取当前元素 
int getCur(){
	return cur->element;
}

接下来,用函数radixSort来实现排序,函数声明如下:

List radixSort(int M , int N , List before);

输入待排序整数的范围、数量,以及存储待排序数的链表。返回排好序的链表。
下面是我根据自己的理解写的代码实现,仅供参考:

List radixSort(int M , int N , List before){
	//以10为基 ,有10个桶 
	int n = 10;
	
	//确认排序的趟数,这里以10为基,那么趟数就是最大数的位数 
	//这里假设待排序的数在0~M范围内
	int times = 0;
	while(M != 0){
		++times;
		M /= 10;
	} 
	
	//初始化10个桶,每个桶中存储的是一个空链表
	vector<List> buckets(10); 
	//将所有待排序的数根据最低有效位放到对应的桶中 
	before.toHead();
	while(!before.isLast()){	//遍历所有节点 		
		before.toNext(); 
		int x = before.getCur();
		buckets[x%10].append(x);
	} 
	
	//每个循环完成一趟桶式排序,一共times趟
	//第i趟,根据第i最低位放入数 
	for(int i = 1 ; i <= times ; ++i){
		//需要计算哪一位
		int c = pow(10 , i); 
		//暂放数据的10个桶
		vector<List> temp(10);  
		//遍历10个buckets桶
		for(int j = 0 ; j < 10 ; ++j){
			List list = buckets[j];
			list.toHead();
			while(!list.isLast()){
				list.toNext();
				int x = list.getCur();
				temp[(x/c)%10].append(x);
			}
		} 
		buckets = temp;
	}
	
	//扫描所有桶,有序地将结果放在after中
	 List after = List();
	 for(int i = 0 ; i < 10 ; ++i){
	 	if(!buckets[i].isEmpty())
	 		after.append(buckets[i]);
	 }
	 
	 return after;
}

可以用以下代码对上面提到的例子进行测试:

//基数排序 
int M = 999;	//表示待排序的数范围在0~M之间
int N = 10;		//待排序的数的数量
List before;	//待排序数
before.append(64);
before.append(8);
before.append(216);
before.append(512);
before.append(27);
before.append(729);
before.append(0);
before.append(1);
before.append(343);
before.append(125);	
//	before.print();
List after = radixSort(M,N,before);
after.print();
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值