线性表类型的实现——链式映象

线性表类型的实现——链式映象

单链表

  • 用一组地址任意的存储单元存放线性表中的数据元素
  • 元素(数据元素的映象)+ 指针(指示后继元素存储位置的)= 结点(表示数据元素)
    结点的序列表示线性表——称作链表
    以线性表的第一个数据元素
    以线性表的第一个数据元素 a 1 a_1 a1的存储地址作为线性表的地址,称作线性表的头指针
    头节点:数据元素为空,指向第一个数据元素。
    头指针指向头节点,若为空表,则头节点的指针域为空

结点和链表的C语言描述

Typedef struct LNode{
  ElemType    data; //数据域
  struct Lnode    *next;  //指针域
}LNode,*LinkList

整个链表可以用指向头节点的指针来表示。

单链表操作的实现

  • 线性表的操作GetElem(L,i,&e)在链表中的实现
    基本操作:使指针p始终指向线性表中的第j个元素
Status GetElem_L(LinkList L,int pos, ElemType &e){
  p = L->next; j = 1; //初始化,p指向第一个结点,j为计数器
  while(p && j<pos){p = p->next; ++j}  //顺指针向后查找,知道p指向第pos个元素或p为空
  if(!p||j>pos)
    return ERROR;  //第pos个元素不存在
  e = p->data;   //取第pos个元素
  return OK;
}//GetElem_L

算法的时间复杂度为:O(ListLength(L))

  • 线性表的操作ListInsert(&L,i,e)的实现:
    基本操作为:找到线性表中第 i − 1 i-1 i1的结点,修改其指向后继的指针
    有序对 < a i − 1 , a i > 改 变 为 < a i − 1 , c > 和 < c , a i > <a_{i-1},a_i>改变为<a_{i-1},c>和<c,a_i> <ai1,ai><ai1,c><c,ai>
Status ListInsert_L(LinkList L,int pos, ElemType e){
  p = L; j = 0;  //有可能插入位置是第一个元素,所以j=0
  while(p&&j<pos-1)
    {p = p_next;  ++j;} //寻找第pos-1个节点
  if(!p || j>pos-1)
    return ERROR;  //pos小于1或者大于表长
  s = (LinkList)molloc(sizeof(LNode));  //生成新结点
  s->data = e; s->next = p->next;  //插入L中
  p->next = s;
  return OK;
}//LinkInsert_L

算法的时间复杂度:O(ListLength(L))

  • 线性表的操作ListDelete(&L,i,&e)在链表中的实现:
    基本操作为:找到线性表中第 i − 1 i-1 i1个结点,修改其后继指针
    有序对 < a i − 1 , a i > <a_{i-1},a_i> <ai1,ai> < a i , a i + 1 > <a_i,a_{i+1}> <ai,ai+1>改变为 < a i − 1 , a i + 1 > <a_{i-1},a_{i+1}> <ai1,ai+1>
Status ListDelete_L(LinkList L, int pos, ElemType &e){
  p = L; j = 0;
  while(p->next&&j<pos-1)  
    {p = p->next; ++j;}  //寻找第pos个结点,并令p指向其前驱
  if(!(p->next) || j>pos-1)
    return ERROR;  //删除位置不合理
  q = p->next; p->next = q->next;  //删除并释放节点
  e = q->data; free(q);
  return OK;
}//ListDelete_L

算法的时间复杂度为O(ListLength(L))

链表的创建

  • 策略一:每次插入到表头
  • 策略二:每次插入到表尾
void CreateList_L(LinkList &L, int n){
  //策略一
  L = (LinkList)malloc(sizeof(LNode));
  L->next = NULL;  //先建立一个带头结点的单链表
  for(i=n; i>0; --i){
    p = (LinkList)malloc(sizeof(LNode));
    scanf(&p->data); //输入元素值
    p->next = L->next;  L->next = p;  //插入到表头
  }
}//CreateList_L

算法的时间复杂度为O(ListLength(L))

链表改进

用上述定义的单链表实现线性表的操作时,存在的问题

  1. 单链表的表长是一个隐含的值
  2. 在单链表的最后一个元素最后插入元素时,需遍历整个链表;
  3. 在链表中,元素的“位序”概念淡化,结点的“位置”概念强化

改进链表的设置:

  1. 增加“表长”,“表尾指针”,“当前位置指针”三个数据域
  2. 将基本操作中的位序改为指针

一个带头节点的线性链表类型

Typedef struct LNode{  //结点类型
  ElemType    data; //数据域
  struct Lnode    *next;  //指针域
}*Link,*Position

Status MakeNode(Link &p, ElemType e);
  //分配由p指向的值为e的结点,并返回OK;
  //若分配失败,则返回ERROR
void FreeNode(Link &p);
  //释放p所指结点
typedef struct{ //链表类型
  Link head,tail; //指向头节点和最后一个结点
  int len; //指示链表长度
  Link current;
  //指向当前访问的结点的指针
  //初始位置指向头节点
}LinkList

