线性结构

线性表及其实现

[引例]:多项式的表示

一元多项式:f(x)=a0+a1X+...+an-1Xn-1+anXn

主要运算:多项式的相加、相减、相乘等。

[分析]如何表示多项式?

多项式的关键数据:

  • 多项式的项数n
  • 各项系数ai及指数i

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

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

a[i]:项Xi的系数ai,i是对应的指数

例如:f(x)=4x5-3x2+1,表示成:

 0  1   2   3  4  5    ......    下标i

1 0 -3 0 0 4 ......

 a[i]

 1     -3x2      4x5  

以这样的形式表示后,两个多项式进行相加时,直接用两个数组对应分量相加即可。

用这个方法有一个问题,比如,如何表示多项式x+3x2000?

  显然,至少要用2001个长度的数组,但其本身只有两项在起作用,不仅造成的空间极大浪费,而且运算过程中要进行循环,含有很多0项,比较繁琐。

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

每个非零项aixi涉及两个信息:系数ai和指数i

可以将一个多项式看成是一个系数和指数(ai,i)的二元组的集合

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

例如:P1(x)=9x12+15x8+3x2和P2(x)=26x19-4x8-13x6+82

用这种方式来表示,只需要表示非零项即可。为了计算方便,需要按指数大小有序存储

相加过程:从头开始比较两个多项式当前对应项的指数

P1:(9,12),(15,8),(3,2)

P2:(26,19),(-4,8),(-13,6),(82,0)

先看两个多项式的第一项(9,12)和(26,19),由于指数19比12大,那么就将(26,19)输出,

然后将(9,12)与P2的第二项(-4,8)进行比较,由于12比8大,所以将(9,12)输出,

然后将(-4,8)与P1的第二项(15,8)进行比较,此时两者指数相同,则将系数相加得到(11,8)输出,

以此类推,当其中一个多项式结束之后,另一个多项式未完成的项直接输出即可。

===>P3:(26,19),(9,12),(11,8),(-13,6),(3,2),(82,0)

即P3(x)=26x19+9x12+11x8-13x6+3x2+82

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

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

coef expon link
typedef struct PolyNode *Polynomial;
struct PolyNode{
     int coef;//系数
     int expon;//指数
     Polynomial link;//指针域
}//定义一个结构体

上述的例子中,两个多项式的链表存储形式可以表示为:

它的加法运算过程和方法2的过程是一样的



什么是线性表?

由前面关于多项式的引例可以得到如下启示:

  • 同一个问题可以有不同的表示(存储)方法
  • 有一类共性问题:有序线性序列的组织和管理

线性表[Linear List]”:由同类型数据元素构成有序序列的线性结构

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

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

  • 类型名称:线性表(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):在位序前插入一个新元素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;//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;//Last表示链表的最后一个元素,当last为0时,表示含有一个元素,没有元素就赋值为-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;//找到后返回的是存储位置
}

查找成功的平均次数是(n+1)/2,平均时间性能为O(n)。

3.插入(第i(1≤i≤n+1)个位置上插入一个值为X的新元素)

只能先移动最后一个元素

void Insert(ElementTyepe X,int i,List PtrL){
    int j;
    if(PtrL->Last == MAXSIZE-1){
         printf("表空间已满,不能插入");
         return;
    }
    if(i<1 || i>PtrL->Last+2){
         printf("位置不合法");
    }
    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.删除(删除表的第i(1<= i<= n)个位置上的元素)

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];//将ai+1~an顺序向前移动
     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=p->Next;
         j++;//当前p指向的是第j个结点
    }
    return j;
}

平均时间性能:O(n)

2.查找

(1)按序号查找: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;//找到第K个,返回指针
     else
          return NULL;//否则返回空
}

(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(1≤i≤n+1)个结点后插入一个值为X的新结点)

(1)先构造一个新结点,用s指向;

(2)再找到链表的第i-1个结点,用p指向

(3)然后修改指针,插入节点(p之后插入新结点是s)

执行顺序:(1)s->Next=p->Next;(2)p->Next=s;

思考:修改指针的两个步骤如果交换一下,将会发生什么?

如果语句执行顺序为:(1)p->Next=s;(2)s->Next=p->Next,则

将会导致s->Next指向其本身。

