【数据结构与算法】线性表顺序存储结构

文章目录

  • 一.顺序表的存储结构定义
    • 1.1定义
    • 1.2 图示
    • 1.3结构代码
    • *C语言的内存动态分配
  • 二.顺序表基本运算
    • *参数传递
    • 2.1建立
    • 2.2初始化(InitList(&L))
    • 2.3销毁(DestroyList(&L))
    • 2.4判断线性表是否为空表(ListEmpty(L))
    • 2.5求线性表的长度(ListLength(L))
    • 2.6输出线性表(DispList(L))
    • 2.7查找元素的操作(GetElem与LocateElem)
      • 2.7.1按序号(GetElem(L,i,&e))
      • 2.7.2按元素(LocateElem(L,e))
    • 2.8插入数据元素(ListInsert(&L,i,e))
    • 2.9删除数据元素(ListDelet(&L,i,&e))
  • 三.顺序表优缺点

一.顺序表的存储结构定义

1.1定义

线性表的顺序表示又称为顺序存储结构顺序映像,称为顺序表,其逻辑结构与存储结构一致:用一段地址连续的存储单元依次存储线性表的数据元素。

设顺序表的每个元素占用 c 个存储单元,则第 i 个元素的存储地址为:

所以,只要确定了存储顺序表的起始地址(即基地址```a1```),计算任意一个元素的存储地址的时间是相等的。

1.2 图示

如图可见顺序表的特点:地址连续,依次存放,随机存取和类型相同(即以物理位置相邻表示逻辑关系,任一元素均可随机存取),我们通常用一维数组表示顺序表,也就是把线性表中相邻元素存储在数组中相邻位置,从而导致数据元素的序号和存放它的数组下标之间具有一一对应的关系。

注意:
C语言中数组的下标是 从0开始 的,而顺序表中元素的序号是 从1开始 的,即线性表中第 i 个元素存储在数组中下标为 i-1 的位置。

1.3结构代码

#define LIST INIT SIZE 100    //线性表存储空间的初始分配量
typedef struct{
    ElemType elem[LIST_INIT_SIZE];//存放顺序表中的元素的一个数组,可以替换,事先定义
    int length;               //当前长度
}SqList;                      //SqList是存放ElemType类型元素的顺序表,指针指向第一个元素的地址

从上面代码我们可以看到,这里我们封装了一个结构,事实上就是对数组进行封装,增加了个当前长度的变量罢了。(通过sqlist这个结构对数组进行封装,多了一个length的变量)

总结一下:顺序存储结构封装需要三个属性。

  1. 存储空间的起始位置,数组data,它的存储位置就是线性表存储空间的存储位置。(类似队伍的第一个同学找到自己的位置)
  2. 线性表的最大存储容量,数组长度maxsize(也就是我们总共买了多少张票)
  3. data三个含义:首地址,地址常量,&data[0]
  4. 线性表的当前长度:length(也就是总共来了多少人)

注意:数组的长度与线性表的当前长度需要区分一下:数组的长度是存放线性表的存储空间的总长度,一般初始化后不变。而线性表的当前长度是线性表中元素的个数,是会变化的。由于线性表中可以进行插入操作,所以数组长度要大于当前线性表的长度。

*C语言的内存动态分配

由上诉内容可知,[MaxSize]是固定的,存放地址也固定,下面我们介绍动态内存分配,可以重建或修改数组

  • 获得这个根数组中基地址(第一个元素的地址),其他地址用下标来操作。
//先定义指针,用内存分配函数,动态分配内存定义空间
typedef struct{
    ElemType *elem;
    int length;
}SqList;//顺序表类型
L.elem=(ElemType*)malloc(sizeof(ElemType)*MAXSIZE);//分配MaxSize个ElemType类型的空间
//返回值类型是void*,所以要强转
//要保存这个地址到data中就要把malloc返回值强转为L.data的类型

二.顺序表基本运算

*参数传递

