线性表的源码分析

线性表

在我们刚开始学习数据结构的过程中,往往是从数据结构的历史开始学起,然后是什么是算法,算法的特性等,接下来便是复杂度的分析。
因为写这个博客我的初衷是对每章的源码进行剖析,以便于自己能够更好的学习这门课,掌握其思想。在这里笔者只希望大家不要去死学数据结构。数据结构是一切算法的思想,它本身就非常有趣,当然也可以学的很开心。


顺序表


我来先讲一下顺序表的思想。
咱们举个例子来阐述,中学的时候大家应该都会做广播体操的吧,在做广播体操之前,我们往往会先排队,你的前面是谁,后面是谁,一般都是会固定好。而且数量短时间内是不会变得。等老师喊多少号多少号同学出列,很明显很好查找。好了,你把顺序表想成数组就行。
下面我们来看代码

/*顺序表的初始化*/
#define MAXSIZE 60/*数组元素的最大个数为60*/
typedef struct {
int data;/*数组,看你数组中存放什么类型的数据,此处也可以不是整型*/
int length;/*顺序表的长度*/
}L;

插入的图形解释
在这里插入图片描述

/*顺序表的插入*/
L insert(L *H,int i,int z)/*在位置为i的地方出入值为z的元素*/
/*此时注意,顺序表位置i是从1开始的,而数组的位置则是从0开始的,所以e要减1*/
{
    if(H->length==MAXSIZE){
    reurn error;
    }
    if(i-1<0||i>H->length+1)
    return error;/*判断插入的位置是不是顺序表里的位置*/
    for(int a=length-1;a>=i-1;a--){/*从顺序表的末尾开始移动,直到第i-1个元素*/
            H->data[a+1]=H->data[a];       
    }
    H->data[i-1]=z;/*第i个元素的值为z*/
    H->length++;/*顺序表的长度加1*/
    return H;/*返回顺序表*/
}

删除的图形解释
在这里插入图片描述

/*顺序表的删除*/
L Delete(L *H,int i,int &z){/*删除位置为i的元素*/
    if(i-1<0||i>H->length+1||H->length==0)/*判断顺序表中是否有i的位置和这个顺序表是不是空表*/
    return error;
    z=H->data[i-1];/*把要删除的元素赋值给z*/
    for(int a=i;a<=length-1;a++){
    H->data[i-1]=H->data[i];/*数组下标从i开始,直到末尾,每个元素向前移一格*/
    }
    H->length--;/*顺序表的长度减1*/
    return H;
}

小总结
我们从代码中可以看到,顺序表的查找效率是非常高的,时间复杂度为O(1),但是顺序表的插入和删除操作却是非常的繁琐。在插入时,最好的情况肯定是插入在表尾,时间复杂度为O(1)但是平均的时间复杂度为O(n),因为我们从源码中可以看出,每次插入一个元素,所属位置以及他后面的元素都要向后移动一个元素。删除操作和插入操作相似。
所以如果我们要如何解决插入和删除过于复杂的问题呢。前人早已经帮我们想好了,就是用链式存储。


线性表的链式存储


我们该如何理解链式存储的思想呢。老规矩,先上例子(虽然我的例子并不是那么通俗易懂,哈哈哈,理科生,稍微理解一下),大家应该都有过去银行办理业务的经历吧。去银行时,银行为了保证效率,通常会采用取号的方法。这样取到号码的人根本不用去排在一起,这就和链表的思想相一致,存储空间没必要一定连续,上一个记录下一个的位置就好。

/*单链表的初始化*/
typedef struct LNode{
int data;/*数据域,和顺序表的数组类型一样,此处不一定是int,我只是为了方便*/
struct LNode *next;/*指针域,用于指向下个节点的地址*/
}LNode,*Linklist;

使用头插法建立单链表图解
在这里插入图片描述

