线性表

本文详细介绍了线性表的数据结构,包括顺序表与链表的不同实现方式及其基本操作。对比了顺序表与链表在查找、插入及删除操作上的时间与空间效率。

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

          数据结构的整体框架如下:

              

        线性结构的特点:在元素的非空集合中  

           (1)存在唯一的一个被称作“第一个”的数据元素
           (2)存在唯一的一个被称作“最后一个”的数据元素  

           (3)除第一个之外,每个元素均只有一个前驱
           (4)除最后一个之外,每个元素均只有一个后继

                                                                                                                               线性表
  一:线性表的顺序表示和实现:

1.特点:逻辑相邻,物理相邻,地址连续

2.分配方式:

(1)静态分配:数组

(2)动态分配:

物理存储结构为:

  Typedef struct {              //若后面不再用,可省略结构名
         ElemType   *elem;         //表基址
         int        length;        //表长(特指元素个数)
         int        listsize;       //表当前存储容量(字节数)
       }SqList;

3.基本操作:

(1)创建(初始化)

   Status InitList_Sq( SqList &L )
   { //构造一个空的线性表L。
    L.elem = (ElemType*)malloc( LIST_INIT_SIZE * sizeof( ElemType) );//申请内存区域,返回首地址
    If  ( ! L.elem )  
              exit ( OVERFLOW ) ;   // 存储分配失败
    L.length = 0;                 // 空表当前长度为0
    L.listsize = LIST_INIT_SIZE;  // 初始存储容量
    return   OK;
   } // InitList_Sq