在学习顺序表基本运算之前,我们先了解一下相关的知识点——C++中的参数传递

  1. 函数调用时传送给形参表的实参必须与形参三个一致:类型,个数,顺序

  2. 参数传递有两种方式:传值方式(参数为整型,实型和字符型等)和传地址

  3. 传地址有三种:参数为指针变量,参数为引用类型,参数为数组名

    注意:当指针变量做参数时,形参变化不影响实参(栈的创建和销毁)

    #include<iostream.h>
    void swap(float *m,float *n){
        float *t;
        t=m;
        m=n;
        n=t;
    }
    void main(){
        float a,b,*p1,*p2;
        cin>>a>>b;
        p1=&a;p2=&b;//即交换p1和p2的值不会影响a和b的值
        swap(p1,p2);
    }
    cout<<a<<endl<<b<<endl;
    
    • 数组名作参数

      • 数组可以看成const指针(特殊的指针),定义后不可修改

      • 传递的是数组的首地址

      • 对形参数组所做的任何改变都将反映到实参数组中

    //用数组做函数的参数,求10个整数的最大数
    #include<iostream.h>
    #define N 10//定义常量N为10
    int max(int b[]){//定义计算数组中最大数的函数
        int i,n;
        n=b[0];
        for(i=1;i<N;i++)
            if(n<b[i])
              n=b[i];
        return n;        
    }
    int max(int a[]);
    void main(){
        int a[10];
        int i,m;
        for(i=0;i<N;i++)
            cin>>a[i];
        m=max(a);
        cout<<"the max number is:"<<m;
    }
    
    • 引用类型做参数

      • 引用,它用来给一个对象提供一个替代的名字

      • 传递引用参数给函数与传递指针的效果是一样的,形参变化实参也发生变化。

      引用类型做形参,在内存中并没有产生实参的副本,它直接对实参操作;而一般变量做参数,形参与实参就占用不同的存储单元,所以形参变量的值是实参变量的副本。因此,当参数传递的数据量较大时,用引用比用一般变量传递参数的时间和空间效率都好。

      • 指针参数虽然也能达到与使用引用的效果,但在被调函数中需要重复使用“*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性交差;另一方面,在主函数的调用点处,必须用变量的地址作为实参。
      #include<iostream.h>
      void main(){
          int i=5;
          int &j=i;
          //j是一个引用类型,代表i的一个替代名i值改变时,j值也跟着改变,所以会输出i=7,j=7
          i=7;
          cout<<"i="<<i<<"j="<<j;
      }
      
      #include<iostream.h>
      void swap(float &m,float &n){
          float temp;
          temp=m;
          m=n;
          n=temp;
      }
      void main(){
          float a,b;
          cin>>a>>b;
          swap(a,b);
          cout<<a<<endl<<b<<endl;
      }
      
  4. 形参&LL的区别:带&的是引用型参数,它是地址传递,其实参会随着形参的改变而改变;不带&的参数是一般参数,是值传递,其实参不会随着形参的改变而改变。所以,结构改变,并且需要传回这种改变的要用引用型参数,否则用一般参数。

  • 引用L->length等同于(*L)length

2.1建立

这里介绍整体创建顺序表,即由数组元素a[0…n-1]创建顺序表L。其方法是将数组a中的每个元素依次放入顺序表中,并将n赋给顺序表的长度域。算法如下:

void CreatList(SqList *&L,ElemType a[],int n){//由a中的n个元素建立顺序表
    int i=0,k=0;//k表示L中元素的个数,初始值为0
    L=(SqList*)malloc(sizeof(SqList));//分配存放线性表的空间
    while(i<n){//i扫描数组a的元素
        L->data[k]=a[i];//将元素a[i]存放到L中
        k++;i++;
    }
    L->length=k;//设置L的长度K
}

调用上述算法创建好L所指的顺序表之后,需要回传给对应的实参,也就是说L是输出型参数,所以在形参L的前面需要加上引用符"&".

2.2初始化(InitList(&L))

该运算功能是构造一个空的线性表L,实际上只需分配线性表的存储空间并将length域设置为0即可。算法如下:

void InitList(SqList *&L){
    L=(SqList*)malloc(sizeof(SqList));//分配存放线性表的空间
    L->length=0;;//置空线性表的长度为0
}

本算法的时间复杂度为O(1)。

2.3销毁(DestroyList(&L))

该运算功能是释放线性表L占用的内存空间。算法如下:

void DestroyList(SqList *&L){
    free(L);//释放L所指的顺序表空间
}

