数据结构(C语言版)-第2章 线性表

image

image

image

#define  MAXSIZE 100     //最大长度
typedef  struct {
  ElemType  *elem;     //指向数据元素的基地址
  int  length;          //线性表的当前长度                                                      
 }SqList;

例子

#define MAXSIZE 10000    //图书表可能达到的最大长度 
typedef struct            //图书信息定义
{ 
   char no[20];            //图书ISBN
   char name[50];        //图书名字
   float price;             //图书价格
}Book; 
typedef struct
{ 
   Book *elem;    //存储空间的基地址 
   int length;        //图书表中当前图书个数 
}SqList;        //图书表的顺序存储结构类型为SqList

线性表的重要基本操作

1.  初始化    2.  取值   3.  查找    4.  插入   5.  删除

初始化线性表L (参数用引用)

Status InitList_Sq(SqList &L){    //构造一个空的顺序表L
    L.elem=new ElemType[MAXSIZE];   //为顺序表分配空间
    if(!L.elem) exit(OVERFLOW);       //存储分配失败
    L.length=0;                      //空表长度为0
    return OK;
}

初始化线性表L (参数用指针)

Status InitList_Sq(SqList *L){    //构造一个空的顺序表L
    L-> elem=new ElemType[MAXSIZE];   //为顺序表分配空间
    if(! L-> elem) exit(OVERFLOW);       //存储分配失败
    L-> length=0;                      //空表长度为0
    return OK;
}

销毁线性表L

void DestroyList(SqList &L)
{
  if (L.elem) delete[]L.elem;    //释放存储空间
}

清空线性表L

void ClearList(SqList &L) 
{
   L.length=0;                //将线性表的长度置为0
}

判断线性表L是否为空

int IsEmpty(SqList L)
{
  if (L.length==0) return 1;      
   else return 0;
}

获取线性表L中的某个(第i 个)数据元素的内容

int GetElem(SqList L,int i,ElemType &e)
{
  if (i<1||i>L.length) return ERROR;   
   //判断i值是否合理,若不合理,返回ERROR
  e=L.elem[i-1];   //第i-1的单元存储着第i个数据
  return OK;
}

在线性表L中查找值为e的数据元素

int LocateELem(SqList L,ElemType e)
{
  for (i=0;i< L.length;i++)
      if (L.elem[i]==e) return i+1; //返回是第几个元素               
  return 0;
}

在线性表L中第i个数据元素之前插入数据元素e

Status ListInsert_Sq(SqList &L,int i ,ElemType e){
   if(i<1 || i>L.length+1) return ERROR;             //i值不合法
   if(L.length==MAXSIZE) return ERROR;    //当前存储空间已满     
   for(j=L.length-1;j>=i-1;j--) 
       L.elem[j+1]=L.elem[j];    //插入位置及之后的元素后移
    L.elem[i-1]=e;                     //将新元素e放入第i个位置
  ++L.length;                 //表长增1
  return OK;
}
//见下图

image

(1)判断插入位置i 是否合法。
(2)判断顺序表的存储空间是否已满。     
(3)将第n至第i 位的元素依次向后移动一个位置,空出第i个位置。
(4)将要插入的新元素e放入第i个位置。
(5)表长加1,插入成功返回OK。

若插入在尾结点之后,则根本无需移动(特别快);若插入在首结点之前,则表中元素全部后移(特别慢);若要考虑在各种位置插入(共n+1种可能)的平均移动次数,该如何计算?

image

 将线性表L中第i个数据元素删除

Status ListDelete_Sq(SqList &L,int i){
   if((i<1)||(i>L.length)) return ERROR;     //i值不合法
   for (j=i;j<=L.length-1;j++)                   
    L.elem[j-1]=L.elem[j];       //被删除元素之后的元素前移  
   --L.length;                         //表长减1
  return OK;
}

image

(1)判断删除位置i 是否合法(合法值为1≤i≤n)。
(2)将欲删除的元素保留在e中。     
(3)将第i+1至第n 位的元素依次向前移动一个位置。
(4)表长减1,删除成功返回OK。

若删除尾结点,则根本无需移动(特别快);若删除首结点,则表中n-1个元素全部前移(特别慢);若要考虑在各种位置删除(共n种可能)的平均移动次数,该如何计算?

image

查找、插入、删除算法的平均时间复杂度为O(n),顺序表的空间复杂度S(n)=O(1)(没有占用辅助空间)

image

2.5 线性表的链式表示和实现

链式存储结构:结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。线性表的链式表示又称为非顺序映像或链式映像。
image

各结点由两个域组成:
数据域:存储元素数值数据
指针域:存储直接后继结点的存储位置

结点:数据元素的存储映像。由数据域和指针域两部分组成

单链表、双链表、循环链表:
结点只有一个指针域的链表,称为单链表或线性链表
有两个指针域的链表,称为双链表
首尾相接的链表称为循环链表

循环链表:

image

image

image

有头结点时,当头结点的指针域为空时表示空表

头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。

image

2.5.1 单链表的定义和实现

image

单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名
若头指针名是L,则把链表称为表L

typedef struct LNode{
     ElemType   data;       //数据域
     struct LNode  *next;   //指针域
}LNode,*LinkList;   // *LinkList为Lnode类型的指针

若p->data=ai, 则p->next->data=ai+1

image

2.5.2 单链表基本操作的实现

1.  初始化   2.  取值      3.  查找       4.  插入       5.  删除

