数据结构复习(二)线性表

一、线性表的两种存储结构

1.顺序存储

线性表的顺序表示:用一组地址连续的存储单元依次存储线性表的数据元素。用顺序存储结构表示的线性表又称为顺序表。

顺序表特点

  • 元素的存储位置能反映元素间的逻辑关系,逻辑上相邻的数据元素在物理位置上也相邻。
  • 只要确定顺序表在内存中的起始位置,表中的任何元素都可随机存取,顺序表是一种随机存取的存储结构

顺序表优点

可以随机存取表中任一元素; 大部分操作都比较容易实现;存储利用率高(关系隐含在存储结构里,不需显式表示)

顺序表缺点

不知道表的规模的情况下,2个常量的大小不太好确定;顺序表在进行插入和删除操作时,需要移动大量元素,浪费大量时间。

适用场合:表中元素变动不大,但需要快速存取元素;或存储空间需求预先知道。

顺序表的实现

静态分配存储结构

#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int length;
}SqList;

动态分配存储结构

#define LIST_INIT_SIZE 100  //顺序表存储空间的初始分配量
#define LISTINCREMENT 10    //顺序表存储空间的分配增量
typedef struct {
     ElemType *elem;   //存储空间的基地址
     int length;       //顺序表的当前长度
     int listsize;     //数组存储空间的长度
}SqList;

2.链式存储

用一组任意的存储单元(可连续可不连续)存储线性表中的数据元素。

链式表特点:

逻辑关系上相邻的两个元素在物理位置上不一定相邻,元素之间的关系由指针指示

优点:

链表在进行插入和删除操作时,不再移动大量元素,仅需修改指针;不再需要定义2个常量

缺点:

元素只能顺序存取;大部分操作的时间复杂度都为O(n);存储利用率较低(存储元素时要附带存储指针)

适用场合: 表中元素频繁插入和删除;或存储空间需求不定的应用。

单链表存储结构

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

二、基本操作

1.顺序表

初始化

void InitList(List *L) {
	(*L).data = (ElementType *)malloc(InitSize * sizeof(ElementType));
	(*L).length = 0;
	(*L).listsize = InitSize;
}

插入
时间复杂度 O(n)

//类C语言
Status ListInsert(SqList &L,int i,ElemType e){
    //插入元素e到顺序表L的第i个位置之前
    if(i<1 ||i>L.length+1) return ERROR;
    if(L.length==L.listize)
     {
       L.elem=(ElemType *) realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
       if(!L.elem) exit(OVERFLOW);
       L.Listsize += LISTINCREMENT;
     }//重新分配空间
    q=&L.elem[i-1];
    for(p=&(L.elem[L.length-1];p>=q;--p) 
         *(p+1)=*p;
    *q=e;
    ++L.length;
    return OK;
}

删除
时间复杂度O(n)

//类C语言
Status ListDelete(SqList &L,int i,ElemType &e){
    //删除顺序表L中的第i个元素,其值由e返回;
    if(i<1 ||i>L.length) return ERROR;
    p=&L.elem[i-1];
    e=*p;
    q=L.elem+L.length-1;//表尾元素的位置
    for(++p;p<=q;++p) 
       *(p-1)=*p;//元素依次前移
    --L.length;
    return OK;
}

遍历

Status ListTraverse(List L)
{
for(i=0;i<L.length;i++)
    printf("%d",L.data[i]);
}

2.单链表

初始化

//类C语言
Status InitList(LinkList &L) 
{
    L=(LinkList)malloc(sizeof(LNode));
    if (!L) exit(OVERFLOW);
    L->next=NULL;
    return OK;
}

插入
时间复杂度O(n)

//在带头结点的单链表L中在第i个位置前插入(类C)
Status Insert(LinkList &L,int i,ElemType e)
{
  while(p&&j<i-1)
  {
  p=p->next;++j;
  }
  if(!p||j>i-1)
  return ERROR;
  s=(LinkList )malloc(sizeof(LNode));
  s->data=e;
  s->next=p->next;
  p->next=s;
  return ok;

删除
时间复杂度O(n)

//删除第i个元素
ListDelete(&L, i ,&e)
{
   p=L; j=0;
   while(p->next&&j<i-1) {p=p->next;++j;}
   if(!(p->next)||j>i-1) return ERROR;
   q=p->next;//q指向被删除结点
   p->next=q->next;
   e=q->data;
   free(q);
}

遍历
时间复杂度O(n)

Status ListTraverse(LinkList L,Status (*visit)(ElemType))
 {
    for( p=L->next; p; p=p->next )
      if( !visit(p->data) ) return ERROR;
    return OK;
}

三、循环、双向链表

循环链表

带头结点的循环链表:尾结点的指针指向头结点

优点:从表中任一结点出发都可找到表中其他结点。
操作:和线性链表基本一致,差别在于判空和判表尾条件。
判空:L->next=L;判表尾:p->next=L;

线性单链表和循环链表的缺点:寻找结点后继和前驱的可能性及复杂度不同。

双向链表:

在单链表的每个结点里增加一个指向其直接前趋的指针域prior。方便查找结点的前驱结点。
双向链表C语言描述

typedef struct DulNode{
    ElemType  data;
    Struct DulNode *prior;//指向其直接前驱的指针域
    Struct DulNode *next; //指向其直接后继的指针域
}DulNode,*DuLinkList; 

//本文中仅仅写出了自己老师所点的复习重点,不是很完善,假期会完善。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全糖去冰不加料

打赏一块钱💰也是钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值