zoukankan      html  css  js  c++  java
  • 数据结构 学习笔记02——线性表

    线性表的定义:具有相同特性的数据元素的一个有限序列。该序列中所含元素的个数叫做线性表长度。
    定义:L=(a1,a2,a3....an)
    其中为a1表头元素,为an表尾元素
    线性表的顺序存储结构
    其直接将线性表的逻辑结构映射到存储结构上

    ***********************************************

    例:线性表La,Lb分别表示集合A,B,求A = AUB。

    void Union(List &La, List Lb) {  // 算法2.1
      // 将所有在线性表Lb中但不在La中的数据元素插入到La中
      int La_len,Lb_len,i;
      ElemType e;
      La_len = ListLength(La);          // 求线性表的长度  
      Lb_len = ListLength(Lb);
      for (i=1; i<=Lb_len; i++) {
        GetElem(Lb, i, e);              // 取Lb中第i个数据元素赋给e
        if (!LocateElem(La, e, equal))  // La中不存在和e相同的数据元素
          ListInsert(La, ++La_len, e);  // 插入
      }
    } // union
    View Code

    例:

    void MergeList(List La, List Lb, List &Lc) {  // 算法2.2
      // 已知线性表La和Lb中的元素按值非递减排列。
      // 归并La和Lb得到新的线性表Lc,Lc的元素也按值非递减排列。
      int La_len, Lb_len;
      ElemType ai, bj;      
      int i=1, j=1, k=0;
      InitList(Lc);
      La_len = ListLength(La);    
      Lb_len = ListLength(Lb);
      while ((i <= La_len) && (j <= Lb_len)) {  // La和Lb均非空
        GetElem(La, i, ai);
        GetElem(Lb, j, bj);
        if (ai <= bj) {
          ListInsert(Lc, ++k, ai);
          ++i;
        } else { 
          ListInsert(Lc, ++k, bj);
          ++j;
        }
      }
      while (i <= La_len) {
        GetElem(La, i++, ai);  ListInsert(Lc, ++k, ai);
      }
      while (j <= Lb_len) {
        GetElem(Lb, j++, bj);  ListInsert(Lc, ++k, bj);
      }
    } // MergeList
    View Code

    ***********************************************

    线性表的顺序表示和实现


    //---------------------线性表的顺序存储类型描述------------------------

    #define MAXSIZE 50
    typedef struct
    {
       ElemType date[MAXSIZE];
       int length;
    }SqList;

    //---------------------线性表的的动态分配顺序存储结构--------------------

    #define LIST_INIT_SIZE  100  //线性表存储空间的初始分配量
    #define LISTINCREMENT  10   //线性表存储空间的分配增量
    typedef struct{
             ElemType  * elem ;    //存储空间基址
             int            length ;     //当前长度
             int            listsize ;    //当前分配存储容量(以sizeof(ElemType)为单位)
    }SqList;

    //-------------------构造一个空的线性表L(以动态的方式)-------------------

    Status InitList_Sq(SqList &L) {  // 算法2.3
      // 构造一个空的线性表L。
      L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
      if (!L.elem) return OK;        // 存储分配失败
      L.length = 0;                  // 空表长度为0
      L.listsize = LIST_INIT_SIZE;   // 初始存储容量
      return OK;
    } // InitList_Sq

    常用基本运算集合
          初始化
          创建
          销毁
          是否为空
          求线性表的长度
          输出线性表
          求线性表中某个元素的值
          按元素值查找
          插入运算(一般是前插)

    顺序表位序从1开始,因此要注意将逻辑位序转化为物理位序

    线性表插入、删除算法时间复杂度为O(n) [注:一般实现]
    ----------------------------------------------------------------------------------------------------------------
    例:有一个顺序表A。设计一个算法,删除所有元素值在[x,y]之间的所有元素,要求算法时间复杂度为O(n),空间复杂度为O(1)
    实现:以A表为基础重新构建一个表。

    例:有一个顺序表L,假设元素类型ElemType为整型,并且所有元素均不相等。设计一个算法,以第一个元素为分界线,将所有小于它的元素移到该元素前面,将所有大于它的元素移到该元素的后面。
    实现1:从两边向中间交替查找不满要求的元素进行交换
    实现2:保留第一个元素的值,然后从右边前左边查到不满要求的元素,并将其设置到第一个元素位置 ... ...(变化基准位置(原来第一个元素的位置),从而将缺省出的基准位用于存放找到的数值)

    //---------------------在顺序线性表L的第i个元素之前插入新的元素e-------------------------

    Status ListInsert_Sq(SqList &L, int i, ElemType e) {  // 算法2.4
      // 在顺序线性表L的第i个元素之前插入新的元素e,
      // i的合法值为1≤i≤ListLength_Sq(L)+1
      ElemType *p;
      if (i < 1 || i > L.length+1) return ERROR;  // i值不合法
      if (L.length >= L.listsize) {   // 当前存储空间已满,增加容量
        ElemType *newbase = (ElemType *)realloc(L.elem,
                      (L.listsize+LISTINCREMENT)*sizeof (ElemType));
        if (!newbase) return ERROR;   // 存储分配失败
        L.elem = newbase;             // 新基址
        L.listsize += LISTINCREMENT;  // 增加存储容量
      }
      ElemType *q = &(L.elem[i-1]);   // q为插入位置
      for (p = &(L.elem[L.length-1]); p>=q; --p) *(p+1) = *p;
                                      // 插入位置及之后的元素右移
      *q = e;       // 插入e
      ++L.length;   // 表长增1
      return OK;
    } // ListInsert_Sq
    View Code

    //--------------------在顺序线性表L中删除第i个元素,并用e返回其值-------------------------

    Status ListDelete_Sq(SqList &L, int i, ElemType &e) {  // 算法2.5
      // 在顺序线性表L中删除第i个元素,并用e返回其值。
      // i的合法值为1≤i≤ListLength_Sq(L)。
      ElemType *p, *q;
      if (i<1 || i>L.length) return ERROR;  // i值不合法
      p = &(L.elem[i-1]);                   // p为被删除元素的位置
      e = *p;                               // 被删除元素的值赋给e
      q = L.elem+L.length-1;                // 表尾元素的位置
      for (++p; p<=q; ++p) *(p-1) = *p;     // 被删除元素之后的元素左移
      --L.length;                           // 表长减1
      return OK;
    } // ListDelete_Sq
    View Code

    //--------------------在顺序线性表L中查找第1个值与e满足compare()的元素的位序------------

    int LocateElem_Sq(SqList L, ElemType e,
            Status (*compare)(ElemType, ElemType)) {  // 算法2.6
      // 在顺序线性表L中查找第1个值与e满足compare()的元素的位序。
      // 若找到,则返回其在L中的位序,否则返回0。
      int i;
      ElemType *p;
      i = 1;        // i的初值为第1个元素的位序
      p = L.elem;   // p的初值为第1个元素的存储位置
      while (i <= L.length && !(*compare)(*p++, e)) 
        ++i;
      if (i <= L.length) return i;
      else return 0;
    } // LocateElem_Sq
    View Code

    //--------------------已知顺序线性表La和Lb的元素按值非递减排列---------------------------

    void MergeList_Sq(SqList La, SqList Lb, SqList &Lc) {  // 算法2.7
      // 已知顺序线性表La和Lb的元素按值非递减排列。
      // 归并La和Lb得到新的顺序线性表Lc,Lc的元素也按值非递减排列。
      ElemType *pa,*pb,*pc,*pa_last,*pb_last;
      pa = La.elem;  pb = Lb.elem;
      Lc.listsize = Lc.length = La.length+Lb.length;
      pc = Lc.elem = (ElemType *)malloc(Lc.listsize*sizeof(ElemType));
      if (!Lc.elem)
        exit(OVERFLOW);   // 存储分配失败
      pa_last = La.elem+La.length-1;
      pb_last = Lb.elem+Lb.length-1;
      while (pa <= pa_last && pb <= pb_last) {  // 归并
        if (*pa <= *pb) *pc++ = *pa++;
        else *pc++ = *pb++;
      }
      while (pa <= pa_last) *pc++ = *pa++;      // 插入La的剩余元素
      while (pb <= pb_last) *pc++ = *pb++;      // 插入Lb的剩余元素
    } // MergeList
    View Code

    线性表的链式表示和实现


    //--------------线性表的单链表存储结构---------------------

    typedef struct  LNode{
            ElemType      data;
            struct LNode  *next;
    }LNode, *LinkList;

    对于带头节点的单链表而言
    插入结点:s->next = p->next;
    (插入S,先找到前一个节点p)
                   p->next = s;

    删除结点: p->next = p->next->next;(先找到前一个节点p)
    --------------------------------------------------------------
    例:有一个带头结点的单链表L={a1,b1,a2,b2,a3,b3,...,an,bn},设计一个算法将其拆分成两个带头结点的单链表L1和L2,L1={a1,a2,a3...an},L2={bn,bn-1,...b1}.要求L1使用L的头结点
    注:链表的插入分头插和尾插
    例:有一个带头结点的单链表L,设计一个算法使其元素递增有序。
    问:带头结点的单链表与不带头结点的单链表有何区别?
    答:带头结点单链表可以在头节点中加入一些附加信息,并且有利于实现各种运算(删除和插入)。

    //----------------------取第i个值-----------------------------------------

    Status GetElem_L(LinkList &L,int i, ElemType &e) {  // 算法2.8
      // L为带头结点的单链表的头指针。
      // 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
      LinkList p;
      p = L->next;   
      int j = 1;           // 初始化,p指向第一个结点,j为计数器
      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
    View Code

    //----------------在带头结点的单链线性表L的第i个元素之前插入元素e--------

    Status ListInsert_L(LinkList &L, int i, ElemType e) {  // 算法2.9
      // 在带头结点的单链线性表L的第i个元素之前插入元素e
      LinkList p,s;
      p = L;   
      int j = 0;
      while (p && j < i-1) {  // 寻找第i-1个结点
        p = p->next;
        ++j;
      } 
      if (!p || j > i-1) return ERROR;      // i小于1或者大于表长
      s = (LinkList)malloc(sizeof(LNode));  // 生成新结点
      s->data = e;  s->next = p->next;      // 插入L中
      p->next = s;
      return OK;
    } // LinstInsert_L
    View Code

    //---------在带头结点的单链线性表L中,删除第i个元素,并由e返回其值--------

    Status ListDelete_L(LinkList &L, int i, ElemType &e) {  // 算法2.10
      // 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
      LinkList p,q;
      p = L;
      int 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;
      free(q);
      return OK;
    } // ListDelete_L
    View Code

    //------逆位序输入(随机产生)n个元素的值,建立带表头结点的单链线性表L-----

    void CreateList_L(LinkList &L, int n) {  // 算法2.11
      // 逆位序输入(随机产生)n个元素的值,建立带表头结点的单链线性表L 
      LinkList p;
      int i;
      L = (LinkList)malloc(sizeof(LNode));
      L->next = NULL;              // 先建立一个带头结点的单链表
      for (i=n; i>0; --i) {
        p = (LinkList)malloc(sizeof(LNode));  // 生成新结点
        p->data = random(200);     // 改为一个随机生成的数字(200以内)
        p->next = L->next;    L->next = p;    // 插入到表头
      }
    } // CreateList_L
    View Code

    //-----------------有序合并链表----------------------------------------

    void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) {
      // 算法2.12
      // 已知单链线性表La和Lb的元素按值非递减排列。
      // 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列。
      LinkList pa, pb, pc;
      pa = La->next;    pb = Lb->next;
      Lc = pc = La;             // 用La的头结点作为Lc的头结点
      while (pa && pb) {
        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
    View Code

    //--------------线性表的静态单链表存储结构------------------------

    #define MAXSIZE 100
    typedef struct
    {
       ElemType data; //数据域
       int cur;      //游标域,指示下一个元素在数组中的位置
    }component,StaticList[MaxSize];

    静态链表是借助一维数组来描述链表。数组中的一个分量表示一个结点,同时使用游标(cur)代替指针以指示结点在数组中的相对位置(游标为-1时表示相对应的结点为空).数组中的0分量可以看成头结点,其指针域指示静态链表的第一个结点,并将最后一个元素的指针域0构成循环结构
    这种存储结构需预先分配一个较大空间,但是在进行线性表插入和删除操作时不需移动元素,仅需要修改“指针”,因此仍然具有链式存储结构的主要优点。

    对于静态链表的初始化,一定要将其它没有元素的结点的.next设为-1,并将下标为[0].next设为0
    对于静态链表可视为一个带头节点的循环链表,_StaticList[0]为其头结点,对于插入操作一般都先查找到前一个结点(前插),另对于新的插入项一定要存在下.next为-1的位置上。
    另对于删除时要考虑链表是否为空表,对于插入要考虑是否表满

    同样静态也有不带头节点的,类似于不带头节点的循环链表
    同样可以构造类似于循环双链表的静态链表  
    等等总之灵活多样但一般不存在单链表式的静态链表

     双链表
    双链表的创建与单链表相似,只不过每个结点多了个PRIOR指针域

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


    双链表亦可分头插和尾插
    特点(对称性):
    p->next->prior = p;
    p->prior->next = p;
    //-------------在带头结点的双链循环线性表L的第i个元素之前插入元素e--------------

    Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) { //算法2.18
      // 在带头结点的双链循环线性表L的第i个元素之前插入元素e,
      // i的合法值为1≤i≤表长+1。
      DuLinkList p,s;
      if (!(p = GetElemP_DuL(L, i)))  // 在L中确定第i个元素的位置指针p
        return ERROR;                 // p=NULL, 即第i个元素不存在
      if (!(s = (DuLinkList)malloc(sizeof(DuLNode))))
        return ERROR;
      s->data = e;
      s->prior = p->prior;
      p->prior->next = s;
      s->next = p;
      p->prior = s;
      return OK;
    } // ListInsert_DuL

    //--------------删除带头结点的双链循环线性表L的第i个元素-----------------

    Status ListDelete_DuL(DuLinkList &L, int i, ElemType &e) {//算法2.19
      // 删除带头结点的双链循环线性表L的第i个元素,i的合法值为1≤i≤表长
      DuLinkList p;
      if (!(p = GetElemP_DuL(L, i)))  // 在L中确定第i个元素的位置指针p
        return ERROR;                 // p=NULL, 即第i个元素不存在
      e = p->data;
      p->prior->next = p->next;
      p->next->prior = p->prior;
      free(p);    
      return OK;
    } // ListDelete_DuL
    View Code


    循环链表
    分为带头结点的循环单链表和循环双链表。
    其判断结尾条件是:p->next == L(头结点)
    因此头结点也连在整个循环链表中
    针对于其初始化:
    L->prior = L;
    L->next  = L;
    一般为解决特殊问题
    还存在不带头结点的循环单链表和循环双链表(如约瑟夫环)
    --------------------------------------------------------------------------------


    一元多项式的表示及相加


    //-------输入m项的系数和指数,建立表示一元多项式的有序链表P------------

    void CreatPolyn(PLinkList &P, int m) {  // 算法2.22
      // 输入m项的系数和指数,建立表示一元多项式的有序链表P
      PLink h, q, s;
      PElemType e;
      int i;
      InitList(P);   h = GetHead(P);
      e.coef = 0.0;  e.expn = -1;
      SetCurElem(h, e);       // 设置头结点
      for (i=1; i<=m; ++i) {  // 依次输入m个非零项
        // scanf ("%f,%d
    ",&e.coef, &e.expn);
        e.coef = (float)(random(1, 90) + random(10)/10.0);
        if (random(2)) e.coef = -e.coef;
        e.expn=e.expn+random(1,10); //产生随机的数据,但是expn值是递增的
        if (!LocateElem(P, e, q, cmp)) { // 当前链表中不存在该指数项
          if (MakeNode(s,e)) InsFirst(q, s);  // 生成结点并插入链表
          if(q==P.tail) P.tail=s;
        } else i--;  //  如果没有产生插入,则将i值减1
      }
    } // CreatPolyn
    View Code

    //---------打印输出一元多项式---------------

    Status PrintfPoly(PLinkList P) {
      int i=0;
      PLink q=P.head->next;
      while (q) {
        if (fabs(q->data.coef) > 0.005) {
          if (i>0) {
            if (q->data.coef>0.005) printf(" + ");
            else printf(" - ");
            printf("%.2f", fabs(q->data.coef));
          } else printf("%.2f", q->data.coef);
          if (q->data.expn>=1) printf("x");
          if (q->data.expn>1) printf("^%d", q->data.expn);
        }
        q=q->next;
        if (++i % 6 == 0) printf("
         ");
      }
      printf("
    ");
      return OK;
    }
    View Code

    //-------------多项式加法-----------------

    void AddPolyn(PLinkList &Pa, PLinkList &Pb) {  // 算法2.23
      // 多项式加法:Pa = Pa+Pb,利用两个多项式的结点构成"和多项式"。
      PLink ha,hb,qa,qb;
      PElemType a, b, temp;
      float sum;
      ha = GetHead(Pa);      // ha和hb分别指向Pa和Pb的头结点
      hb = GetHead(Pb);
      qa = NextPos(Pa,ha);   // qa和qb分别指向La和Lb中当前结点
      qb = NextPos(Pb,hb);
      while (qa && qb) {     // Pa和Pb均非空
        a = GetCurElem (qa); // a和b为两表中当前比较元素
        b = GetCurElem (qb);
        switch (Compare(a,b)) {
          case -1:  // 多项式PA中当前结点的指数值小
              ha = qa;
              qa = NextPos (Pa, qa);
              break;  
          case 0:   // 两者的指数值相等
              sum = a.coef + b.coef ;
              if (sum != 0.0) {  // 修改多项式PA中当前结点的系数值
                temp.coef=sum;
                temp.expn=a.expn;
                SetCurElem(qa, temp) ;
                ha = qa;
              } else {  // 删除多项式PA中当前结点
                DelFirst(ha, qa);
                FreeNode(qa);
              }
              DelFirst(hb, qb);
              FreeNode(qb);
              qb = NextPos(Pb, hb);
              qa = NextPos(Pa, ha);
              break;
          case 1:   // 多项式PB中当前结点的指数值小
              DelFirst(hb, qb);
              InsFirst(ha, qb); 
              qb = NextPos(Pb, hb);
              ha = NextPos(Pa, ha);
              break;
        } // switch
      } // while
      if (!Empty(Pb)) Append(Pa, qb);   // 链接Pb中剩余结点
      FreeNode(hb);  // 释放Pb的头结点
    } // AddPolyn


    数据结构实验——线性表,链表

    ----------------------------

    http://pan.baidu.com/share/link?shareid=2216128142&uk=186843953

    源文件,实验报告分享。



  • 相关阅读:
    Acwing 164 可达性统计 (拓扑排序+bitset)
    STL-bitset的基本用法
    Acwing 115 给树染色 (贪心)
    Acwing 112 雷达设备 (贪心)
    Acwing 110 畜栏预定 (贪心+stl)
    Acwing 110 防晒 (贪心算法)
    Acwing 七夕祭 (前缀和+中位数+思维)
    Acwing 103 电影 (map)
    USACO 最佳牛围栏 (二分答案+前缀和+双指针)
    Acwing 101 最高的牛 (差分数组)
  • 原文地址:https://www.cnblogs.com/Hewie/p/3431620.html
Copyright © 2011-2022 走看看