2.3.1笔记-线性表的链式表示和实现

本文详细介绍了链表的实现原理及基本操作,包括初始化、插入、遍历等,并提供了具体的C语言实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

和顺序表相比,链表存储结构在实现插入、删除的操作时,不需要移动大量数据元素(但不容易实现随机存取线性表的第i个数据元素的操作)。所以,链表适用于经常需要进行插入和删除操作的线性表,如飞机航班的乘客表等。

线性链表中单链表的结构(教材P28)

struct LNode{//线性表的单链表结构
    ElemType data;
    LNode *next;
};

下面程序中用到的单链表是带有头结点的单链表(教材P28)
程序包含了带有头结点的单链表的基本操作,这些操作包括算法2.8~2.10,这几种操作都是按照main函数中出现的次序排列的。
附注:为了方便测试,所有的函数都放到了一个程序里。12个基本操作,其他的函数都是这些操作中用到的小函数,很简单不做过多记录。
1. InitList(LinkList &L) 构造一个空的线性表
2. ListInsert(LinkList &L,int i,ElemType e) 算法2.9 在带头结点的单链线性表L中第i个位置之前插入元素e
3. ListTraverse(LinkList L,void (*visit)(ElemType )) 依次对L的每个数据元素调用函数visit(),visit()是模板函数,具体由main函数中指定
4. ListEmpty(LinkList L) 若L为空表,则返回TRUE,否则返回FALSE
5. ListLength(LinkList L) 返回L中数据元素的个数
6. DestroyList(LinkList &L) 销毁线性表L
7. ClearList(LinkList L) 将L重置为空表(只留下头指针和头结点) 线性表L的结构:L+头结点+头结点指向的单链表
8. LocateElem(LinkList L,ElemType e, Status(* compare)(ElemType,ElemType)) 返回L中第1个与e满足关系compare()的数据元素的位序,若这样的数据元素不存在,则返回值为0 compare()是数据元素判定函数(满足为1,否则为0)
9. GetElem(LinkList L,int i,ElemType &e) 算法2.8 L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回ok,否则返回ERROR
10. PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e) 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回ok,否则操作失败,pre_ew无定义,返回ERROR
11. NextElem(LinkList L,ElemType cur_e,ElemType &next_e) 若cur_e是L的数据元素,且不是最后一个,则用next_e 返回它的后继,返回OK
12. ListDelete(LinkList L,int i,ElemType &e) 算法2.10 不改变L 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值

#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;

struct LNode{//线性表的单链表结构
    ElemType data;
    LNode *next;
};
typedef LNode *LinkList;
void InitList(LinkList &L){
    //操作结果:构造一个空的线性表
    L=(LinkList)malloc(sizeof(LNode));
    if(!L)//存储分配失败
        exit(OVERFLOW);
    L->next=NULL;//头结点的指针域为空
}
Status ListInsert(LinkList &L,int i,ElemType e){//算法2.9 (教材P29)不改变L
    //在带头结点的单链线性表L中第i个位置之前插入元素e
    int j=0;
    LinkList s,p=L;//p指向头结点
    while(p&&j<i-1){
    j++;//计数器+1
    p=p->next;//p指向下个结点
    }
    if(!p||j>i-1)
        return ERROR;
    s=(LinkList)malloc(sizeof(LNode));//生成新结点,以下将其插入L中
    s->data=e;//将e赋给新结点
    s->next=p->next;//新结点指向元第i个结点
    p->next=s;//原第i-1个结点指向新结点
    return OK;//插入成功
}
void print(ElemType e){
printf("%d ",e);
}
void ListTraverse(LinkList L,void (*visit)(ElemType )){
    //初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数visit()
    LinkList p=L->next;
    while(p)//p所指结点存在
    {
        visit(p->data);//对p所指结点调用函数visit()
        p=p->next;//p指向下一个结点
    }
    printf("\n");
}
Status ListEmpty(LinkList L){
    //初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
    if(L->next)
        return FALSE;
    else
        return TRUE;
}

