链表--扩展讨论与多项式ADT

        常见错误

        最常遇到的错误是程序崩溃,这通常意味着指针变量包含了伪地址,一个通常原因是未初始化变量,另一个原因是寻找非法的指向,如NULL。前一篇文章若传的参数为NULL,则程序会崩溃,当然我们如此写例程是知道参数不会为NULL的,不过我们还是得确保例程能够处理这一情况。所以在每个例程开始时 检查参数 也是我们要考虑的事。

        其次是malloc的使用,要知道声明一个指向该结构的指针并不会创建该结构,当我们想要创建一个链表时,我们可以先声明指向表头结构的指针,此时表头并未被创建,用malloc创建一个未被声明的空间,然后将空间里的数据改为需要的值,在把此指针指向该空间,这时表头的创建才算完成。创建一个新节点也是如此。还要注意当系统大量空间被占用满足不了你的空间需求时,malloc会返回空指针,这种情况也需要考虑到。

       还有前一篇文章提到的若需要释放空间时,需要格外注意释放空间的顺序,避免出现释放了某指针P存放的地址后,继续用指针P访问这个地址。


        另外链表还有双链表循环链表等结构,双链表即是在单链表原基础上附加域,它包含指向前一个单元的指针。循环链表是让最后一个单元指向第一个单元(或表头),第一个单元(或表头)指向最后一个单元。这些都不难实现,接下来提供三个使用链表的例子

        例子

        多项式ADT

        我们可以用表来定义一种关于(非负幂)多项式的抽象数据类型,令F(X)=\sum_{i=0}^{N}A_{i}X^i。先看看数组实现相加相乘操作,如果大部分系数非零,我们可以用简单数组来存储这些系数。然后可以编写一些对多项式加、减、乘、微分及其他操作的例程。

        类型声明:

        coeffArray代表系数数组,数组下标代表当前项的次幂,highPower代表最高次幂。

#define MAX 10
typedef struct
{
	int coeffArray[MAX + 1];
	int highPower;
}*polynomial;

        将多项式初始化为0的例程:

void ZeroPolynomial(Polynomial poly)
{
	int i;
	for (i = 0; i < MAX + 1; i++)
		poly->coeffArray[i] = 0;
	poly->highPower = 0;
}

        相加例程:

        polySum的最高次幂肯定是两者中的最高次幂。然后我们只需要遍历到最高次幂处即可,因为更高次幂的数不存在,也即是系数一定为0那么后面的就不用去管了。但是不乏从0到最高次幂的遍历中,也有系数为0的存在。

void AddPolynomial(const Polynomial poly1, const Polynomial poly2, Polynomial polySum)
{
	int i;
	ZeroPolynomial(polySum);
	polySum->highPower = Max(poly1->highPower, poly2->highPower);
	for (i = 0; i <= polySum->highPower; i++)
		polySum->coeffArray[i] = poly1->coeffArray[i] + poly2->coeffArray[i];
}

        相乘例程:

        polyProd最高次幂是两者最高次幂的和,这里需要考虑得到的二者之和是否超出数组的大小。

void MultPolynomial(const Polynomial poly1, const Polynomial poly2, Polynomial polyProd)
{
	int i,j;
	ZeroPolynomial(polyProd);
	polyProd->highPower = poly1->highPower + poly2->highPower;
	if (polyProd->highPower > MAX)
	{
		printf("Exceeded array size\n");
		return;
	}
	for (i = 0; i <= poly1->highPower; i++)
		for (j = 0; j <= poly2->highPower; j++)
			polyProd->coeffArray[i + j] = poly1->coeffArray[i] * poly2->coeffArray[j];
}

        前面提到了一个问题,即使是系数为0的时候,数组依然要处理这种情况,这就导致了如果两个多项式中大量的系数为0的话,会浪费很多时间在多项式中系数为0的部分,这部分实际上不存在。如乘法例程的运行时间与两个输入多项式的次数的乘积成正比。如P_{1}(X)=10X^{1000}+5X^{14}+1,P_{2}(X)=3X^{1990}-2X^{1492}+11X+5,这里循环次数就达到1001×1991次,(要多加一个次方,考虑常数的情况,数组下标为0的元素表示常数)。可以发现用于不存在的项的时间比有效的项的时间多得多。

       另一种方法则是使用单链表。多项式的每一项含在一个单元中,这些单元以次数递减顺序排序。

        类型声明:

typedef struct Node* PtrToNode;
typedef struct Node* List;

struct Node
{
	int coefficient;
	int exponent;
	PtrToNode next;
};

         基本判断、清空表、初始化表与删除表的例程:

int IsLast(PtrToNode node)
{
	return node->next == NULL;
}
int IsEmpty(List list)
{
	return list->next == NULL;
}
void MakeEmpty(List list)
{
	PtrToNode tmpNode = list->next;
	while (list->next != NULL)
	{
		list->next = tmpNode->next;
		free(tmpNode);
		tmpNode = list->next;
	}
}
void ZeroPolynomial(List list)
{
	MakeEmpty(list);
}

        结点创建:

PtrToNode CreateNode()
{
	return HeaderCreate();
}
List HeaderCreate()
{
	PtrToNode tmpNode;
	tmpNode = (PtrToNode)malloc(sizeof(struct Node));
	if (tmpNode != NULL)
	{
		tmpNode->coefficient = 0;
		tmpNode->exponent = 0;
		tmpNode->next = NULL;
	}
	return tmpNode;
}

         加法例程:

        我们需要考虑到有空表的情况,可以发现下列例程能够处理这种情况,若表2为空则运行时间为O(N),其他情况运行时间为O(N^2)

        需要注意当用于接收的表空间不够时需要增加结点,16行 if 语句的功能是判断若空间已满,则创建结点,若创建结点失败则跳出循环。若空间不满则21行tmpNode接收下一个单元的地址,或者空间满并创建结点成功后tmpNode会接收刚刚创建好的结点的地址。

void AddPolynomial(const List list1, const List list2, List listSum)
{
	PtrToNode i, j, k, tmpNode;
	ZeroPolynomial(listSum);
	tmpNode = listSum;
	for (i = list1->next; i != NULL; i = i->next)
	{
		if (IsLast(tmpNode) && (tmpNode->next = CreateNode()) == NULL)
		{
			printf("error in creating node");
			return;
		}
		tmpNode = tmpNode->next;
		tmpNode->coefficient = i->coefficient;
		tmpNode->exponent = i->exponent;
	}
	for (j = list2->next; j != NULL; j = j->next)
	{
		for (k = listSum->next; k != NULL; k = k->next)
		{
			if (j->exponent == k->exponent)
			{
				k->coefficient += j->coefficient;
				break;
			}
		}
		if (k == NULL)
		{
			if (IsLast(tmpNode) && (tmpNode->next = CreateNode()) == NULL)
			{
				printf("error in creating node");
				return;
			}
			tmpNode = tmpNode->next;
			tmpNode->coefficient = j->coefficient;
			tmpNode->exponent = j->exponent;
		}
	}		
}

        乘法例程:

        乘法例程给出一个运行时间为O(N^3)的例程,若有更好的算法欢迎提出。

        一开始可以考虑单独处理空表情况,如有空表则直接返回空表。也可选择不做单独处理

        6行和7行的循环遍历两个多项式的乘法。17行的循环用于判断当某两项进行乘法之后得到的次幂,若与listProd中某项次幂相等时则将前者的次幂加到listProd中那一项的次幂。若k=NULL说明listProd中没有哪一项的次幂与相乘得到的次幂相等,则此时将tmpNode接收创建的结点。

void MultPolynomial(const List list1, const List list2, List listProd)
{
	PtrToNode i, j, k, tmpNode, nextNode;
	ZeroPolynomial(listProd);
	if (IsEmpty(list1)||IsEmpty(list2))
		return;
	tmpNode = listProd;
	for (i = list1->next; i != NULL; i = i->next)
		for (j = list2->next; j != NULL; j = j->next)
		{
			if (IsLast(tmpNode) && (tmpNode->next = CreateNode()) == NULL)
			{
				printf("error in creating node");
				break;
			}
			nextNode = tmpNode->next;
			nextNode->exponent = i->exponent + j->exponent;
			nextNode->coefficient = i->coefficient * j->coefficient;
			for (k = listProd->next; k != NULL; k = k->next)
			{
				if (nextNode->exponent == k->exponent)
				{
					k->coefficient += nextNode->coefficient;
					break;
				}
			}
			if (k == NULL)
				tmpNode = nextNode;
		}
}

链表 多项式ADT 就实现了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值