本节的顺序表是通过malloc函数分配存储空间的,当不再需要顺序表时务必调用DestroyList基本运算释放其存储空间;否则,尽管系统会自动释放顺序表的指针变量L,但不会自动释放L指向的存储空间,如此可能会造成内存泄漏,本算法的时间复杂度为O(1).

2.4判断线性表是否为空表(ListEmpty(L))

该运算返回一个布尔值表示L是否为空表。若L为空表,返回true,否则返回false。算法如下:

bool ListEmpty(SqList *L){
    return(L->length==0);
}

2.5求线性表的长度(ListLength(L))

该运算返回顺序表L的长度,实际上只需返回length域的值即可。算法如下:

bool ListEmpty(SqList *L){
    return(L->length==0)
}

2.6输出线性表(DispList(L))

该运算依次输入L中各元素的值。算法如下:

void DispList(SqList *L){
    for(int i=0;i<L->length;i++)
        printf("%d",L->data[i]);
    printf("\n");
}

本算法中的基本操作为for循环中的printf语句,故时间复杂度为O(n)

2.7查找元素的操作(GetElem与LocateElem)

2.7.1按序号(GetElem(L,i,&e))

实现GetElem的具体操作,即将线性表L中的第i个位置元素值返回。就程序而言,我们只需要把数组第i-1下标的值返回即可。

bool GetElem(SqList *L,ElemType e){
    int i=0;
    while(i<1||i>L->length)
        return false;//参数i错误时返回false
    e=L->data[i-1];//取元素的值
    return true;//成功找到元素时返回true
}

本算法的时间复杂度为O(1)

2.7.2按元素(LocateElem(L,e))

按运算顺序查找第一个值为e的元素的逻辑符号,若这样的元素不存在,则返回为0.算法如下:

int LocateElem(SqList *L,ElemType e){
    int i=0;
    while(i<L->length && L->data[i]!=e)
        i++;//查找元素e
    if(i>=L->length)
        return 0;//未找到时返回0
    else
        return i+1;//找到后返回其逻辑符号
}

该算法的时间复杂度为O(n)

2.8插入数据元素(ListInsert(&L,i,e))

刚才我们提到过排队的问题,如果这时候有一个人要回到队列当中,我们应该怎么做,我们会找到他要插入位置的那个同学,让他以及它后面的同学全部往后移动一个位置,再将新同学安排在这个位置里面。所以我们插入算法的思路如下:

  1. 如果插入位置不合理,抛出异常;
  2. 如果线性表长度大于等于数组长度,则抛出异常或动态增加数组容量;
  3. 从最后一个元素开始向前遍历到第i个位置,分别将他们都向后移动一个位置;
  4. 将要插入元素填入位置i处;
  5. 线性表长度+1;
    算法如下:
Status ListInsert(SqList *L,int i,ElemType e){
    int k;
    if(L->length==MAXSIZE){//顺序线性表已经满了
        return ERROR;
    }
    if(i<1||i>L->length+1){//当i不在范围内时
        return ERROR;
    }
    if(i<=L->length){//若插入数据位置不在表尾
        //将要插入位置后数据元素向后移动一位
        for(k=L->length-1;k>=i;k--){
            L->data[k+1]=L->data[k];
        }
    }
    L->data[i-1]=e;//将新元素插入
    L->length++;
    return true;
}

时间复杂度为O(n)

2.9删除数据元素(ListDelet(&L,i,&e))

我们完全可以借鉴前面插入数据元素的算法,得到删除数据算法的思路:

  1. 如果删除位置不合理,抛出异常;

  2. 取出删除元素

  3. 从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置;

  4. 表长-1

    算法如下:

Status ListDelet(SqList *L,int i,ElemType *e){
    int k;
    if(L->length==0){
        return ERROR;
    }
    if(i<1||i>L->length){
        return ERROR;
    }
    *e=L->data[i-1];
    if(i<L->length){
        for(k=i;k<L->length;k++){
            L->data[k-1]=L->data[k];
        }
    }
    L->length--;
    return true;
}

三.顺序表优缺点

线性表的顺序存储结构,在存、读数据时,不管是哪个位置,时间复杂度都是O(1)而在插入或删除数据时,时间复杂度都是O(n)。这就说明,它比较适合元素个数比较稳定,不经常插入和删除元素,而更多的操作是存取数据的应用。那么我们可以得出它的优缺点:

优点:

  • 无需为表示表中元素之间的逻辑关系而增加额外的存储空间
  • 可以快速地存取表中任意位置的元素。

缺点:

  • 插入和删除操作需要移动大量元素。
  • 线性表长度变化较大时,难以确定存储空间的容量。
  • 容易造成存储空间的“碎片”(因为申请空间的时候是一整块一整块地申请的,那么两块地的中间就会有一小块碎片的地方被浪费)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/889867.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

根据请求错误的状态码判断代理配置问题

SafeLine&#xff0c;中文名 “雷池”&#xff0c;是一款简单好用, 效果突出的 Web 应用防火墙(WAF)&#xff0c;可以保护 Web 服务不受黑客攻击。 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL 注入、XSS、 代码注入、命…

如何高效撰写和发表SCI论文

第一章、论文写作准备即为最关键 1、科技论文写作前期的重要性及其分类 2、AI工具如何助力学术论文 3、研究主题确定及提高创新性 兴趣与背景&#xff1a;选择一个您感兴趣且有背景知识的研究领域。 创新性&#xff1a;选题和研究设计阶段如何提高学术创新性的方法。 研究缺…

yolov5-7.0模型DNN加载函数及参数详解(重要)

yolov5-7.0模型DNN加载函数及参数详解&#xff08;重要&#xff09; 引言yolov5&#xff08;v7.0&#xff09;1&#xff0c;yolov5.h(加载对应模型里面的相关参数要更改)2&#xff0c;main主程序&#xff08;1&#xff09;加载网络&#xff08;2&#xff09;检测推理&#xff0…

QD1-P2 HTML 编辑器:HBuilderX

本节学习&#xff1a; HTML课程内容介绍HBuilderX编辑器的使用 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p2 HTML 内容 基础语法 标签整体架构DOCTYPE 常用标签 标题和水平线段落和换行列表div 和 span格式化标签图片超链接标签表格表单字符实体 编辑器 HBuilder…

解决pyinstaller 打包 ddddocr 库方法

前言 ddddocr 库 在打包成 exe 文件后一直有各种各样的问题。无法运行。 总是提示缺少 onnxruntime_providers_shared.dll 等问题。例如下图: 所以这里总结一下打包解决方法。 方法 1、 第一步,先使用命令打包一次 pyinstaller -F demo.py -p D:\Python38\Lib\site-pac…

登录注册静态网页实现(HTML,CSS)

实现效果图 实现效果 使用HTML编写页面结构&#xff0c;CSS美化界面&#xff0c;点击注册&#xff0c;跳转到注册界面&#xff0c;均为静态网页&#xff0c;是课上的一个小作业~ 使用正则表达式对输入进行验证&#xff0c;包括邮箱格式验证&#xff0c;用户名格式验证。 正则…

【Java】类型转换与类型提升

目录 1.类型转换 1.1自动类型转换&#xff08;隐式&#xff09; 1.2强制类型转化&#xff08;显式&#xff09; 2.类型提升 3.字符串类型 1.类型转换 Java作为一个强类型编程语言,当不同类型之间的变量相互赋值的时候,会有教严格的校验. 在Java中&#xff0c;当参与运算数…

【C++】——继承(下)

【C】——继承&#xff08;下&#xff09; 5 继承与友元6 继承与静态成员7 多继承7.1 继承模型7.2 菱形继承的问题7.3 虚继承7.4 多继承中的指针偏移问题 8 组合与继承 5 继承与友元 友元关系不能被继承。即一个函数是父类的友元函数&#xff0c;但不是子类的友元函数。也就是说…

组合模式详解

1、组合模式基本介绍 1) 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;它创建了对象组的树形结构&#xff0c;将对象组合成树状结构以 表示“整体-部分”的层次关系。 2) 组合模式依据树形结构来组合对象&#xff0c;用来表示部…

UE5 武器IK瞄准系统

创建空项目 创建基础蓝图类My_GameMode&#xff0c;My_HUD&#xff0c;My_PlayChar&#xff0c;My_PlayController 项目设置地图模式 近裁平面 0.1 My_PlayChar蓝图中添加摄像机&#xff0c;角色骨骼网格体&#xff0c;武器骨骼网格体 编辑角色骨骼&#xff0c;预览控制器使用…

