数据结构 - 线性表(顺序表)

2023-09-22 11:26:59

线性表是什么

线性表是包含若干数据元素的一个线性序列,记为: L = (a0,…ai-1,ai,ai+1,…an-1)

  • L为表名,ai(0≤ i ≤n-1)为数据元素;
  • n为表长,n>0时,线性表 L 为非空表,否则为空表。

线性表L可用二元组形式描述(程序员间的表述):
L = (D,R)
即线性表 L 包含数据元素集合 D 和关系集合 R
D = {ai | ai ∈ datatype ,i=0,1,2, …n-1 ,n≥0}
R = {<ai, ai+1> | ai,ai+1∈D,0 ≤ i ≤ n-2}

  • 关系符<ai,ai+1>在这里称为有序对
  • 表示任意相邻的两个元素之间的一种先后关系
  • ai 是 ai+1直接前驱,ai+1 是 ai直接后继

在这里插入图片描述

线性表的特征

  • 对非空表,a0是表头,无前驱;
  • an-1是表尾,无后继;
  • 其它的每个元素 ai 有且仅有一个直接前驱 ai-1 和 一个直接后继 ai+1

线性表 - 顺序表

顺序存储结构的表示1

若将线性表 L = (a0,a1,…,an-1) 中的各元素依次存储于计算机一片连续的存储空间。

设 Loc(ai) 为 ai 的地址,Loc(a0) = b,每个元素占 d 个单元, 则:Loc(ai) = b + i*d

顺序存储结构的特点

  • 逻辑上相邻的元素 ai,ai+1,其存储位置也是相邻的
  • 对数据元素 ai 的存取为随机存取按地址存取
  • 存储密度高
    • 存储密度 D = (数据结构中元素所占存储空间)/ (整个数据结构所占空间)
  • 不足:对表的插入和删除等运算的时间复杂度较差

顺序存储结构的表示2

在 C 语言中,可借助于一维数组类型来描述线性表的顺序存储结构:

#define N 100
typedef int data_t;
typedef struct
{
	data_t data[N]; //表的存储空间;
	int last;
} sqlist, *sqlink;
	

在这里插入图片描述
上面的代码可以这么理解:

  • typedef struct sqlist 可以看作是整个图书馆,而这里的 data[N] 是图书馆里的书,结构体里的 last 就可以看作是一共有多少本书,虽然叫作 last 是因为它是顺序表里最后一个元素的下标,但是这个下标也可以标识一共有多少本书,只要可以代表总数,也可以换成别的。
  • 那为什么要用 typedef 定义变量和结构体?因为这样可以复用代码,比如这里的 data_t 可以换成 char ,也可以换成别的类型,它可以是书,也可以是餐具等等,结构体 typedef 同理

线性表的基本运算

