常见错误
最常遇到的错误是程序崩溃,这通常意味着指针变量包含了伪地址,一个通常原因是未初始化变量,另一个原因是寻找非法的指向,如NULL。前一篇文章若传的参数为NULL,则程序会崩溃,当然我们如此写例程是知道参数不会为NULL的,不过我们还是得确保例程能够处理这一情况。所以在每个例程开始时 检查参数 也是我们要考虑的事。
其次是malloc的使用,要知道声明一个指向该结构的指针并不会创建该结构,当我们想要创建一个链表时,我们可以先声明指向表头结构的指针,此时表头并未被创建,用malloc创建一个未被声明的空间,然后将空间里的数据改为需要的值,在把此指针指向该空间,这时表头的创建才算完成。创建一个新节点也是如此。还要注意当系统大量空间被占用满足不了你的空间需求时,malloc会返回空指针,这种情况也需要考虑到。
还有前一篇文章提到的若需要释放空间时,需要格外注意释放空间的顺序,避免出现释放了某指针P存放的地址后,继续用指针P访问这个地址。
另外链表还有双链表和循环链表等结构,双链表即是在单链表原基础上附加域,它包含指向前一个单元的指针。循环链表是让最后一个单元指向第一个单元(或表头),第一个单元(或表头)指向最后一个单元。这些都不难实现,接下来提供三个使用链表的例子
例子
多项式ADT
我们可以用表来定义一种关于(非负幂)多项式的抽象数据类型,令。先看看数组实现相加相乘操作,如果大部分系数非零,我们可以用简单数组来存储这些系数。然后可以编写一些对多项式加、减、乘、微分及其他操作的例程。
类型声明:
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的部分,这部分实际上不存在。如乘法例程的运行时间与两个输入多项式的次数的乘积成正比。如,这里循环次数就达到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为空则运行时间为,其他情况运行时间为。
需要注意当用于接收的表空间不够时需要增加结点,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;
}
}
}
乘法例程:
乘法例程给出一个运行时间为的例程,若有更好的算法欢迎提出。
一开始可以考虑单独处理空表情况,如有空表则直接返回空表。也可选择不做单独处理
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 就实现了。