(2)向第i个位置插入一个元素

  status listinsert ( &L,i,e)  {
   if (i<1 || i>L.len+1) return ERROR;  // i值不合法
   if (L.len = = L.lsize) {     // 当前存储空间已满,增加分配
     newbase = (Elemtype * ) realloc(L.elem, (L.listsize +
                       LISTINCREMENT) *  (sizeof(ElemType));
     if (!newbase) exit(overflow);   // 存储分配失败
     L.elem = newbase;              // 新基址
     L.listsize += LISTINCREMENT;  }       // 增加存储容量 
     q = &(L.elem[i-1]);             // q为插入位置
     for (p = &(L.elem[L.len-1]);p>=q;--p)   
          *(p+1) = * p;  // 元素后移
     *q = e;   ++L.len;  // 插入e,表长增1
   return OK; 
  } //listinsert


(3)向线性表中第i个位置删除一个元素

    Status ListDelete ( &L,i,&e)   
   {
     if (i<1 || i>L.len ) 
          return ERROR; // 删除位置不合法
     p = &(L.elem[i-1]); // p 为被删除元素的位置
     e = *p;             // 被删除元素的值赋给 e
     q = L.elem + L.len – 1; // 表尾元素的位置地址
     for (p; p<q; ++p)   
           *p =*(p+1); // 被删除元素之后的元素左移
     -- L.len;    // 表长减1
    return OK; 
   } // ListDelete

4.时间复杂度+空间复杂度分析

插入平均移动次数为:n(n+1)/2÷(n+1)n/2 ≈ O(n)

删除平均移动次数为:  n(n-1)/2÷n=(n-1)/2≈ O(n)
5.优缺点分析:

优点:可以随机存取表中任一元素(查找方便)

缺点:在插入,删除某一元素时,需要移动大量元素(非查找不方便)


二:线性表的链式表示和实现:

1.存储结构:Typedef struct Lnode{

                   ElemType data;

                   Struct  Lnode*next;

           }Lnode,*LinkList

2.基本操作:

(1)单链表的建立

*先进后出

 Void CreateLinkList(LinkList &L)
   {
         //首先建立一个头结点
          L=(LinkList)molloc(sizeof(Lnode));
          L->next=null;
         //先进后出建立n个元素的链表
         For(i=n;i>0;i--)
          {
            p=(LinkList)malloc (sizeof(LNode));   // 生成新结点
            cin>>p->data;       // 输入元素值
            p→next = L→next ;  
            L→next = p;              // 插入到表头
          }
    }
*先进先出
void CreateLinkList(LinkList &L)
   {
         //首先建立一个头结点
          L=(LinkList)molloc(sizeof(Lnode));
          L->next=null;
          head=L; 
          //先进后出建立n个元素的链表
          For(i=n;i>0;i--)
          {
            p=(LinkList)malloc (sizeof(LNode));   // 生成新结点
            cin>>p->data;// 输入元素值           
            head->next=p;//使头指针指向p
            p->next=null;//p的指向为空
            head=p->next;//head移到下一个
           }
    }
(2)单链表的查找

 status GetElem_L(LinkList L, int i, ElemType &e)
{ 
   // L为带头结点的单链表的头指针
   // 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
   P = L->next; j=1; // 初始化 ,p指向第一个结点,j为计数器
   while(p&&j<i)
    { p=p->next; ++j;}
                // 顺指针向后查找,直到p指向第i个元素或p为空
   if(!p||j>i) return ERROR;     // 第i个元素不存在
   e=p->data;                            // 取第i个元素
   return OK;
} // GetElem_L

(3)单链表的插入

status ListInsert(LinkList &L,int i,ElemType e )
{ //在带头结点的单链表L中第i个位置之前插入元素e 
   p = L;  j = 0;
   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; 
 } // ListInsert_l

(4)单链表的删除\

Status ListDelete_L(LinkList &L,int i,ElemType &e) {
// 删除以L为头指针(带头结点)的单链表中第i个结点
   p = L;    j = 0;
   while (p->next && j < i-1) 
      { p = p->next;   ++j;  } 
          // 寻找第 i 个结点,并令 p 指向其前趋
   if  (!(p->next) || j > i-1) 
      return ERROR;  // 删除位置不合理
   q = p->next; p->next = q->next; // 删除并释放结点
   e = q->data;   free(q);
   return OK;
} // ListDelete_L
(5)两有序单链表的有序合并

Void  MergeList_L(LinkList  &La,LinkList &Lb,LinkList &Lc)
    {    //按值排序的单链表LA,LB,归并为LC后也按值排序
   pa=La-->next;  pb=Lb-->next;   Lc=pc=La;  //初始化  
   while(pa&&pb)        //将pa 、pb结点按大小依次插入C中
            { if(pa->data<=pb->data)
                {pc->next=pa; pc=pa;  pa=pa->next;}
              else {pc->next=pb;  pc=pb;  pb=pb->next}  
             }
pc->next = pa?pa:pb ;     //插入剩余段
  free(Lb);                       //释放Lb的头结点
} //MergeList_L

3其它链表的形式

 (1)双向链表:有两个指针域的链表,称为双链表

     特点:可以双向查找表中结点

       Typedef struct Lnode{

                   ElemType data;

                   Struct  Lnode*prior,*next;

           }Lnode,*LinkList  


 

插入:

删除:


(2)循环链表:将表中最后一个结点的指针域指向头结点( p->next=head),整个链表形成一个环。


特点:从任一结点出发均可找到表中其他结点。


4.时间效率+空间效率分析

时间效率分析:

查找因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为 O(n)

插入和删除因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为 O(1)

但如果要在单链表中进行前插或删除操作,由于要查找前驱结点,所耗时间复杂度为 O(n)

空间效率分析:

链表中每个结点都要增加一个指针空间,相当于总共增加了n个整型变量,空间复杂度为 O(n)


小结:(1)顺序表是用数组实现的,而链表是用指针来实现的。

(2)当线性表的长度变化较大,难以估计其存储规模时,益采用动态链表作为存储结构为佳;当线性表的长度变化不大,易于事先确定其大小时,为了节约存储空间,宜采用顺序表作为存储结构。(基于空间的考虑)

(3)顺序表是一种随机存取结构,对表中任一结点都可在O(1)时间内直接存取。而链表中的结点则需用从头指针开始顺链扫描。

因此:若线性表的主要操作是查找,很少涉及插入、删除时,可采用顺序表结构;若要频繁进行插入和删除,宜采用链表结构;若表的插入、删除操作主要发生在表的两端,则宜采用有尾指针的单循环链表。



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值