设线性表 L = (a0,a1,…,an-1) , 对 L 的基本运算有:
1)建立一个空表:list_create(L)
2)置空表:list_clear(L)。若成功返回1,失败返回0。
3)判断表是否为空:list_empty(L)。若表为空,返回值为 0,否则返回 -1
4)求表长:length(L)
5)取表中某个元素:GetList(L,i),即ai。要求0≤ i ≤ length(L) - 1
6)定位运算:Locate(L,x)。确定元素 x 在表 L 中的位置(或序号)
L o c a t e ( L , x ) = { i 当元素 x = a i ∈ L ,且 a i 是第一个与 x 相等时 − 1 当 x 不属于 L 时 Locate(L,x)=\left\{ \begin{array}{rcl} i & & {当元素x=a_i∈L,且a_i是第一个与x相等时}\\ -1 & & {当x不属于L时}\\ \end{array} \right. Locate(L,x)={i1当元素x=aiL,且ai是第一个与x相等时x不属于L
7)插入:Insert(L,x,i)。将元素 x 插入到表 L 中第 i 个元素 ai 之前,且表长 +1。

线性表运算的实现

我们在实现时一般会写三个文件(顺序表):
1、sqlist.h 定义数据结构,定义线性表运算
2、sqlist.c 实现线性表运算
3、 test.c 实现实际功能,调 sqlist.h 接口
这样写的好处是:①结构清晰; ②代码复用性高:自己可以反复用,不管是现在的项目,还是以后的项目都可以用;同事也可以用。③给外包公司接口,实现部分给他们二进制 .so 文件,这样以后业务升级还找你

分文件编写以后怎么编译运行呢?

  • 可以一条一条的把 .c 汇编为 .o ,最后链接 .o 和 库文件 运行:
    例如:

    gcc -c sqlist.c -o sqlist.o
    
    gcc -c test.c -o test.o
    
    gcc test.o sqlist.o -o test
    
  • 利用简洁的命令进行编译执行:

    gcc *.c -o test
    
  • 利用 Makefile 进行编译

    SRC=sqlist.c
    SRC+=test.c
    
    test:$(SRC)
      gcc $^ -o $@
    
    .PHONY:clean
    
    clean:
      rm *.o
    

sqlist.c 中线性表运算具体实现

  • 创建顺序表 list_create()

    • 思路:
      1. 分配内存空间(一定要注意检查分配成功没有)
      2. 初始化
      3. 返回顺序表指针
    • 代码:
    sqlink list_create()
    {
    	//malloc
    	sqlink L;
    	L = (sqlink)malloc(sizeof(sqlist));
    	if(L == NULL)
    	{
    		printf("list malloc failed!\n");
    		return L;
    	}
    
        //init
        memset(L, 0, sizeof(sqlist));
        L->last = -1;
    
        //return
        return L;
    }
    
  • 清空顺序表 list_clear(sqlink L)

    • 思路:
      1. 先检查表原来是否是空表(如果是空表的话清空也没啥意义)(有疑问??)
      2. 然后给表空间置0,给表尾元素挪到初始位置(-1)
    • 代码:
     int list_clear(sqlink L)
     {
         if(L == NULL)
         {
              printf("the list is null!\n");
              return -1;
         }
    
     	memset(L, 0, sizeof(sqlist));
     	L->last = -1;
    
     	return 0;
     }
    
  • 判断顺序表是否为空表 list_empty(sqlink L)

    • 思路:
      1. 判断指针 L 是否为空,避免非法操作
      2. 判断表尾元素是否还在初始位置(-1),如果在初始位置证明是空表,返回 0,如果不是的话就返回 1
    • 代码:
    int list_empty(sqlink L)
    {
    	if (L == NULL)
    	{
    		printf("illegal operation!\n");
    		return -1;
    	}
    	
    	if (L->last == -1)
    	{
    		return 0;
    	}
    	else
    	{
    		return 1;
    	}
    }
    
  • 计算顺序表长度 list_length(sqlink L)

    • 思路:
      1. 判断指针 L 是否为空,避免非法操作
      2. 顺序表长度可以看作是表尾元素下标 + 1
    • 代码:
    int list_length(sqlink L)
    {
      if (L == NULL)
      {
      	return -1;
      }
      
      return (L->last + 1);
    }
    
  • 向顺序表中插入元素

    • 思路:
      1. 判断顺序表满不满,如果已经满了就不能再插入元素了
      2. 判断元素插入位置是否合法(插入位置不能小于下标 0 ,也不能大于顺序表末尾元素下标 L->last 的后一位)
      3. 如果以上两条都通过,那么开始新增元素,新增元素按照下图的箭头所示,依次从后往前的每个元素都向后移动一位给新增元素腾地方。比如插入元素位置在下标为 1 的地方,那么就先移动下标 4 的元素 9 到下标 5 ,然后移动下标 3 的元素 4 到 下标 4,······以此类推,把下标 1 的元素移动到下标 2 后,就可以进行下一步了
      4. 把要新增的值赋给数组的新增元素
      5. 末尾元素下标加一
        在这里插入图片描述
    • 代码:
    int list_insert(sqlink L, data_t value, int pos)
    {
      int i;
      if (L->last == N - 1)
      {
      	printf("the list is full!\n");
      	return -1;
      }
      
      if (pos < 0 || pos > L->last + 1)
      {
      	printf("the insertion position is invalid\n");
      	return -1;
      }
    
      for (i = L->last; i <= pos ; i--)
      {
      	L->data[i+1] = L->data[i];
      }
      
      L->data[pos] = value;
      
      L->last++;
    
      return 0;
    }
    
  • 打印顺序表 list_show(sqlink L)

    • 思路:
      1. 判断表指针 L 是否为空,避免非法操作
      2. 判断表尾元素是否在初始位置,给予空表提示
      3. 打印顺序表
    • 代码:
    int list_show(sqlink L)
    {
      int i;
       
      if (L == NULL)
      {
      	printf("invalid!\n");
      	return -1;
      }
      
      if (L->last == -1)
      {
      	printf("the list is null!\n");
      	return 0;
      }
    
      for (i = 0; i <= L->last; i++)
      {
      	printf("%d ", L->data[i]);
      }
      
      printf("\n");
    
  • 销毁顺序表 list_free(sqlink L) ,因为有时候程序并不能主动释放顺序表内存空间,一直占用内存会造成浪费

    • 思路:
      1. 判断表指针 L 是否为空,如果为空那么这个表就没有建立起来,也没必要销毁
      2. 释放 L 内存空间
      3. 把表指针 L 置空,不再让其指向这段已经被释放的内存空间
    • 代码:
    int list_delete(sqlink L)
    {
    	if (L == NULL)
    	{
    		printf("invalid!\n");
    		return -1;
    	}
    
        free(L);
        L = NULL;
        
        return 0;
    }    
    
更多推荐

【面试经典150 | 数组】跳跃游戏 II

文章目录写在前面Tag题目来源题目解读解题思路方法一:贪心写在最后写在前面本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:Tag:介绍本题牵涉到的知识点、数据结构;题目来源

软件测试/测试开发丨利用人工智能ChatGPT编写晋级报告

点此获取更多相关资料简介不管是在哪个公司,如果想要有一个长足的发展,想要获得晋升,除了平时的表现与积累,还有就是晋级答辩与晋级报告。不同的岗位,比如设计、产品、研发、测试,都有不同的答辩通道和晋级标准。一篇好的晋级报告,可以完整地体现一个人过去的工作贡献,以及未来的工作展望。而晋级报告的编写,也可以借助ChatGPT轻

微信CRM系统致力于帮助金融保险行业

在当今信息化的时代,金融保险行业面临着越来越大的竞争与挑战,那么微信CRM系统是怎么帮助金融保险行业解决问题的呢?金融保险行业面临的困难①销售管理困难,企业资源容易流失;金融保险业通过在线销售实现业务。电话销售、微信沟通难以监控管理,销售部员工流动性大,导致监管风险和客户资源流失。②客户营销困难,用户意向定位模糊;由于

《golang设计模式》第二部分·结构型模式-06-享元模式(Flyweight)

文章目录1.概述1.1角色1.2类图2.代码示例2.1设计2.2代码2.3类图示例1.概述享元(Flyweight)模式采用共享方式向客户端提供数量庞大的细粒度对象。所谓细粒度对象,是指实现了业务细节并相互独立的对象。细粒度对象是一种相对概念,一般不会进行更小粒度的拆分。1.1角色抽象享元(Flyweight):通常是

【跟小嘉学 Rust 编程】三十、Rust 使用 Slint UI

系列文章目录【跟小嘉学Rust编程】一、Rust编程基础【跟小嘉学Rust编程】二、Rust包管理工具使用【跟小嘉学Rust编程】三、Rust的基本程序概念【跟小嘉学Rust编程】四、理解Rust的所有权概念【跟小嘉学Rust编程】五、使用结构体关联结构化数据【跟小嘉学Rust编程】六、枚举和模式匹配【跟小嘉学Rust

(vue2)面经基础版-案例效果分析

配路由先配一级,一级里面配二级。一级路由:首页(二级:嵌套4个小页面)、详情页高亮a->router-link,高亮效果对自带高亮类名router-link(-exact)-active设置注:通过children配置项,可以配置嵌套子路由。并在该组件中准备路由出口<router-view></router-view>

「聊设计模式」之 设计模式的前世今生

🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!目录:一、什么是设计模式设计模式的定义设计模式的作用二、设计模式的发展历程设计模式的起源设计模式的发展阶段三、设计模式的分类创建型模式结构型模式行为型模式四、常用的设计模式工厂模式单例模式装饰器模式代理模

【SpringBoot项目】SpringBoot+MyBatis+MySQL电脑商城

在b站听了袁老师的开发课,做了一点笔记。01-项目环境搭建_哔哩哔哩_bilibili基于springboot框架的电脑商城项目(一)_springboot商城项目_失重外太空.的博客-CSDN博客项目环境搭建1.项目分析1.项目功能:登录、注册、热销商品、用户管理(密码、个人信息、头像、收货地址)、购物车(展示、增加

Promise的链式调用

catch方法.catch(onRejected)=.then(null,onRejected)链式调用then方法必定会返回一个新的Promise可理解为后续处理也是一个任务新任务的状态取决于后续处理:若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据若有后续处理但还未执行,新任务挂起。若后续处理执行

C++笔记之文档术语——将可调用对象作为函数参数

C++笔记之文档术语——将可调用对象作为函数参数相关博文:C++笔记之函数对象functors与可调用对象文章目录C++笔记之文档术语——将可调用对象作为函数参数1.在函数参数中传递可调用对象2.‘在参数中传入可调用对象’和‘将可调用对象作为函数参数’哪个描述更加专业官方?3."将可调用对象作为函数参数"是不是和‘回调

【Java 基础篇】Java网络编程基础知识详解

网络编程是现代软件开发中不可或缺的一部分,它使我们能够在不同的计算机之间实现数据传输和通信。Java作为一种强大的编程语言,提供了丰富的网络编程库,使开发者能够轻松地创建网络应用程序。本文将介绍Java网络编程的基础知识,面向初学者,详细讨论网络通信的概念、Socket编程、服务器和客户端编程等内容。1.网络通信的基本

热文推荐