zoukankan      html  css  js  c++  java
  • 数据结构:线性表(链表)

    1、链表

    (1)概念

    • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
    • n 个结点由指针链组成一个链表。它是线性表的链式存储映像,称为线性表的链式存储结构

    (2)结点组成

    • 数据域:存储元素数值数据
    • 指针域:存储直接后继结点的存储位置

    (3)单链表、双链表与循环链表:

    • 结点只有一个指针域的链表,称为单链表或线性链表
    • 有两个指针域的链表,称为双链表。双向链表能够克服单链表查询前驱结点必须从表头出发的问题,双向链表有两个指针域分别指向自己的前驱结点和后继结点,查找的时间复杂度为O(1)
    • 首尾相接的链表称为循环链表,从循环链表中的任何一个结点的位置都可以找到其他所有结点,而单链表做不到 

    (4)头指针、头结点和首元结点 

    • 头指针是指向链表中第一个结点的指针
    • 首元结点是指链表中存储第一个数据元素a1的结点
    • 头结点是在链表的首元结点之前附设的一个结点,数据域内只放空表标志和表长等信息。头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。

           

     (5)在链表中设置头结点有什么好处

    • 便于首元结点的处理:首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理
    • 便于空表和非空表的统一处理:无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了

     (6)特点

    • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
    • 访问时只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等

    (7)优缺点

    优点:

    • 数据元素的个数可以自由扩充
    • 插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高

    缺点:

    • 存储密度小
    • 存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问

    (8)结构定义

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

    (9)初始化

    Status InitList_L(LinkList &L){ 
       L=new LNode;  //生成新结点作头结点,用头指针L指向头结点。                      
       L->next=NULL; //头结点的指针域置空    
       return OK; 
    } 

    2、链表的基本操作

    (1)销毁

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

    (2)清空

    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; }

    与销毁不同,清空的时候会保留头结点

    (3)求表长

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

    (4)判断是否为空

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

    3、对链表的操作

    (1)添加元素:在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 

    时间复杂度:O(1)

    (2)删除元素:将线性表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 

    时间复杂度:O(1)

    (3)查找:在线性表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;
    } 

    时间复杂度:O(n)

    4、单链表的创建

    (1)前插法

    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 
    • 生成新结点
    • 将读入数据存放到新结点的数据域中
    • 将该新结点插入到链表的前端

    (2)尾插法

    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 
    • 从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点
    • 初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点

    5、线性表和链表的比较

    (1)空间

    • 存储空间:顺序表需要预先分配,会导致空间闲置或溢出现象,链表动态分配,不会出现存储空间闲置或溢出现象
    • 存储密度:线性表等于1,链表小于1

    (2)时间

    • 取数据:顺序表是随机存取,时间复杂度为O(1),链表是顺序存取,时间复杂度为O(n)
    • 插入删除:顺序表需要移动元素,时间复杂度为O(n),链表不需要,时间复杂度为O(1)

    (3)适用范围

    线性表

     表长变化不大,且能事先确定变化的范围
    很少进行插入或删除操作,经常按元素位置序号访问数据元素

    链表

    长度变化较大
    频繁进行插入或删除操作

  • 相关阅读:
    实验三 进程调度模拟程序
    实验二作业调度模拟程序
    最新广商小助手 项目进展 OpenGL ES 3D在我项目中引用 代码太多只好选重要部分出来
    最后冲刺 我的项目 广商小助手
    最新一课 老师指点用Listview适配器
    安卓小学生四则运算
    大三上学期安卓一边学一边开始做一个自己觉得可以的项目 广商小助手App 加油
    我要再接再力 学更多
    用场景来规划测试工作
    阅读第13,14,15,16,17章
  • 原文地址:https://www.cnblogs.com/zhai1997/p/13370521.html
Copyright © 2011-2022 走看看