从零开始的数据结构——线性结构

本文介绍了数据结构的基础概念,包括线性结构中的顺序和链式存储多项式,重点讲解了多项式表示优化,以及线性表的操作如顺序存储、链式存储和广义表的使用。文中还提到矩阵的稀疏表示方法,通过十字链表有效管理非零元素。
摘要由CSDN通过智能技术生成

从零开始的数据结构

本期正式步入数据结构

本篇文章是学习视频中的笔记

参考视频 [浙江大学数据结构(陈越、何钦铭)](数据结构_中国大学MOOC(慕课) (icourse163.org))

线性结构

多项式的表示

多项式的关键数据

1、多项式项数n

2、各项系数ai以及指数i


顺序存储结构直接表示

数组各分量对应多项式各项:

在这里插入图片描述

两个多项式相加:两个数组对应分量相加

但是问题是:如何表示多项式x+3x^2000?

显然我们要用2001个分量表示,但其实只有两项是非零的,这样会造成空间的巨大浪费

所以虽然这种方法表示方便,但仍有很大的问题,于是引入第二种方法


顺序存储结构表示非零项

基本思路:只表示非零项

每个非零项 aix^i 涉及两个信息:系数ai和指数i,可以将一个多项式看成是一个(ai,i)二元组的集合

我们这里也可以用数组表示,但每个分量就不能简单只是系数了,而且要包含指数

结构数组表示:数组分量是由系数ai、指数i组成的结构,对应一个非零项。

在这里插入图片描述

这种方法是否计算方便呢?

只要我们按指数大小有序存储,仍然是非常容易的

例如两个多项式相加

在这里插入图片描述

先定位到两个多项式的第一项,比较两个定位点对应的项,较大的这一项提取出来,并使该定位点后移,另一个定位点不变,继续比较两个定位点对应的项,循环以上操作,直到其中一个多项式的项全部遍历,然后将另一个多项式剩余项按序排放。


链表结构存储非零项

链表中每个结点存储多项式中的一个非零域,包括系数和指数两个数据域以及一个指针域

排列的时候我们仍然按指数大小有序存储即可

对应的结构定义可以这样写

typedef struct PolyNode *Polynomial:
struct PolyNode{
    int coef;
    int expon;
    Polynomial link;
}

例如仍是上面的P1(x)和P2(x)

在这里插入图片描述


线性表

线性表的定义及操作集:

定义:线性表是由同类型数据元素构成有序序列的线性结构

  • 表中元素个数称为线性表的长度
  • 线性表没有元素时,称为空表
  • 表起始位置称表头,结束位置称表尾

类型名称:线性表(List)

数据对象集:线性表是n(>=0)个元素构成的有序序列(a1,a2,… ,an)

操作集:线性表L∈List,整数i表示位置,元素X∈ElementType

  1. List MakeEmpty() 初始化一个空线性表L
  2. ElementType FindKth( int K, List L ) 根据位序K返回对应元素
  3. int Find( ElementType X, List L ) 在线性表L中查找X的第一次出现位置
  4. void Insert( ElementType X, int i, List L ) 在位序i前插入一个新元素X
  5. void Delete( int i, List L ) 删除指定位序i的元素
  6. int Length( List L ) 返回线性表L的长度n