int ListLength(LinkList L){
    //初始条件:线性表L已存在。操作结果;返回L中数据元素的个数
LinkList p;
int i=0;//计数器初值为0
p=L->next;//p指向第1个结点
while(p){//未到表尾
    i++;//计数器+1
    p=p->next;//p指向下个结点
}
return i;
}
void DestroyList(LinkList &L){
//初始条件:线性表L已存在。操作结果:销毁线性表L
    LinkList q;
    while(L){//L指向的结点(非空)
        q=L->next;//q指向现在线性表的首元结点
        free(L);//释放现在线性表的头结点
        L=q;//L指向现头结点
    }
}
void ClearList(LinkList L){
    //初始条件:线性表L已存在。操作结果:将L重置为空表(只留下头指针和头结点)
    //线性表L的结构;  L+头结点+头结点指向的单链表
    LinkList p=L->next;//p指向第1个结点
    L->next=NULL;//将头结点指针域为空,断开头结点和头结点指向的单链表
    DestroyList(p);//销毁p所指的单链表

}
int LocateElem(LinkList L,ElemType e, Status(* compare)(ElemType,ElemType)){
//初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
//操作结果:返回L中第1个与e满足关系compare()的数据元素的位序
//若这样的数据元素不存在,则返回值为0
int i=0;
LinkList p=L->next;//p指向第1个结点
while(p)//未到表尾
 {
     i++;
     if(compare(p->data,e))//找到这样的数据元素
         return i;
     p=p->next;//p指向下一个结点
 }
    return 0;//满足关系的数据元素不存在
}
Status equal(ElemType c1,ElemType c2){
    //判断是否相等的函数
    if(c1==c2)
        return TRUE;
    else
        return FALSE;
}
Status GetElem(LinkList L,int i,ElemType &e){//算法2.8
    //L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回ok,否则返回ERROR
    int j=1;//计数器初值为1
    LinkList p=L->next;//p指向第1个结点
    while(p&&j<i)
     {
       j++;
       p=p->next;//p指向下一个结点
    }
    if(!p||j>i)
        return ERROR;
    e=p->data;//取第i个元素的值赋给e
    return OK;
}
Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e){
    //初始条件:线性表L已存在
    //操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回ok,否则操作失败,pre_ew无定义,返回ERROR
    LinkList q,p=L->next;
    while(p->next){//p所指结点有后继
        q=p->next;
        if(q->data==cur_e)//p的后继为cur_e
        {
            pre_e=p->data;//将p所指元素的值赋给pre_e
            return OK;//
        }
        p=q; //p的后继不为cur_e,p向后移
    }
    return ERROR;
}
Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e){
  //初始条件:线性表L已存在
 //操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e 返回它的后继,返回OK
    LinkList p=L->next;//p指向第一个结点
    while(p->next) //p所指结点有后继
     {
         if(p->data==cur_e)//p所指结点的值为cur_e
           {
               next_e=p->next->data;
               return OK;   
         }
         p=p->next;//p指向下个结点
      } 
    return ERROR;
}
Status ListDelete(LinkList L,int i,ElemType &e){//算法2.10 不改变L
//在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
  int j=0;//计数器初值为0
  LinkList q,p=L;//p指向头结点
  while(p->next && j<i-1){
     j++;
     p=p->next;//p指向下一个结点
  }
  if(!p->next || j>i-1)//删除位置不合理
      return ERROR;
  q=p->next;
  p->next=q->next;//待删除结点的前驱指向待删结点的后继
  e=q->data;//将待删结点的值赋给e
  free(q);//释放待删结点
  return OK;//删除成功
}
void main(){
    LinkList L;//首先定义指针L
    ElemType e,e0;
    Status i;
    int j,k;
    InitList(L);//初始化线性表
    for(j=1;j<=5;j++)
        i=ListInsert(L,1,j);//从L的表头插入j
    printf("在L的表头依次插入1~5后,L=");
    ListTraverse(L,print);//依次对元素调用print(),输出元素的值
    i=ListEmpty(L);//检测表L是否为空
    printf("L是否为空?i=%d(1:是 0:否),表L的长度=%d\n",i,ListLength(L));
    ClearList(L);//清空表L
    printf("清空L后,L=");
    ListTraverse(L,print);
    i=ListEmpty(L);//检测表L是否为空
    printf("L是否为空?i=%d(1:是 0:否),表L的长度=%d\n",i,ListLength(L));
    for(j=1;j<=10;j++)
        ListInsert(L,j,j);//在L的表尾插入j
    printf("在L的表尾依次插入1~10后,L= ");
    ListTraverse(L,print);//依次输出表L中的元素
    for(j=0;j<=1;j++)
        k=LocateElem(L,j,equal);//查找表L中与j相等的元素,并将其在链表中的排序赋给k
        if(k) //k不为0,表明有符合条件的元素
           printf("第%d个元素的值为%d\n",k,j);
    for(j=1;j<=2;j++)//测试头2个数据
    {
        GetElem(L,j,e0);//把表L中的第j个数据赋给e0
        i=PriorElem(L,e0,e);//把表L中的第j个数据赋给e0
        if(i==ERROR)
            printf("元素%d无前驱\n",e0);
        else
            printf("元素%d的前驱为%d\n ",e0,e);
    }
    for(j=ListLength(L)-1;j<=ListLength(L);j++)  //最后2个数据
      {
         GetElem(L,j,e0);//把表L中的第j个数据赋给e0
         i=NextElem(L,e0,e);//求e0的后继,如成功,将值赋给e
         if(i==ERROR)
             printf("元素%d无后继\n",e0);
         else
             printf("元素%d有后继%d\n",e0,e);
       } 
    k=ListLength(L);//k为表长
    for(j=k+1;j>=k;j--)
    {
      i=ListDelete(L,j,e);//删除第j个数据
      if(i==ERROR)//表中不存在第j个数据
          printf("删除第%d个元素失败(不存在此元素)。",j);
      else //表中存在第j个数据,删除成功,其值赋给e
          printf("删除第%d个元素成功,其值为%d\n",j,e);
    }
    printf("依次输出L的元素:");
    ListTraverse(L,print);//依次输出表L中的元素
    DestroyList(L);//销毁表L
    printf("销毁L后,L=%u\n",L);
}

运行截图:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值