动静态IP地址多方面对比分析

“静态IP地址”和“动态IP地址”是互联网通信基础中的重要概念&#xff0c;两者作为IP地址分配的两种基本机制&#xff0c;各自适应不同的应用场景和需求。 我们可以从定义、地址分配机制、网络管理和运维、服务与应用兼容性等角度来分析有什么不同。 首先是定义。 从概念上来…

快速入门Tomcat服务(业务发布基础技能)

文章目录 1 Tomcat简介 2 安装tomcat 2.1 安装jdk 2.2 安装Tomcat 3 Tomcat目录结构 4 Tomcat重要配置文件 1 Tomcat简介 Tomcat是Sun公司官方推荐的Servlet和JSP容器&#xff0c;在中小型系统和并发访问用户不是很多的场合下&#xff0c;其作为轻量级应用服务…

解决无法安装“vue.volar“扩展,跟vscode版本不兼容问题

问题&#xff1a;安装volar插件的时候提示跟vscode版本不兼容 解决方案 1、进入VSCode插件市场&#xff0c;搜索Vue.volar&#xff08;直达链接&#xff1a;volar下载界面&#xff09; 2、点击download Extension&#xff08;下载插件&#xff09; 3、下载.vsix文件完成后&a…

Axure PR 9 开关切换 设计交互

大家好&#xff0c;我是大明同学。 这期内容&#xff0c;我们来探讨Axure开关按钮设计与交互技巧​。 创建切换开关所需的元件 1.打开一个新的 RP 文件并在画布上打开 Page 1。 2.将“圆形”元件拖到画布上&#xff0c;在样式窗格中将高度和宽度设置为35&#xff0c;线段宽度…

HTMLCSS练习

1) 效果如下 2) 代码如下 2.1) HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conte…

图像处理(二)——MDPI特刊推荐

特刊征稿 01 期刊名称&#xff1a; Computer Vision and Image Processing, 2nd Edition 截止时间&#xff1a; 投稿截止日期&#xff1a;2024年12月31日 目标及范围&#xff1a; 感兴趣的主题包括但不限于&#xff1a; 用于图像分类和识别的深度学习 对象检测和跟…

浙江省发规院产业发展研究所调研组莅临迪捷软件考察调研

2024年10月10日下午&#xff0c;浙江省发展与规划院产业发展研究所调研组一行莅临迪捷软件考察调研&#xff0c;绍兴市府办、区发改、区经信、迪荡街道等相关领导陪同。 调研组一行参观了迪捷软件的展厅与办公区&#xff0c;深入了解了迪捷软件的公司发展历程、运营状况、产品…

ECCV`24 | 新加坡国立华为提出Vista3D: 实现快速且多视角一致的3D生成

文章链接&#xff1a;https://arxiv.org/pdf/2409.12193 gitbub链接&#xff1a;https://github.com/florinshen/Vista3D 亮点直击 提出了Vista3D&#xff0c;一个用于揭示单张图像3D darkside 的框架&#xff0c;能够高效地利用2D先验生成多样的3D物体。开发了一种从高斯投影到…

tauri开发Mac电脑Safari浏览器一个很奇怪的问题:在 input 输入框输入的是全小写英文字母,会自动将首字母转换为大写解决办法

问题原因 在 Mac 系统中默认使用 Safari 的内核 WKWebView 作为渲染引擎&#xff0c;而 Safari 浏览器的一些 “人性化” 机制&#xff1a;如果输入框中输入的是全小写英文&#xff0c;会自动将首字母转换为大写。 解决办法 我只需要禁止这个默认的行为&#xff0c;即可解决这…

【js逆向学习】极志愿 javascript+python+rpc

JSRPC使用方式 逆向目标逆向过程逆向分析1、什么是 websocket2、websocket的原理3、总体过程3.1 环境说明3.2 python服务端代码3.3 python客户端代码 4、Sekiro-RPC4.1 执行方式4.2 客户端环境4.3 参数说明4.4 SK API4.5 python代码调试4.6 代码注入流程 逆向总结 逆向目标 网…