线性表顺序与链式存储

线性表顺序存储结构

有关线性表的讨论分为以下几点:

  • 线性表的逻辑结构
  • 存储结构
  • 相关运算

定义

由n(n>=0)个数据特性相同的元素构成的有限序列称为线性表

特别地,当n=0时,称该线性表为空表。

对于非空的线性表和线性结构,其特点是:

  • 存在唯一的一个被称作“第一个”的数据元素;
  • 存在唯一的一个被称作“最后一个”中的数据元素;
  • 除第一个之外,结构中的每个数据元素均只有一个前驱
  • 除最后一个之外,结构中的每个元素均只有一个后继
  • 线性表有限
判断是否为线型结构?
  1. 公司的组织架构?
  2. 班级同学之间的情谊?
  3. 班级里的点名册?

答案:
1.错,树结构,一对多的关系
2.错,图结构,多对多的关系
3.对,线性结构,按照学号排成线性关系

线性表的抽象数据类型

ADT 线性表(List)
Data
线型表的数据对象集合为{a1,a2,,an},每个元素的类型均为DataType。
Operation
InitList(*L):初始化操作,加你一个空的线性表L。
ListEmpty(L):判断线性表是否为空表,若线性表为空,返回true,否则返回false.
ClearList(*L):将线性表清空。
GetElem(L,i,*e):将线性表L中的第i个位置元素值返回给e。
LocateElem(L,e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号标识成功;否则返回0表示失败。
ListInsert(*L,i,e):在线性表中第i个位置插入新元素e。
ListDelete(*L,i,*e):删除线性表L中第i个位置元素,并返回e的值。
ListLength(L):返回线性表中L的元素个数。
endADT

线性表有两种物理存储结构:顺序存储结构链式存储结构

线性表的顺序存储结构

指的是用一段地址连续的存储单元一次存储线性表的数据元素。
表示顺序表存储的结构代码:

#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;

顺序存储结构的三个属性:

  • 存储空间的起始位置,数组data,它的存储位置就是线性表存储空间的存储位置。
  • 线性表的最大存储容量:数组的长度MAXSIZE/
  • 线性表的当前长度:length
地址计算方法

假设ElemType占用的是c个存储单元(字节),那么元素表中第i+1个元素和第i个元素的位置关系为:
== Loc(ai+1)=Loc(ai)+c ==
Loc(ai)=Loc(a1)+(i-1)*c

查找操作(GetElem)

typedef int Status;
Status  GetElem(SqList L,int i,ElemType *e)
{
 if(L.length==0||i<1||i>L.length){
  return 0;
 }
 *e=L.data[i-1];
 return 1;
}

插入操作(ListInsert)
思路:

  • 如果插入位置不合理,抛出异常;
  • 如果线性表长度大于等于数组长度,则抛出异常或动态增加数组容量;
  • 从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置;
  • 将要插入的元素填入位置i;
  • 线性表长度+1
typedef int Status;
Status ListInsert (SqList *L,int i,ElemType e)
{
 int k;
 if(L->length==MAXSIZE)//线性表已满 
 {
  return 0;
 }
 if(i<1||i>L->length+1)//i不在范围 
 {
  return 0;
 }
 if(i<=L->length){//若插入数据不在表尾 
  for(k=L->length-1;k>=i-1;k--)//将插入位置后的元素向后移位 
  { 
   L->data[k+1]=L->data[k];
  }
 }
 L->data[i+1]=e;//插入新元素 , 下标从1开始 
 L->length++;
 return 1;
 } 

删除操作:

#define ERROR 0
#define OK 1
typedef int Status; 
Status ListDelete(SqList *L,int i)
{
 int j;
 if (i<1||i>L.length)//删除元素位置不符 
 {
  return ERROR;
 }
 for(j=1;j<=L.length;j++)//遍历线性表 
 {
  L.elem[j-1]=L.elem[j];//将删除后的元素位置向前移位 
  --L.length;//线性表长度-1 
  return OK;
 }
} 

综上:
在存储、读取数据时,不管是哪个位置,时间复杂度都是O(1),
而在插入或删除时,时间复杂度都是O(n)。
这就说明,线性表适合元素个数较稳定,不经常插入和删除元素,更多的操作是存取数据的应用。
顺序存储结构优点

  • 无须为表示表中元素之间的逻辑关系而增加额外的存储空间。
  • 可以快速地存取表中任意位置的元素

== 缺点==:

  • 插入和删除操作需移动大量元素
  • 当线性表长度变化较大时,难以确定存储空间的容量。
顺序表的链式存储结构

特点:用一组任意的存储单元存储线性表的数据元素,这组存储单元可以存在内存中未被占用的任意位置。

除了要存储数据元素信息外,还要存储它的后继元素的存储地址(指针)。

链表的每个结点中只包含一个指针域,叫做单链表

链表中第一个节点的存储位置叫做头指针,最后一个结点指针为空(NULL)

头结点的数据域不存储任何信息。

头指针与头结点的异同
  • 头指针是指链表指向第一个结点的指针,若链表头结点,则是指向头结点的指针。

  • 具有标识作用,常作为链表的名字(指针变量的名字)。

  • 无论链表是否为空,头指针均不为空。

  • 头指针是链表的必要元素。

  • 头结点放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。

  • 头结点不是链表的必要元素。

用结构指针来描述单链表:

typedef struct Node
{
ElemType data;//数据域
struct Node* Next;//指针域
}Node;
typedef struct Node* LinkList;

结点由存放数据的数据域和存放后继结点地址的指针域组成。

假设p是指向线性表第i个元素的指针,则p->data第i个元素的数据。p->next指第i+1个元素,也就是指向ai+1。
单链表的读取(GetElem)
思路:

  • 声明 一个结点p指向链表第一个结点,初始化j从1开始;
  • 当j<i时,遍历链表。让p的指针向后移动,不断指向下一结点,j+1;
  • 若到链表尾为空,则说明元素不存在,查找失败。
  • 否则查找成功,返回结点p的数据。
//GetElem.c

由于单链表的结构中没有定义表长,因此不知道要循环多少次,因此也就不用for来控制循环。

单链表的插入

假设插入的单链表元素为s,两个标准语句

  • s->next=p->next;

  • p->next=s;
    思路:

  • 声明一结点p指向链表头结点,初始化j从1开始;

  • 当j<1时,就遍历链表,让p的指针向后移动,不断指向下一节点,j+1;

  • 若到链表末尾p为空,则说明第i个元素不存在;

  • 否则查找成功,在系统中声称一个空结点s;

  • 将数据元素e赋值给s->data;

  • 插入两个标准语句;

  • 返回成功

//ListInsert
单链表的删除
  • p->next=p->next->next;
  • q=p->next ; p->next=q->next;
//ListDelete
单链表删除插入总结
  • 单链表无论是插入还是删除,都是先遍历第i个元素,然后再实现插入和删除操作。
  • 时间复杂度都为O(n)。
  • 单链表中无法预知元素i的位置,每次操作需遍历链表。存在的意义在于:若在第i个位置插入连续的10个元素,对于顺序存储结构,每一次插入需移动n-i个位置,所以每次都是O(n)。对于单链表,只需要在第一次时,找到第i个位置的指针,此时为O(n),接下来时间复杂度都为O(1)。
  • 对于插入或删除数据越频繁的操作,单链表的效率优势越明显。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值