数据结构与算法 | 2.线性结构——线性表

博客内容为 浙江大学:陈越姥姥 的教学视频笔记

数据结构与算法

  • 线性结构的特点:在数据元素的非空有限集中,
    • (1)存在唯一的一个被称作“第一个”的数据元素
    • (2)存在唯一的一个被称作“最后一个”的数据元素
    • (3)除第一个之外,集合中的每个数据元素均只有一个前驱
    • (4)除最后一个之外,集合中每个元素均只有一个后继

1.线性表的类型定义

线性表是最常用且最简单的数据结构。简言之,线性表是n个数据元素的有限序列。
两个注意点:序列(即元素之间是有顺序的),有限(即元素个数是有限的)

(1)简单线性表、复杂线性表举例

  • 举例1(简单).26个英文字母的顺序表 (A,B,C,...,Z)
  • 举例2(复杂).学生登记表
    • 在复杂线性表中,一个数据元素可以由若干个数据项(item)组成。此时,常把数据元素成为记录(record),含有大量记录的线性表又称文件(file)
    • 复杂线性表
      • 在这个线性表中,共有5条记录(数据元素,一行为一条记录)
      • 每条记录(数据元素)共由5个数据项组成,分别是:学号、姓名、性别、年龄、班级。

(2)线性表 “引子:多项式表示”

  • [例]一元多项式及其运算
    • 一元多项式:f(x) = a0 + a1x + a2x2 + ... + an-1xn-1 + anxn
    • 主要运算:多项式相加、相减、相乘等。
  • [分析]如何表示多项式?
    • 多项式的关键数据
      • 多项式项数n
      • 各项系数ai及指数i

方法1:顺序存储结构直接表示

  • 数组各分量对应多项式各项:
    • a[i]:项xi的系数ai
    • 例如: f(x) = 4x5 - 3x2 + 1
    • 表示成:
      • 多项式
    • 两个多项式相加:两个数组对应分量相加
    • 问题:如何表示多项式 x + 3x2000
      • 如果按照上述表示,则数组大小需要为 2001 ,而只有两项为非零,造成了空间的极大浪费(遍历时也浪费时间)。

方法2:顺序存储结构表示非零项

  • 每个非零项aixi涉及两个信息:系数ai和指数i
    • 可以将一个多项式看作是一个 (ai,i) 二元组的集合。
    • 用结构数组表示:数组分量是由系数 ai、指数i组成的结构,对应一个非零项
    • 例如:P1(x) = 9x12 + 15x8 + 3x2 和 P2(x) = 26x19 - 4x8 -13x6 + 82
      • 多项式非零项
      • 按指数大小有序存储!!!
  • 上述两个多项式P1 和 P2 相加过程
    • P1 和 P2 按照指数递减排列
    • P1:(9,12), (15,8), (3,2)
    • P2:(26,19), (-4,8), (-13,6), (82,0)
    • 对比 P1 的第一项和 P2 的第一项比较,找到指数大的项。得到最大项并输出(26,19)
    • 对比 P1 的第一项和 P2 的下一项比较,找到指数大的项。得到第二项项并输出(9,12)
    • 对比 P1 的下一项和 P2 的下一项比较,得知此时指数大小相等,因此把系数相加。得到第三项并输出(11,8)
    • ...
    • 最终得到 P3:(26,19), (9,12), (11,8), (-13,6), (-13,6), (82,0)
    • 对应的多项式表示为: P3 = 26x19 + 912 + 118 - 136 + 32 + 82

方法3:链表结构存储非零项

  • 链表中每个结点存储多项式中的一个非零项,包括系数和指数两个数据域以及一个指针域
    • 链表
    • 结构定义
      typedef struct PolyNode *Polynomial;
      struct PolyNode{
          int cofe;
          int expon;
          Polynomial link;
      }
      
  • 例如: P1(x) = 9x12 + 15x8 + 3x2 和 P2(x) = 26x19 - 4x8 -13x6 + 82 的链表存储形式为:
    • 链表多项式
    • 两个多项式相加的逻辑和方法2一样

2.线性表的抽象数据类型描述

  • 线性表的抽象数据类型描述
    ADT List{
        类型名称:线性表(List)
        数据对象集:
    }
    

3.线性表及其顺序存储

多项式表示问题的启示:
a.同一个问题可以有不同的表示(存储)方法
b.有一类共性问题:有序线性序列的组织和管理

(1)什么是线性表

  • 线性表(Linear List):由同类型数据元素构成有序序列的线性结构
    • 表中元素个数称为线性表的长度
    • 线性表没有元素时,称为空表
    • 表起始的位置称为表头,表结束位置称为表尾

(2)线性表的顺序存储实现

  • 利用数组的 连续存储空间顺序存放 线性表的各元素
    • 线性表6

数据结构的定义

  • (线性表的顺序存储)数据结构的定义
    typedef struct LNode *List;
    struct LNode{
        ElementType Data[MAXSIZE];
        int Last;
    };
    struct LNode L;
    List PtrL;
    // 访问下标为 i 的元素:L.Data[i] 或 PtrL->Data[i]
    // 线性表的长度:L.Last+1 或 PtrL->Last+1
    