线性表的顺序存储

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

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;
        else     //找到了
            return PtrL;
    }
    //查找成功的平均比较次数为(n+1)/2,平均时间性能为O(n)
    
  3. 插入( 第 i(1 =< i <= n+1)个位置上插入一个值为X的新元素)

    //现移动,再插入
    //从后往前挪
    void Insert( ElementType X,int i,List PtrL) 
    {
        int j;
        if(PtrL->Last == MAXSIZE-1){
            printf("表满");
            return;
        }
        if(i<1||i>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;
    }
    //平均移动次数为n/2,平均时间性能为O(n)
    
  4. 删除

    //现移动,再插入
    //从后往前挪
    void Delete( int i, List L )
    {
        int j;
        if(i<1||i>PtrL->Last+1){ //检查空表及删除位置的合法性
            printf("不存在第%d个元素",i);
            return;
        }
        for(j = i;j<=PtrL->Last;j++)
            //将a(i+1)到 an 顺序前移
            PtrL->Data[j-1]=PtrL->Data[j]; 
        PtrL->Last--;//Last仍指向最后元素
        return;
    }
    //平均移动次数为(n-1)/2,平均时间性能为O(n)
    

线性表的链式存储实现

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

  • 插入、删除不需要移动数据元素,只需要修改“链”。
typedef struct LNode *List;//定义指向链表指针
struct LNode{
    ElementType Data;
    List Next;
};
struct Lnode L;
List PtrL;
主要操作
  1. 求表长

    int Length( List PtrL )
    {
        List p = PtrL; //p指向表的第一个结点
        int j = 0;
        while(p){   //条件:p不等于NULL
            p = p->Mext;
            j++;
        }
        return j;
    }
    //时间性能为O(n)
    
  2. 查找

    (1)按序号查找:FindKth

    List FindKth(int K,List PtrL)
    {
        List p = PtrL; //p指向表的第一个结点
        int i = 1;
        while(p!=NULL&&i<K)
        {
            p=p->Next;
            i++;
        }
        if(i== K)
            return p;
        else 
            return NULL;
    }
    //平均时间性能O(n)
    

    (2)按值查找:Find

    List Find(ElementType X,List PtrL){
        List p = PtrL;
        while(p!=NULL && p->Data != x)   
            p = p->Next;
        return p;
    }
    //平均时间性能O(n)
    
  3. 插入( 第 i(1 =< i <= n+1)个位置上插入一个值为X的新元素)

    步骤:

    1. 先构造一个新结点,用s指向
    2. 再找到链表的第i-1个结点,用p指向
    3. 修改指针,插入结点(p之后插入新结点是s)

    在这里插入图片描述

    注意顺序(1) s->Next=p->Next;(2) p->Next=s;

    List Insert(ElementType X,int i ,List PtrL){
        List p,s;
        //第1个位置插入,表头地址改变
        if(i == 1){
            s = (List)malloc(sizeof(struct LNode));
            s->Data = X;
            s->Next = PtrL;
            return s;           //返回新的头结点地址
        }
        p = Findkth(i-1,PtrL); //返回第i-1结点地址
        //p不存在
        if(p == NULL){
            printf("参数i错");
            return NULL;
        }else{
            s = (List)malloc(sizeof(struct LNode)); 
            s->Data = X;
            s->Next = p->Next;
            p->Next = s;        
            return PtrL;    //返回头结点地址(头指针不变)
        }
    }
    
    //平均查找次数n/2
    
  4. 删除( 删除第 i(1 =< i <= n)个位置上的结点 )

    步骤:

    1. 先找到链表的第i-1个结点,用p指向;
    2. 再用指针s指向要被删除的结点(p的下一个结点)
    3. 然后修改指针,删除s所指结点
    4. 最后释放s所指结点的空间

在这里插入图片描述

List Delete(int i,List PtrL){
    List p,s;
    if(i == 1){
        s = PtrL; //为了释放内存所以保存
        if(Ptrl!=NULL)
            PtrL = PtrL->Next;
        else 
            return NULL;
        free(s);
        return PtrL;
    }
    p = Findkth(i-1,PtrL); //返回第i-1结点地址
    //判断指针p的合法性(是否前一个不存在或是结点i不存在)
    if(p == NULL){
        printf("第%d个结点不存在",i-1);
        return NULL;
    }else if(p->Next == NULL){           
        printf("第%d个结点不存在",i); 
        return NULL;
    }else{
        s = p->Next;
        p->Next = s->Next;
        free(s);
        return PtrL;
    }
}

广义表

引例

我们知道了一元多项式的表示,那么二元多项式如何表示?

比如,给定二元多项式

在这里插入图片描述

如何表示呢?

分析:将上述二元多项式看成关于x的一元多项式

在这里插入图片描述

所以可以用“复杂”链表表示为:

在这里插入图片描述


定义
  • 广义表是线性表的推广
  • 对于线性表而言,n个元素都是基本的单元素
  • 广义表中,这些元素不仅可以是单元素也可以是另一个广义表
typedef struct GNode *GList;
struct GNode{
    int Tag;    //标志域:0表示结点是单元素,1表示结点是广义表
    union{      //子表指针域SubList与单元素数据域Data复用,即共用存储空间
        ElementType Data;
        GList SubList;
    }URegion;
    GList Next;     //指向后继结点
};

我们在构造时可能会碰到一个问题:一个域有可能是不能分解的单元,有可能是一个指针

C语言有一种手段,union。union可以把不同类型的数据组合在一起,可以把这个空间理解成某种类型,也可以理解为另外一种类型

如何区分不同类型?

一般的方法是设一个标记,即Tag来区分后面的空间是Data还是SubList

在这里插入图片描述


多重链表

定义

多重链表:链表中的节点可能同时隶属于多个链

  • 多重链表中结点的指针域会有多个,如前面例子包含了Next和SubList两个指针域;
  • 但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表

多重链表有广泛用途:基本上树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储


矩阵

矩阵可以用二维数组表示,但二维数组表示有两个缺陷:

  • 数组大小需要事先确定
  • 对于“稀疏矩阵”,造成大量的存储空间浪费(稀疏矩阵是指矩阵里面0很多)

分析:采用一种典型的多重链表——十字链表来存储稀疏矩阵

  • 只存储矩阵非0元素项

    结点的数据域:行坐标Row、列坐标Col、数值Value

  • 每个结点通过两个指针域,把同行、同列串起来

    • 行指针(向右指针)Right
    • 列指针(向下指针)Down

头节点的标识值为“Head“,矩阵非0元素结点的标识值为”Term“

对应结构如下:

在这里插入图片描述


实例

给出一个矩阵A

在这里插入图片描述

链表图如下

在这里插入图片描述

  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值