表长变为显值

链表的基本操作
  • 结构初始化和销毁结构
Status InitList(LinkList &L);
  //构造一个空的线性表L
  //头指针,尾指针和当前指针均指向头节点,表长为0
Status DestroyList(LinkList &L);
  //销毁线性链表L,L不在存在
  • 引用型操作
    Status ListEmpty(LinkList L) //判表空;
    int ListLength(LinkList L); //求表长 (时间复杂度为常量)
    Status Prior(LinkList L); //改变当前指针指向其前驱(时间复杂度和表长成正比)
    Status Next(LinkList L); //改变当前指针指向其后继(时间复杂度为常量)
    ElemType GetCurElem(LinkList L); //返回当前指针所指数据元素(时间复杂度为常量)
    Status LocatePos(LinkList L, int i);//改变当前指针指向第i个结点 (时间复杂度和表长成正比)
    Status LocateElem(LinkList L,ElemType e, Status(*compare)(ElemType,ElemType));
    //若存在与e满足函数compare()判定关系的元素,则移动当前指针指向第一个满足条件的元素,并返回OK,否则返回ERROR (时间复杂度和表长成正比)
    Status ListTraverse(LinkList L, Status(*visit()));//依次对L的每个元素调用visit() (时间复杂度和表长成正比)

  • 加工型操作

Status ClearList(LinkList &L);  //重置为空表
Status SetCurElem(LinkList &L,ElemType e); //更新当前指针所指数据元素
Status Append(LinkList &L,Link s);  //一串结点链接在最后一个节点之后,时间复杂度为常量
Status InsAfter(LinkList &L,ElemType e); //将元素插入在当前指针之后,时间复杂度为常量
Status DelAfter(LinkList &L,ElemType *e); //删除当前指针之后的结点 ,时间复杂度为常量

InsAfter(LinkList &L,ElemType e);的实现:

Status InsAfter(LinkList &L,ElemType e){
  //若当前指针在链表中,则将数据元素e插入在线性链表L中
  //当前指针所指结点之后,并返回OK,否则返回ERROR
  if(!L.current) return ERROR;
  if(!MakeNode(s,e)) return ERROR;
  s->next = L.current->next;
  L.current->next = s;
  return OK;
}//InsAfter

DelAfter(LinkList &L,ElemType *e);的实现:

Status DelAfter(LinkList &L, ElemType &e){
  //若当前指针及其后继在链表中,则删除线性链表L中
  //当前指针所指结点之后的结点,并返回OK,否则返回ERROR
  if(!(L.current&&L.current->next))
    return ERROR;
  q = L.current->next;
  L.current->next = q->next;
  e = q->data;  FreeNode(q);
  return OK;
}DelAfter
利用线性表的基本操作完成其他操作
  • 例1

在带头结点的单链表线性表L的第i个元素之前插入元素e

Status ListInsert_L(LinkList, int i,ElemType e){
  //在带头结点的单链表线性表L的第i个元素之前插入元素e
  if(!LocatePos(L,i-1))  return ERROR; //i值不合法
  if(InsAfter(L,e))  return OK; //插入在第i-1个结点之后
  else return ERROR;
}//ListInsert_L
  • 例2

归并两个“其数据元素按值非递减有序排列(关键字递增序排列,但并非是单调递增)的”线性表LA和LB,求得线性表LC也具有同样特性

void MergeList_L(LinkList &La,LinkList  &Lb, LinkList &Lc,
		  int(*compare)(ElemType,ElemType)){
		  //时间复杂度与两个表长之和成正比
  if(!InitList(Lc))  return ERROR;  //存储空间分配失败
  LocatePos(La,0); LocatePos(Lb.0);  //当前指针指向头节点
  ifDelAfter(La,e))  a = e;
  else a = MAXC; //MAXC为常量最大值
  if(DelAfter(Lb,e))  b = e;
  else b = MAXC;
  while(!(a=MAXC && b = MAXC)){  //La或Lb非空
    if((*compare)(a,b)<=0){ //a<=b
      InsAfter(Lc,a);
      if(DelAfter(La,el)) a = el;
      else a = MAXC;
    }
    else{ //a>b
      InsAfter(Lc,b);
      if(DelAfter(Lb,el))  b = el;
      else b = MAXC;
    }
  }
  DestroyList(La);  DestroyList(Lb);  //销毁链表La和Lb
  return OK;
}//MergeList_L

其他形式链表

双向链表
  • 定义:
//线性表的双向链表存储结构
typedef struct DulNode{
  ElemType  data; //数据域
  struct DulNode *prior; //指向前驱的指针域
  struct DulNode *next;  //指向后继的指针域
}DulNode, *DuLinkList;
  • 特点:

头指针的前驱和后继为空
对每个当前指针指向的元素来说,其后继的前驱和前驱的后继是当前节点

循环链表

最后一个元素的指针域指向头节点
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值