List Insert(ElementType X,int i,List PtrL){
     List p,s;
     if(i==1){//新结点插入在表头
          s=(List)malloc(sizeof(struct LNode))//申请、填装结点
          s->Data=X;
          s->Next=PtrL;
          return s;//返回新表头指针
     }
     p=FindKth(i-1,PtrL);//查找第i-1个结点
     if(p==NULL){//第i-1个不存在,不能插入
          printf("参数i错误");
          return NULL;
     }else{
          s=(List)malloc(sizeof(struct LNode));//申请、填装结点
          s->Data=X;
          s->Next=p->Next;//新结点插入在第i-1个结点的后面
          p->Next=s;
          return PtrL;
     }
}

平均时间性能是:O(n/2)

4.删除(删除链表的第i(1≤i≤n)个位置上的结点)

(1)先找到链表的第i-1个结点,用p指向;

(2)再用指针s指向要被删除的结点(p的下一个结点);s=p->Next;

(3)然后修改指针,删除s所指结点;p->Next=s->Next;

(4)最后释放s所指结点的空间;free(s),这样内存空间才不会泄露。

思考:操作指针的几个步骤如果随意改变,将会发生什么?

s->Next将会指向其本身。

List Delete(int i,List PtrL){
    List p,s;
    if(i==1){//若要删除的是第一个结点
        s=PtrL;//s指向第一个结点
        if(PtrL!=NULL)
            PtrL=PtrL-Next;//从链表中删除
        else
            return NULL;
        free(s);//释放被删除结点
        return PtrL;
   }
    p=FindKth(i-1,PtrL);//查找第i-1个结点
    if(p==NULL){
        printf("第%d个结点不存在",i-1);
        return NULL;
    }else if(p->Next==NULL){
        printf("第%d个结点不存在",i);
        return NULL;
    }else{
        s=p->Next;//s指向第i个结点
        p->Next=s->Next;//从链表中删除
        free(s);//释放被删除结点
        return PtrL;
    }
}

平均时间复杂性:O(n/2)



广义表

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

比如,给定二元多项式:P(x,y)=9x12y2+4x12+15x8y3-x8y+3x2

[分析]可以将上述二元多项式看成关于x的一元多项式:P(x,y)=(9y2+4)x12+(15y3-y)x8+3x2它的系数不再是常量,而是一个关于y的多项式。

所以,上述二元多项式可以用“复杂”链表表示为:

广义表(Generalized List)定义:

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

在广义表构造时,我们会遇到这样一个问题:一个域有可能是一个不能分解的单元素,有可能是一个指针,为了解决这个问题,C语言提供了Union

typedef struct GNode *GList;
struct GNode{
     int tag;//标识域:0表示结点是单元素,1表示结点是广义表
     union{//子表指针域Sublist与单元素数据域Data复用,即共用存储空间
          ElementType Data;
          GList Sublist;
     }URegion;
     GList Next;//指向后继结点
}

通过Tag来区分到底是Data还是SubList



多重链表

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

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

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

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

  • 一是数组的大小需要事先确定
  • 对于“稀疏矩阵”,将造成大量的存储空间浪费

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

  • 只存储矩阵非0元素项

    • 结点的数据域:行坐标Row、列坐标Col、数值Value
  • 每个结点通过两个指针域,把同行、同列串起来
    • 行指针(或称为向右指针)Right
    • 列指针(或称为向下指针)Down

矩阵A的多重链表图:存在两种结构类型

第一种:Term类型:它包含两个指针,一个指向同一行,一个指向同一列。同一行和同一列都设计成一个循环链表。

第二种:Head类型:作为行(列)链表的头结点

左上角的Term作为整个稀疏矩阵的入口,

  • 4——表示稀疏矩阵的行数
  • 5——表示稀疏矩阵的列数
  • 7——表示稀疏矩阵含有的非零项数

用一个标志域Tag来区分头结点和非0元素结点:

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

           

时间: 2024-10-13 12:05:33

线性结构的相关文章

线性结构和非线性结构

线性结构和非线性结构同属于数据结构中的逻辑结构类型 线性结构是指该结构中的节点之间存在一对一的关系.其特点是开始节点和终端节点都是唯一的,除了开始节点和终端节点外,其余节点都有且仅有一个直接前驱,有且仅有一个直接后继.此类型的存储结构有:顺序表(数组).链表.堆栈结构.队列结构等 非线性结构又包括集合.树形结构.图形结构或网状结构,特点是数据元素之间存在一个对多个或多个对多个的关系,其中集合是一种关系极为松散的结构.

查找算法--线性结构的查找方法

