一,链式结构的地址形式
首先,要说明的是,在线性结构中的链式结构的结点都是地址,而在逻辑连接中也都是通过地址相连,即指针。在建立结点时,我们可以回顾一下动态数组的原理,而链式结构中的结点与动态数组的逻辑形式相同,在内存的堆区开辟一块地址空间,在运用结点的运算时,也就相当于调运指针的形式运算。
head = c = (link*)malloc(sizeof(link));//建立结点,两者的地址都一样
c->a = 5;
head->a = 4;
printf("%d %d ", c->a, head->a);//将都输出4,因为都是通过地址来进行改变数据的
在这里,我们知道了结点的意义,但是,需提醒一下,地址操作并不是可以随意按照自己的意愿进行逻辑管理,有关的地址操作只能根据地址而改变地址所对应的空间数据的形式,即"地址——数据"的运用,其他结点形式的改变不会影响其对应的地址空间数据的改变,但可能会影响其对应的逻辑形式的改变。如下代码:
#include"linkc.h"
struct pa
{
type a;
link* next;
};
void arr(link* p)
{
link* pp;
pp = (link*)malloc(sizeof(link));
pp->a = 2;
pp->next = 0;
p = pp;
//只是单纯的地址赋值操作,没有出了函数栈直接销毁pp结点,所有p空间数据没有被改变
}
void ar(link* p)
{
p->a = 5;
p->next = 0;
}
type main()
{
link* p;
p = (link*)malloc(sizeof(link));
p->a = 10;
p->next = 0;
printf("%d ", p->a);
arr(p);
//因为只是在函数内部进行的,用指针指向只是将其地址赋值,出了函数等于没影响
//若非要运用函数结点赋值改变后,要用return p形式
printf("%d ", p->a);//原样输出
ar(p);
//这是在函数内部中用地址改变其中的变量,数值会改变
printf("%d ", p->a);//将会改变其值
return 0;
}
二,线性表的算法设置
经过上一篇文章我们已经初步学习了线性表,线性表中的顺序表的算法设置形式与前面的基本形同,因为它没有复杂的逻辑结构,只需注意代码环节即可;链式表是通过链式地址来进行空间逻辑指向的,具有一定的复杂性,在运用链式表时,切记不要忽略了地址的逻辑管束。下面我通过一道合并链表,并将其有序排列的算法进行详解。
link* merge(link* a, link* b)
{
link* p, * pp, * head, * c, * d = 0;
type x;
int n = 0;
for (p = a; p->next; p = p->next)//将a结构排列
{
for (c = p->next; c->next; c = c->next)
if (p->a > c->a)
{
x = p->a;
p->a = c->a;
c->a = x;
}
}
for (p = b; p->next; p = p->next)//将b结构排列
{
for (c = p->next; c->next; c = c->next)
if (p->a > c->a)
{
x = p->a;
p->a = c->a;
c->a = x;
}
}
//head和c的地址是一样的,即其中一个的空间数据改变另一个也随之改变
head = c = (link*)malloc(sizeof(link));
while (a->next > 0 && b->next > 0)
{
if (a->a < b->a)
{
/*若用c=a,这就不属于"地址——数据"的运用,属于"地址——地址"的运用,只是单纯赋值,若出了函数体将会还原成"本样"*/
c->a = a->a;//若要进行结点赋予,将会改变地址,到时,将会出现结构混乱
a = a->next;
}
else
{
c->a = b->a;//若要进行结点赋予,将会改变地址
b = b->next;
}
d = c;
c = (link*)malloc(sizeof(link));
d->next = c;
}//因为要使head与c的头结点相同,所以不能用结点赋值
while (a->next)
{
d->next = a;
d = a;
a = a->next;
}
while (b->next)
{
d->next = b;
d = b;
b = b->next;
}
return head;//返回一个有序的链式结构的头结点
}//算法思想,先将其有序排列,然后将设置一个链表,将其融合排列
综上,当我们运用链式结构时,务必要慎重用地址赋值,地址的赋值在有些情况会影响结构的连接,这将会导致有些很难发现的错误,所有,当我们在以后的编写程序中发现了奇怪的代码,务必先考虑结点的直接赋值和前后的逻辑连接,两者之间存在直接的影响
三,线性表的灵活使用
但运用线性表的顺序表时,因为顺序表没有设计地址的操作,所以顺序表的使用是没有深入的知识,但对应链式结构,我需补充一下,既然在算法结构中无法用"地址——地址"的逻辑操作改变进行主函数的逻辑操作改变,那么这是我们就需要用返回结点的函数操作,直接返回我们引用的逻辑操作,不能用void的形式。若只要单纯的进行链式的逻辑结构的改变,不需要引用新结点,就只需要用void的形式即可,但也有特殊情况,具体情况还需具体分析。
接下来,我给大家介绍几种运用链式结构的常见算法操作,用实际操作来跟大家展示:
(1)链式结构的逆置运算
逆置运算,我们需要将头结点变成尾结点,尾结点变成头结点,即将其中的连接逆置即可,这显然用返回类型的操作更为容易。先逻辑改变,然后直接返回尾结点。
link* reverse(link* head)
{
link* p, * pp, * r;
p = head;
pp = p->next1;
p->next1 = 0;
while (pp->next1)//注意:当指向最后一个终止结点时的pp->next1才会停止
{
r = pp->next1;//向前开辟区域
pp->next1 = p;//指向后面的结点
p = pp;//后结点向前前进
pp = r;//前结点向前前进
}
//head = p;//不可这样使用,因为出了函数域将会回复正常,除非用地址内的内容
return p;
}
(2)双向链表的删除
在双向链表中,next1是指向前项,next2是指向后项。在设置算法时其实也很简单,只需将逻辑上的连接改变即可,显然不需要新结点的引入,也不需要进行"地址——地址"的操作,所以用void型将其逻辑上改变即可。
void deletelink(int i, link* head)//删除第i个数据
{
int n = 0;
link* p = head, * pp = 0;
for (n = 1; n != i; n++)//找到要删除的第i个双向链表
{
pp = p;
p = p->next1;//这里我们使用正向
}
pp->next1 = p->next1;
p->next1->next2 = pp;
free(p);
}
对于查找,添加等基本操作都与之类似,在这里不做过多解释。
(3)乱序多项式的合并运算
首先,先说明一下,m代表系数,n代表指数。在这里,我们使用不建立新结点的使用方法具体过程如下所示:
link* calculate(link* a, link* b)//不建立新节点的调用
{
link* p, * pp, * pb = b, * pc = a;
type x;
int n = 0;
//先将两个链表中的指数排序
for (p = a; p->next; p = p->next)
{
for(pp = p->next; pp->next; pp = pp->next)
if (pp->n < p->n)
{
x = pp->n;
pp->n = p->n;
p->n = x;
}
}
for (p = b; p->next; p = p->next)
{
for (pp = p->next; pp->next; pp = pp->next)
if (pp->n < p->n)
{
x = pp->n;
pp->n = p->n;
p->n = x;
}
}
//排序完之后合并系数项,同类相合并放入b链表中
p = a;
pp = b;
while (p->next)
{
if (p->n == pp->n)//当指数相同系数相加,然后继续往后查找
{
pp->m = pp->m + p->m;
p = p->next;
pp = pb = b;
continue;
}
else//当系数不同时要考虑项的合并操作
{
if (p->n < pp->n)
{
if (pp == b)
{
pc = p->next;
p->next = pp;
b = p;
p = pc;
pp = pb = b;
continue;
}
else
{
pc = p->next;
pb->next = p;
p->next = pp;
pp = pb = b;
p = pc;
continue;
}
}
if (p->n > pp->n && pp->next->next == 0)
{
pp->next = p;
break;
}
}
pb = pp;
pp = pp->next;
}
pp = p = b;
//合并成功后消除系数为0的项
while (p->next)
{
if (p->m == 0 && p == b)
{
b = p->next;
free(p);
p = pp = b;
continue;
}
if (p->m == 0 && p != b)
{
pp->next = p->next;
free(p);
p = pp->next;
continue;
}
pp = p;
p = p->next;
}
return b;
//若用void形式,因为第一项是进行赋值,出了函数域将会失效,所以会出错
//下面是运用void形式的打印,与在主程序中的输出效果有时会不一样/*for (p = b; p->next; p = p->next)
printf("%d ", p->m);
puts("");
for (p = b; p->next; p = p->next)
printf("%d ", p->n);
puts("");*/}
总:线性结构其实还分为很多类型,在以后的学习中我们会遇到许多的更为强大的线性结构,但无论是什么样的线性结构,若不涉及地址运算其复杂性与顺序表类似,若设计地址操作其复杂性与链式结构类似,只要是线性结构,我们都可参考这两种基础的类型进行深入的学习。