1.初始化(构造一个空表 )
【算法步骤】
(1)生成新结点作头结点,用头指针L指向头结点。
(2)头结点的指针域置空。

Status InitList_L(LinkList &L){ 
   L=new LNode;                        
   L->next=NULL;     
   return OK; 
}

销毁

Status DestroyList_L(LinkList &L){
    LinkList p;
       while(L)
        {
            p=L;  
            L=L->next;
            delete p;  
        }
     return OK;
}

清空(链表仍在)

Status ClearList(LinkList & L){
  // 将L重置为空表 
   LinkList p,q;
   p=L->next;   //p指向第一个结点
   while(p)       //没到表尾 
      {  q=p->next; delete p;     p=q;   }
   L->next=NULL;   //头结点指针域为空 
   return OK;
}

求表长

int  ListLength_L(LinkList L){
//返回L中数据元素个数
    LinkList p;
    p=L->next;  //p指向第一个结点
     i=0;             
     while(p){//遍历单链表,统计结点数
           i++;
          p=p->next;    } 
    return i;                             
}

判断表是否为空

int ListEmpty(LinkList L){
//若L为空表,则返回1,否则返回0
   if(L->next)   //非空
     return 0;
 
else
     return 1;
}

2. 取值(根据位置i获取相应位置数据元素的内容)

链表的查找:要从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构

//获取线性表L中的某个数据元素的内容
Status GetElem_L(LinkList L,int i,ElemType &e){ 
    p=L->next;j=1; //初始化
     while(p&&j<i){    //向后扫描,直到p指向第i个元素或p为空 
       p=p->next; ++j; 
     } 
     if(!p || j>i)return ERROR; //第i个元素不存在 
     e=p->data; //取第i个元素 
     return OK; 
}//GetElem_L
//在线性表L中查找值为e的数据元素
int LocateELem_L (LinkList L,Elemtype e) {
//返回L中值为e的数据元素的位置序号,查找失败返回0 
  p=L->next; j=1;
  while(p &&p->data!=e)  
        {p=p->next;  j++;}                  
  if(p) return j; 
  else return 0;
}

3. 插入(插在第 i 个结点之前)

image

//在L中第i个元素之前插入数据元素e 
Status ListInsert_L(LinkList &L,int i,ElemType e){ 
     p=L;j=0; 
      while(p&&j<i−1){p=p->next;++j;}    //寻找第i−1个结点 
      if(!p||j>i−1)return ERROR;    //i大于表长 + 1或者小于1  
      s=new LNode;            //生成新结点s 
      s->data=e;                         //将结点s的数据域置为e 
      s->next=p->next;                     //将结点s插入L中 
      p->next=s; 
      return OK; 
}//ListInsert_L

5. 删除(删除第 i 个结点)

image

//将线性表L中第i个数据元素删除
 Status ListDelete_L(LinkList &L,int i,ElemType &e){
    p=L;j=0; 
    while(p->next &&j<i-1){//寻找第i个结点,并令p指向其前驱 
        p=p->next; ++j; 
    } 
    if(!(p->next)||j>i-1) return ERROR; //删除位置不合理 
    q=p->next; //临时保存被删结点的地址以备释放 
    p->next=q->next;     //改变删除结点前驱结点的指针域 
    e=q->data;     //保存删除结点的数据域 
    delete q;     //释放删除结点的空间 
 return OK; 
}//ListDelete_L

单链表的建立(前插法)

image

void CreateList_F(LinkList &L,int n){ 
     L=new LNode; 
      L->next=NULL; //先建立一个带头结点的单链表 
      for(i=n;i>0;--i){ 
        p=new LNode; //生成新结点 
        cin>>p->data; //输入元素值 
        p->next=L->next;L->next=p;     //插入到表头 
     } 
}//CreateList_F

单链表的建立(尾插法)

image

void CreateList_L(LinkList &L,int n){ 
      //正位序输入n个元素的值,建立带表头结点的单链表L 
      L=new LNode; 
      L->next=NULL;     
      r=L;     //尾指针r指向头结点 
      for(i=0;i<n;++i){ 
        p=new LNode;         //生成新结点 
        cin>>p->data;           //输入元素值 
        p->next=NULL; 
       r->next=p;             //插入到表尾 
        r=p;     //r指向新的尾结点 
      } 
}//CreateList_L

2.5.3 循环链表

image

对循环链表,有时不给出头指针,而给出尾指针可以更方便的找到第一个和最后一个结点
image

开始结点:rear->next->next
终端结点:rear

循环链表的合并

image
image

2.5.4 双向链表

typedef struct DuLNode{
    ElemType   data;              
    struct DuLNode  *prior;  
    struct DuLNode  *next;  
}DuLNode, *DuLinkList


image

image

双向链表的插入
image

Status ListInsert_DuL(DuLinkList &L,int i,ElemType e){
   if(!(p=GetElemP_DuL(L,i))) return ERROR;
    s=new DuLNode; 
   s->data=e;
   s->prior=p->prior;  
   p->prior->next=s;
   s->next=p;  
   p->prior=s;
   return OK;
}

双向链表的删除
image

Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e){
   if(!(p=GetElemP_DuL(L,i)))     return ERROR;
   e=p->data;
   p->prior->next=p->next;
   p->next->prior=p->prior;
   delete p; 
   return OK;
}

总结

image

原文地址:https://www.cnblogs.com/mohuishou-love/p/10319585.html