线性表的详解与深入

简介: 线性表的详解与深入

一,链式结构的地址形式

       首先,要说明的是,在线性结构中的链式结构的结点都是地址,而在逻辑连接中也都是通过地址相连,即指针。在建立结点时,我们可以回顾一下动态数组的原理,而链式结构中的结点与动态数组的逻辑形式相同,在内存的堆区开辟一块地址空间,在运用结点的运算时,也就相当于调运指针的形式运算。

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("");*/

}

总:线性结构其实还分为很多类型,在以后的学习中我们会遇到许多的更为强大的线性结构,但无论是什么样的线性结构,若不涉及地址运算其复杂性与顺序表类似,若设计地址操作其复杂性与链式结构类似,只要是线性结构,我们都可参考这两种基础的类型进行深入的学习。

相关文章
|
存储
线性表的链式存储结构
线性表的链式存储结构
|
存储 缓存
线性表
从以上就很容易看出,在缓存利用率方面,顺序表由于内存是连续的,而链表是一个个单个的节点连起来的,顺序表的命中率绝对要比链表高不少,但是链表又要比顺序表要灵活不少,到这里我相信你就能理解为什么说所以这两种结构是优势互补的关系了,具体使用哪种结构,当然还是要根据具体场景去抉择了,好了这篇博客到这里就结束了,多多点赞支持哦!!
线性表
|
存储 算法 C++
线性表和链表
线性表和链表
119 0
|
存储 人工智能 DataX
线性表的顺序存储实现
线性表的顺序存储实现
|
存储
线性表的链式存储——链表
线性表的链式存储——链表
204 0
|
存储 C++
线性表的顺序存储——顺序表
线性表的顺序存储——顺序表
176 2
线性表的顺序存储——顺序表
|
存储 机器学习/深度学习 人工智能
浅谈线性表
数据结构线性表、栈与队列的区别0
107 0
浅谈线性表
|
存储
线性表之顺序表
线性表之顺序表
106 0
线性表之顺序表
|
存储 C++
C++实现线性表 - 02 单向链表
今天我们来动手实现一下链表结构,链表在我们后续的数据结构中用的十分频繁,可以说就是实现后续很多数据结构一个的基本工具,也是最容易的数据结构之一,我们先从最基础的单向链表讲起,小白刚开始学习肯定会被折磨的头疼,我也是这样的,但只要啃下这块硬骨头就已经前进一大步了!
180 0
C++实现线性表 - 02 单向链表