数据结构复习2:线性表的链式存储结构

复习数据结构线性表的基本操作

一、单链表的定义和表示

线性表链式存储结构的特点:
用一组任意的存储单元存储线性表的数据元素。

结点:链表中的每个数据元素的存储影响,它包括两个域,其中存储数据元
素信息的域称为数据域,存储后继结点地址的域称为指针域。

链表:由n个结点所连接成的表。链表也称为顺序存取的数据结构

二、单链表的基本操作

(1)单链表的存储结构

typedef struct LNode
{
      ElemType data;//结点的数据域
      struct LNode *next;//结点的指针域
}LNode,*LinkList;//LinkList为指向结点的指针

(2)单链表的初始化

Status InitList(LinkList &L)
{
      L=(LNode*)malloc(sizeof(LNode));//生成一个头结点,用头指针L指向它
      L->next=NULL;//空间点的指针域为空
      return OK;
}
头结点的作用:

(1)便于对首元结点的操作(即头结点后面的那一个结点)
(2)便于空表和非空表的统一处理。即当增加了头结点后,无论该表是不是空表,头指针L都指向着一个不为空的结点。
(3)头结点中的数据域可以存放关于链表属性的值,例如表长。

(3)单链表的创建

1.前插法创建单链表

所谓前插法,就是将每一个新结点都插入到链表的最前端,也就是头结点的后继处,即原序列在前插法创建单链表中是倒序的。

在这里插入图片描述
如图所示,先来的节点接到了头结点的后面,依次重复,构成一个链表。

a.算法

①创建一个只有头结点的单链表
②根据创建链表的N个数,循环N次以下操作
· 生成一个新结点P
· 将元素值赋给P的数据域
· 将新结点连到头结点的后继,再将头结点连到新结点

b.代码实现
main()
{
      ...
      InitList(L);
      for(i = 1; !feof(fp) ; i++)
      {
         fscanf(fp,"%d",&num);//将文件中的值赋给num
         printf("%d ",num);
         CreateList_H(L,num);
      } 
}
void CreateList_H(LinkList &L,ElemType data)
{
       LinkList P;
       P=(LNode*)malloc(sizeof(LNode)); 
       P->next=L->next;//将P节点先练到头结点的后继
       L->next=P;//再将头结点连到结点P  //两步的顺序不能颠倒
       P->data=data;
}
c.算法分析

前插法的时间复杂度为O(n)

2.后插法创建单链表

所谓后插法,就是将每一个新结点都插入到链表的最尾端,也就是当前链表最后一个结点的后继处,后插法创建单链表中与原序列顺序相同。
在这里插入图片描述
如图所示,将新结点依次连接到原链表的最后一个结点的后面。由图可知,后插法需要一个辅助指针R。

a.算法

①创建一个只有头结点的单链表,创建一个辅助指针R。将R指向头结点。
②根据创建链表的N个数,循环N次以下操作
· 申请P结点的空间
· 将元素值赋给P的数据域
· 新结点P的指针域赋值为NULL
· 将R的指针域指向新结点
· 令R=P;

b.代码实现
main()
{
 for(i = 1; !feof(fp) ; i++)
 {
      fscanf(fp,"%d",&num);
      printf("%d ",num);
      //CreateList_T
      P=(LNode*)malloc(sizeof(LNode));
      P->data=num;
      P->next=NULL;
      R->next=P;
      R=P;
      /*因为我在这里写的是文件,又因为后插法需要一个辅助指针且每
      次循环都要改变辅助指针,所以我把后插法写到了main函数中,没
      想出来怎样把后插法函数单独写出来*/
 } 
c.算法分析

后插法的时间复杂度为O(n)

(4)单链表的取值

a.算法

① 让P指向首元结点,用i做计时器
② 从首元结点依次向下一个访问,只要P不为空或还没有到达第n个结点,则:
· 让P指向下一个结点
· 计时器加一
③ 若P为空或i>n,则返回ERROR;否则返回OK

b.代码实现
ElemType GetElem(LinkList L,int n)
{
     int i;
     LinkList P;
     P=L->next;
     for(i=1;i<n&&P;i++)
     P=P->next;
     if(i<1||!P) return ERROR;
     else return P->data;
} 
c.算法分析

该算法与while语句的频度有关。 当1≤i≤n时,while频度为i-1,因为判定条件是i<n.当
i>n时,此时while语句频度为n,因为此时的循环结束判定调节为P。因此该算法的最坏平均复杂度为O(n)。

平均复杂度:

设每个位置取值概率相同,p=n/1
ASL=p*Σ(1~n)(i-1)=(n-1)/2
所以该算法的平均复杂度为O(n).

(4)单链表的查找

a.算法

① 使P指向首元结点
② 从首元结点依次向下一个访问,只要P不为空或还没有查找到要查找的值,则另P指向下一个结点
③ 若返回的P值不为空,则查找成功;否则查找失败。

b.代码实现
LNode *LocateElem(LinkList L,ElemType dt)
{
     LinkList P;
     P=L->next;
     while(P&&P->data!=dt)// 疑问? : while(P->data!=dt && P)调试过不去 
     P=P->next;
     return P; 
}
c.算法分析

① 该算法的最差时间复杂度为O(n)
② 每个值被查找的概率为n/1,频度为i-1(i为查找成功时该值的位置)。
③ 平均时间复杂度:p=n/1
ASL=p*Σ(1~n)(i-1)=(n-1)/2

平均复杂度:该算法的平均时间复杂度为O(n).

(5)单链表的插入

在这里插入图片描述

a.算法

① 找到要插入的位置的前一个结点,并让P指向该结点
② 生成一个新的结点S
③ 结点的数据域放入插入值
④ S指向P的后继
⑤ 结点P 指向S

b.代码实现
Status ListInsert(LinkList &L,int n,ElemType num)
{
    int i;
    LinkList P,S;
    P=L;
    S=(LNode*)malloc(sizeof(LNode));
    S->data=num;
    for(i=0;P&&i<n-1;i++) 
        P=P->next;
    if(!P||i>n-1) return ERROR;
    S->next=P->next;
    P->next=S;
    return OK;
}
c.算法分析

时间主要花费在找插入位置前一个结点上,时间复杂度为O(n)

(5)单链表的插入

在这里插入图片描述

a.算法

① 找到要删除的结点的前驱,并用P指向他
② 用S指向P的后继
③ 用P指向S的后继
④ 释放S

b.代码实现
Status ListDelete(LinkList &L,int n)
{
    int i;
    LinkList P,S;
    P=L;
    for(i=0;(P->next)&&i<n-1;i++)
        P=P->next;
    if(!(P->next)||i>n-1) return ERROR;
    S=P->next;
    P->next=S->next;
    free(S);
    return OK;
}
c.算法分析

与插入算法相同,平均时间复杂度为O(n)

需要注意的是,删除算法中的循环条件**(P->next)&&i<n-1与插入算法中的循环条件P&&i<n-1**是有区别的,因为一个链表中能插入的位置有n+1个,如果插入算法的循环条件与删除算法的循环条件相同,那么循环只能进行n次,无法包含整个插入位置。删除同理。

“可见,线性表的链式存储结构只能顺序存取表中的元素,但链式表的存储在插入和删除操作时,不需需要移动大量的元素,操作过程较为简单,存储效率高,但相应的取值效率降低。”

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值