/*头插法建立单链表*/
Linklist HeadBuild(Linklist L){
    L=(Linklist)malloc(sizeof(LNode));/*赋空间给头指针=头节点*/
    LNode *s;/*使用指针变量s来指向一个新的节点*/
    L->next=NULL;/*头节点的指针域为NULL*/
    int x;
    scanf("%d",&x);
    while(x!9999){/*此时不知道链表的长度,所以不能用for语句*/
    s=(LNode*)malloc(sizeof(sizeof(LNode));/*赋空间给指针s=新节点*/
    s->data=x;
    s->next=L->next;/*新节点指向原来的第一个节点,如图第三步所示*/
    L->next=s;/*把新节点作为第一个节点*/
    scanf("%d",&x);
    }
    return L;
}

根据代码我们可以看出,头插法每次都把一个新生成的节点当作链表的第一个节点,所以当你输出每个节点的数据域的值是,顺序是逆序的。

尾插法图示
在这里插入图片描述

/*尾插法建立单链表*/
Linklist TailBuild(Linklist L){
           L=(Linklist)malloc(sizeof(LNode));/*给头节点赋空间*/
           LNode *r=L;/*声明指向表尾的指针*/
           int x;
           LNode *s;/*声明指向新节点的指针*/
           scanf("%d",&x);
           while(x!=9999){
           s=(LNode*)malloc(sizeof(LNode));
           s->data=x;
           r->next=s;/*新节点等于尾节点的下一个节点*/
           r=s;/*尾节点指向新的尾节点*/
           scanf("%d",&x);
           }
           r->next=NULL;/*尾节点的next域设为null*/
           return L;
}

这种方法创建的链表输出正常

链表插入的图示
在这里插入图片描述

/*单链表的插入*/
Linklist Insert(Linklist L,int i,int x)/*在第i个节点后面插入数据域为x的节点*/
{
           LNode *p=L;/*声明一个专门用于查找的指针,初始指向头节点*/
           LNode *s;/*声明一个指向插入节点的指针*/
           s=(LNode *)malloc(sizeof(LNode));/*给插入节点赋空间*/
           s->data=x;/*给准备插入的节点的数据域为x*/
           int z=0;
           while(z<=i){
           p=p->next;/*使p指向第i个节点*/
           z++;
           }
           s->next=p->next;/*s指向p原来的下一个节点*/
           p->next=s;/*p的下一个节点指向s*/
           return L;
}

**

注意,最后的两个顺序是不可以换的,因为你如果换了一个序,原来的p->next就没了

**
线性表的删除
在这里插入图片描述

/*线性表的删除*/
/*这里我们只写关键的一步*/
/*这里我们把要删除的节点叫s,s的前驱节点为p*/
Listlink q;
q=s;
p->next=s->next;
free(q);

小总结:以上是关于单链表的插入,删除以及查找。从代码中我们可以看出,单链表的查找没有顺序表来的方便,顺序表查找的时间复杂度为O(1),而单链表的查找的时间复杂度为O(n).但是插入和删除操作单链表的时间复杂度是高于顺序表的。当我们需要插入和删除的操作多时选用链表,查找的操作多时选择顺序表。

下面我们来讲另一种链表,双链表
如果说单链表的某个节点只知道他的后继的话,那么双链表里的节点既知道他的前驱又知道他的后继。
话不多说先上图,再上代码

/*双链表的初始化*/
typedef struct {
     int data;/*双链表节点的数据域*/
     struct LNode *prior;/*前驱域,用来指向这个节点的前驱节点*/
     struct LNode *next; /*后继域,用来指向这个节点的下一个节点*/
}LNode,*Linklist;

双链表的建立
图示
在这里插入图片描述

/*双链表的建立*/
Linklist Doublebuild(Linklist L){
   int x;
   L=(Linklist)malloc(sizeof(LNode));/*给头节点赋空间,也就是创建头节点*/
   L->next=NULL;/*头节点的next域为NULL*/
   L->prior=NULL;/*头节点前驱域为NULL*/
   Linklist r=L;/*声明一个指向表尾的节点*/
   LNode *s;/*声明一个新生成的节点*/
   scanf("%d",&x);
   while(x!=9999){/*利用这个循环来不断创建新的节点*/
   s=(LNode*)malloc(sizeof(LNode));/*创建新节点*/
   s->data=x;/*给新节点的数据域赋值*/
   r->next=s;/*表尾节点的next域指向新的节点*/
   s->prior=r;/*新的节点的前驱节点域指向表尾的节点*/
   r=s;/*让r指向新的表尾节点*/
   scanf("%d",&x);
   
   }
   r->next=NULL;/*把最后表尾节点的next域指向为NULL*/
}

双链表的插入
图示
在这里插入图片描述

/*双链表的插入操作*/
p->next->prior=s;/*把p节点原来的后继节点的前驱节点域指向新插入的节点*/
s->next=p->next;/*把新插入节点的后继域指向原来p节点的下一个节点*/
s->prior=p;/*把新插入节点的前驱域指向p*/
p->next=s;/*p的后继节点域指向s*/

双链表的删除操作
在这里插入图片描述

/*双链表的删除操作*/
p->prior->next=p->next;/*把p的前驱节点的next域指向p原来的下一个节点*/
p->next->prior=p->prior;/*把p下一个节点的前驱域指向p原来的前一个节点*/
free(p);

循环单链表和双链表就简单的说下,就是把最后一个节点和头节点连起来的,就把原来的NULL改成头节点就行

如果各位对源码还有不了解的欢迎评论区留言。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值