主要操作的实现

  • 1)初始化(建立空的顺序表)

    List MakeEmpty(){
        List PtrL;
        PtrL = (List)malloc(sizeof(struct LNode));
        Ptrl->Last = -1;
        return PtrL;
    }
    
  • 2)查找

    int Find(ElementType X, List PtrL){
        int i = 0;
        while(i <= PtrL->Last && PtrL->Data[i] != X)
            i++;
        if(i > PtrL->Last)
            return -1;    // 如果没找到,返回-1
        else return i;    // 找到后返回的是存储位置
    }
    
    • 查找成功的平均比较次数为 (1 + 2 + ... + n)/n = (n+1)/2;平均时间性能为O(n)。
  • 3)插入(第i(1 <= i <= n+1)个位置上插入一个值为X的新元素)

    • 先移动,再插入。(下标为 i - 1 及其以后部分往后挪)
    void Insert(ElementType X, int i, List PtrL){
        int j;
        if(PtrL->Late == MAXSIZE - 1 ){  /* 判断是否表满 */
            printf("表满");
            return;
        }
        if(i <= 1 || PtrL->Last+2){  /* 检查插入位置的合法性 */
            printf("位置不合法");
            return;
        }
        for(j = PtrL->Last; j >= i-1; j--)
            PtrL->Data[j+1] = PtrL->Data[j];  /* 将ai ~ an倒叙后移 */
        PtrL->Data[i-1] = X;  /* 插入新元素 */
        PtrL->Last++;  /* Last仍指向最后元素 */
        return;
    }
    
    • 平均时间复杂度O(n)
  • 4)删除(删除表的第i(1<=i<=n)个位置上的元素)

    • 将ai+1~an向前移动一个位置
    void Delete(int i, List PtrL){
        int j;
        if(i<=1 || i>PtrL->Last+1){
            printf("不存在第%d个元素",i);
            return;
        }
        for(j=i; j<=PtrL->Last; j++)
            PtrL->Data[j-1] = PtrL->Data[j];
        PtrL->Last--;
        return;
    }
    
    • 平均时间复杂度O(n)

4.线性表的链式存储实现

不要求逻辑上相邻的两个元素在物理上也相邻;通过“链”建立起数据元素之间的逻辑关系。

  • 插入、删除不需要移动数据元素,只需要修改“链”。
    • 数组时,可以根据下标直接访问第i个元素,并且Last指针可以知道表的长度。
    • 链式存储时,这两个问题怎么解决呢?

主要操作的实现

  • 1)求表长

    int Length(List PtrL){
        List p = PtrL;
        int j = 0;
        while(p){
            p = p->Next;
            j++;
        }
        return j;
    }
    
  • 2)查找

    • 按序号查找:FindKth;
      List FindKth(int K; List PtrL){
          List p = PtrL;
          int i = 1;
          while(p!=NULL && i<k){
              p = p->Next;
              i++;
          }
          if(i==k)return p;
          else return NULL;
      }
      
    • 按值查找:Find
      List Find(ElementType X, List PtrL){
          list p = PtrL;
          while(p != NULL && p->Data != X)
              p = p->Next;
          return p;
      }
      
  • 3)插入(第i(1 <= i <= n+1)个位置上插入一个值为X的新元素)

    • 先构造一个新结点,用s指向;
    • 再找到链表的第i-1个结点,用p指向;
    • 然后修改指针,插入结点(p之后插入新节点是s)
    • 插入
    代码实现(前面省略)...
    注意顺序:
    s = (List)malloc(sizeof(struct LNode));
    s->Data = X;
    s->Next = p->Next;
    p->next = s;
    
  • 4)删除(删除表的第i(1<=i<=n)个位置上的元素)

    • 先找到链表的第i-1个结点,用p指向;
    • 再用指针s指向要被删除的结点(P的下一个结点)
    • 然后修改指针,删除s所指向结点
    • 最后释放s所指向结点的空间(防止内存泄漏)
    • 删除
    代码实现(前面省略)...
    如果是第一个结点(表非空)PtrL:
    s = PtrL;
    PtrL = PtrL->Next;
    free(s);
    如果不是第一个结点
    s = p->Next;
    p->Next = s->Next;
    free(s);
    

5.双向链表和循环链表

6.广义表和多重链表

我们知道了一元多项式的表示,那么二元多项式又该如何表示呢?
P(x,y) = 9x12y2 + 4x12 + 15x8y3 - x8y + 3x2

(1)广义表

  • 广义表是线性表的推广
  • 对于线性表而言,n个元素都是基本的单元素
  • 广义表中, 这些元素不仅可以是单元素,也可以是另一个广义表

(2)多重链表

  • 链表中的节点可能同时 隶属于多个链表
    • 多重链表中结点的指针域会有多个
    • 包含两个指针域的链表不一定是多重链表(双向链表不是多重链表)
  • 多重链表有广泛的用途,基本上如树、图这样相对复杂的数据结构可以采用多重链表方式实现存储。

posted on 2020-09-05 16:16  wangxx06  阅读(486)  评论(0)    收藏  举报

导航