查找基本概念: 查找又称为检索,指从一批记录中找出满足指定条件的某一记录过程.在日常生活中比如通讯录查找,字典查找等经常使用查找方法:在程序设计中,查找在许多程序中需要占用很多时间,因此,一个好的查找方法可以提高程序的运行速度. 主关键字和次关键字: 在需要查找的数据结构中,每条记录一般包含多个数据域.查找条件一般是给定其中的一个或几个域的值,这些作为查找条件的域成为关键字(KEY),如果关键字可以唯一标示数据结构中的一条记录,则称此关键字(Primary Key):若关键字不能唯一区别各个不同

2、线性结构——数据结构【基础篇】

线性结构 线性结构的4大特点: 1.有一个结点只有前驱 2.有一个结点只有后继 3.其他结点既有一个前驱也有一个后继 4.在逻辑上相邻在物理上也相邻数据的逻辑关系叫做线性结构 线性结构的3大类型: 1.线性表--线性表是n个数据元素的有限序列 存储结构: 顺序存储对应一组操作:顺序表 链式存储对应一组操作:单向链表.双向链表.循环链表 2.栈--限定只在表的一端(表尾)进行插入和删除操作的线性表:允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),其这一端被固定了. 存储结构

poj 3666 Making the Grade (线性结构上的DP )

题意: 给定n个数,问你将他们修改成非增或非减序列的最小花费.最小花费的定义是 假设原数组为 a[1] a[2] a[3] .... a[n] 修改后数组为 b[1] b[2] b[3] .... b[n] 那么最小花费为|a[1]-b[1]|+|a[2]-b[2]|+| a[3] - b[3] |+.....| a[n] - b[n] |. 思路: 线性结构上的动态规划 定义状态d[i][j] 表示 前i-1个数字已经是最小花费 现在把第i个数修改成b[j] 修改完成后前i个数总共最少需要的花

【Python】09、线性结构与切片

一.线性结构 list.tuple.str.bytes.bytearray,range() 都是顺序存储.顺序访问的.都是可迭代对象.都可以通过索引访问:被称为线性结构 可使用len()获取长度.可以切片,可以使用iter()将可迭代对象转换为迭代器 In [16]: r = range(8) In [17]: r Out[17]: range(0, 8) In [18]: i = iter(range(8)) In [19]: i Out[19]: <range_iterator at 0x7

Mooc数据结构-基础和线性结构

1 数据结构 解决问题方法的效率,跟数据的组织方式有关 解决问题方法的效率,跟空间的利用效率有关 解决问题方法的效率,跟算法的巧妙程度有关 数据结构 数据对象在计算机中的组织方式 逻辑结构 物理存储结构 数据对象必定与一系列加在其上的操作相关联 完成这些操作所用的方法就是算法 抽象数据类型(Abstract Data Type) 数据类型 数据对象集 数据集合相关联的操作集 抽象: 描述数据类型的方法不依赖与具体实现 与存放数据的机器无关 与数据存储的物理结构无关 与实现操作的算法和编程语言无关

一步一步写算法(之线性结构的处理)

原文:一步一步写算法(之线性结构的处理) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 我们知道,在内存中的空间都是连续的.也就是说,0x00000001下面的地址必然是0x00000002.所以,空间上是不会出现地址的突变的.那什么数据结构类型是连续内部空间呢,其实就是数组,当然也可以是堆.数组有很多优势,它可以在一段连续空间内保存相同类型的数据,并且对这些数据进行管理.所以从这个意义上说,掌握了数组才能说明你数据结构入门了. 那么,

【算法和数据结构】_17_小算法_线性结构:顺序表

/* 本程序用来测试数据结构中的线性结构:顺序表 */ #include <stdio.h> #include <stdlib.h> #define LINEAR_MAX_SIZE 64 struct LinearList { int* List; //顺序表指针 unsigned short int ListLen; //顺序表最大的元素个数 unsigned short int CurrentLen; //顺序表当前元素的个数 }; typedef struct LinearL

13. C#数据结构与算法 -- 线性结构

本文中,我们讨论了三个部分的内容: 什么是线性结构,线性结构有哪些特点 . 详细介绍了一个最简单线性结构顺序表,并且通过源代码进行一些的分析. 最后还举了一个例子,让我们更好的理解顺序表. 第一部分:什么是线性结构,线性结构有哪些特点 什么是线性结构,线性结构是最简单.最基本.最常用的数据结构.线性表是线性结构的抽象(Abstract), 线性结构的特点是结构中的数据元素之间存在一对一的线性关系. 这 种一对一的关系指的是数据元素之间的位置关系,即: (1)除第一个位置的数据元素外,其它数据元素