数据结构:顺序表详解

数据结构:顺序表详解

  • 一、 线性表
  • 二、 顺序表概念及结构
  • 1. 静态顺序表:使用定长数组存储元素。
  • 2. 动态顺序表:使用动态开辟的数组存储。
  • 三、接口实现
    • 1. 创建
    • 2. 初始化
    • 3. 扩容
    • 4. 打印
    • 5. 销毁
    • 6. 尾插
    • 7. 尾删
    • 8. 头插
    • 9. 头删
    • 10. 插入任意位置数据
    • 11. 删除任意位置数据
    • 12. 查找
    • 13. 修改
  • 四:所有代码


在这里插入图片描述


一、 线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的
线性表在物理上存储时,通常以数组和链式结构的形式.

在这里插入图片描述


二、 顺序表概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

1. 静态顺序表:使用定长数组存储元素。

在这里插入图片描述

2. 动态顺序表:使用动态开辟的数组存储。

在这里插入图片描述


三、接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

基本增删查改接口

//对数据管理 --- 增删查改
void SLInit(SL* ps);			//初始化
void SLDestory(SL* ps);			//释放
void SLPrint(SL* ps);        	//打印
void SLCheakCapacity(SL* ps);	//检查容量 -- 扩容//头插头删 尾插尾删
void SLPushBack(SL* ps, SLDateType x); //尾插
void SLPopBack(SL* ps);				   //尾删
void SLPushFront(SL* ps, SLDateType x);//头插
void SLPopFront(SL* ps);			   //头删//返回下标,没找到返回-1
int SLFind(SL* ps, SLDateType);		   //查找元素,返回下标//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDateType x);	//任意位置插入
//在pos位置删除x
void SLErase(SL* ps, int pos);					//任意位置删除void SLModify(SL* ps, int pos, SLDateType x);//修改

1. 创建

由于在实际工程中,项目的实现都是采用模块化进行实现的。所以在此处博主也采用了模块化的方式进行实现。
在这里插入图片描述

#pragma once#include <stdio.h>
#include <assert.h>
#include <stdlib.h>//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{SLDateType* a;//指向动态开辟的数组int size;	//有效数据的个数int capacity;//容量空间的大小
}SL;

为了后续好修改类型数据,在此采用typedef将结构体类型struct SeqList 重新命名为SL
在实际开发过程中,为了开发人员更好的输入数据,一般我们会将输入数据的数据类型重命名为SLDateType。(在本篇博客中,采用typedef将其数据类型int重命名为SLDateType


2. 初始化

初始化时,理论上我们只需要开辟一个空间并置为空指针,并将结构体中的数据全部初始化为0即可。
但在实际开发过程中,我们一般会开辟一定大小的空间(本篇博客开4个空间,但具体开多少,各位可自行选择)。

代码实现:

void SLInit(SL* ps)
{assert(ps);ps->a = (SLDateType*)malloc(4 * sizeof(SLDateType));//开辟4个空间if (ps->a == NULL){perror("malloc");exit(-1);}//开辟成功ps->capacity = 4;//开辟多少空间,容量变为多少ps->size = 0;
}

3. 扩容

在后续我们插入数据时,已开辟容量可能已经无法满足需求了。这是就需要扩容。
那一次扩到多少呢?
在实际开发过程中我们一般是扩到原有空间的两倍。(当然你也可以开1000倍,只要后台空间足够大)

代码实现:

void SLCheakCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity){//开辟空间X2SLDateType* tmp = (SLDateType*)realloc(ps->a, ps->capacity * sizeof(SLDateType) * 2);if (tmp == NULL){perror("realloc");exit(-1);}//开辟成功ps->a = tmp;ps->capacity *= 2;}
}

4. 打印

上述函数定义完成后,我们通常需要测试打印以下相关数据,来判断相关函数定义是否成功.

代码实现:

void SLPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);}printf("\n");
}

5. 销毁

由于上述空间是动态开辟的。所以当我们使用完时,要及时销毁,释放空间。

代码实现:

void SLDestory(SL* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->size = 0;
}

6. 尾插

尾插:在尾部插入一个数据。
在这里插入图片描述

但是在数据的尾部插入一个数据时,我们需要考虑一个问题:原有空间是否可以容纳新的数据,是否需要扩容。
所以我们在插入数据时,要先调用 SLCheakCapacity函数来检查是否需要扩容。

代码实现:

void SLPushBack(SL* ps, SLDateType x)
{assert(ps);SLCheakCapacity(ps);//检查是否需要扩容ps->a[ps->size] = x;ps->size++;
}

7. 尾删

尾删:删除尾部最后的一个元素。
在这里插入图片描述

但尾删同样也要考虑一个问题,空间中是否还有数据给我们删除。
所以在进行尾删时,我们可以采用assert函数断言空间中还有数据。

代码实现:

void SLPopBack(SL* ps)
{assert(ps);assert(ps->size >= 0);//断言空间中还有元素ps->size--;//下标减1
}

在删除数据时,我们不用将原有数据删除。只需要下标减1即可。
原因在于我们时根据下标来使用数据的,当下标减1后,尾部最后一个数据便无法进行访问。

Tips:

  • 越界是不一定报错的,系统对越界的检查是一种设岗抽查。
  • 以VS2022为例,微软公司在数据的开始前和结尾后的一小段空间设有一些特殊值。当程序结束或内存空间释放时,编译器就会检查这些值是否发生改变, 从而触发程序的保护机制。但如果这些值没有发生改变,即使发生越界访问,程序也不会报错。就像如果你酒驾,交警只在二环设关卡,但只要你不去二环,你就没事不会被发现。(每个编译器略有差异)

8. 头插

头插:在数据最开始地方插入数据。
在这里插入图片描述

同样,头插也要调用 SLCheakCapacity函数来检查空间是否足够,是否需要扩容。

代码实现:

void SLPushFront(SL* ps, SLDateType x)
{assert(ps);SLCheakCapacity(ps);//检查是否需要扩容//移动数据int i = ps->size-1;while (i >= 0){ps->a[i + 1] = ps->a[i];i--;}//移动数据完成,插入元素。同时有效个数加1ps->a[0] = x;ps->size++;
}

9. 头删

头删:删除数据最开始的元素。
在这里插入图片描述

思路和头插类似,只要下标从1开始,所有数据依次向前移动1位,再把有限个数减1即可。
同时头删也需要使用assert函数断言原有空间中还有数据可以删除。

代码实现:

void SLPopFront(SL* ps)
{assert(ps);assert(ps->size >= 0);//空间中还有数据可以删除//移动数据for (int begin = 1; begin < ps->size; begin++){ps->a[begin - 1] = ps->a[begin];}ps->size--;//有效个数减1
}

10. 插入任意位置数据

由于顺序表要求数据是连续存放的,所以我们只需要找到输入位置的下标pos即可。

【代码思路】:首先我们要检查输入下标是否合法,是有效下标;并检查是否有足够空间来容纳新数据,是否需要扩容。之后从输入的数据下标开始,所有元素向后移动一位,并把新数据插入到下标为pos处即可。

代码实现:

void SLInsert(SL* ps, int pos, SLDateType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);//检查下标是否合法SLCheakCapacity(ps);  //检查空间是否足够//移动数据int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];end--;}ps->a[pos] = x;ps->size++;
}

11. 删除任意位置数据

【代码思路】:和插入任何位置数据思想类似。首先我们要检查输入下标pos是否合法。之后从输入下标开始,后一个元素拷贝到前一个元素空间。

代码实现:

void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);//下标是否合法//移动数据int begin = pos+1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];begin++;}ps->size--;
}

12. 查找

【代码思路】:要查找某个元素。由于这里只是最简单的查找,我们直接暴力查找,遍历整个数组返回下标即可。更为复杂的数据查找,会有更高阶的数据结构来实现。

代码实现:

int SLFind(SL* ps, SLDateType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (x == ps->a[i])return i;}return -1;
}

13. 修改

【代码思路】: 要实现修改数据,我们只需要先判断输入下标是否合法。在将对应下标数据进行修改即可。

代码实现:

void SLModify(SL* ps, int pos, SLDateType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->a[pos] = x;
}

四:所有代码

SeqList.h源文件

#include <assert.h>
#include <stdlib.h>//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{SLDateType* a;int size;int capacity;
}SL;//对数据管理 --- 增删查改
void SLInit(SL* ps);
void SLDestory(SL* ps);
void SLPrint(SL* ps);
void SLCheakCapacity(SL* ps);//头插头删 尾插尾删
void SLPushBack(SL* ps, SLDateType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDateType x);
void SLPopFront(SL* ps);//返回下标,没找到返回-1
int SLFind(SL* ps, SLDateType);//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDateType x);
//在pos位置删除x
void SLErase(SL* ps, int pos);void SLModify(SL* ps, int pos, SLDateType x);

SeqList.c头文件

#define _CRT_SECURE_NO_WARNINGS#include "SeqList.h"void SLInit(SL* ps)
{assert(ps);ps->a = (SLDateType*)malloc(4 * sizeof(SLDateType));if (ps->a == NULL){perror("malloc");exit(-1);}//开辟成功ps->capacity = 4;ps->size = 0;
}void SLDestory(SL* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->size = 0;
}void SLPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);}printf("\n");
}void SLCheakCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity){SLDateType* tmp = (SLDateType*)realloc(ps->a, ps->capacity * sizeof(SLDateType) * 2);if (tmp == NULL){perror("realloc");exit(-1);}ps->a = tmp;ps->capacity *= 2;}
}void SLPushBack(SL* ps, SLDateType x)
{assert(ps);/*SLCheakCapacity(ps);ps->a[ps->size] = x;ps->size++;*/SLInsert(ps, ps->size, x);
}void SLPopBack(SL* ps)
{assert(ps);assert(ps->size >= 0);/*ps->size--;*/SLErase(ps, 0);
}void SLPushFront(SL* ps, SLDateType x)
{assert(ps);SLCheakCapacity(ps);//移动数据/*int i = ps->size-1;while (i >= 0){ps->a[i + 1] = ps->a[i];i--;}ps->a[0] = x;ps->size++;*/SLInsert(ps, 0, x);
}void SLPopFront(SL* ps)
{assert(ps);assert(ps->size >= 0);/*for (int begin = 1; begin < ps->size; begin++){ps->a[begin - 1] = ps->a[begin];}ps->size--;*/SLErase(ps, 0);
}int SLFind(SL* ps, SLDateType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (x == ps->a[i])return i;}return -1;
}void SLInsert(SL* ps, int pos, SLDateType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheakCapacity(ps);int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];end--;}ps->a[pos] = x;ps->size++;}void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);int begin = pos+1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];begin++;}ps->size--;
}void SLModify(SL* ps, int pos, SLDateType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->a[pos] = x;
}

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《MySQL 实战 45 讲》课程学习笔记(三)

事务隔离 事务就是要保证一组数据库操作&#xff0c;要么全部成功&#xff0c;要么全部失败。 隔离性与隔离级别 事务特性&#xff1a;ACID&#xff08;Atomicity、Consistency、Isolation、Durability&#xff0c;即原子性、一致性、隔离性、持久性&#xff09;。当数据库上…

100行代码写一个简易QT点名程序

照例演示一下: 分享一个简易的Qt点名程序&#xff0c;满打满算一百行代码&#xff08;还要什么自行车&#xff09;。 UI界面比较丑&#xff0c;按钮是自己做的&#xff0c;背景是AI作画生成的&#xff0c;大家可以自行更换背景以及按钮。 内容也是非常的简单&#xff0c;就是…

Jmeter接口/性能测试,Jmeter使用教程(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、线程组 线程组…

代码随想录算法训练营第二十二天 | 读PDF复习环节2

读PDF复习环节2 本博客的内容只是做一个大概的记录&#xff0c;整个PDF看下来&#xff0c;内容上是不如代码随想录网站上的文章全面的&#xff0c;并且PDF中有些地方的描述&#xff0c;是很让我疑惑的&#xff0c;在困扰我很久后&#xff0c;无意间发现&#xff0c;其网站上的讲…

Modbus tcp转ETHERCAT网关modbus tcp/ip协议

捷米JM-ECT-TCP网关能够连接到Modbus tcp总线和ETHERCAT总线中&#xff0c;实现两种不同协议设备之间的通讯。这个网关能够大大提高工业生产的效率和生产效益&#xff0c;让生产变得更加智能化。捷米JM-ECT-TCP 是自主研发的一款 ETHERCAT 从站功能的通讯网关。该产品主要功能是…

ARM day8 key1/2/3led

key_led.h #ifndef _KEY_H_ #define _KEY_H_#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h"//EXTI编号 typedef enum {EXTI0,EXTI1,EXTI2,EXTI3,EXTI4,EXTI5,…

设计模式-备忘录模式在Java中使用示例-象棋悔棋

场景 备忘录模式 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&#xff0c;当新的状态无效 或者存在问题时&#xff0c;可以使用暂时存储起来的备忘录将状态复原&#xff0c;当前很多软件都提供了撤销(Undo)操作&#xff0…

【Linux多线程】详解线程控制、线程分离

线程互斥与同步 &#x1f478; 理解线程&#x1f934;pthead_t&#x1f977;关于线程&#x1f9b8;‍♀️线程控制POSIX线程库线程ID及进程地址空间布局 &#x1f9b8;线程分离__thread关键字&#x1f9b8;‍♂️pthread_detach函数&#x1f9b9;‍♀️pthread_exit函数&#x…

Bean的作用域和生命周期

1. Bean的作用域 Bean作用域定义了对象实例在应用程序中的生命周期和访问范围&#xff0c;⽐如 singleton 单例作⽤域&#xff0c;就 表示 Bean 在整个 Spring 中只有⼀份&#xff0c;它是全局共享的&#xff0c;那么当其他⼈修改了这个值之后&#xff0c;那么另⼀ 个⼈读取到…

【LeetCode】102.二叉树的层序遍历

题目 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]示例 2&#xff1a; …

【小梦C嘎嘎——启航篇】类和对象(上篇)

【小梦C嘎嘎——启航篇】类和对象&#xff08;上篇&#xff09;&#x1f60e; 前言&#x1f64c;什么是面向过程&#xff1f;什么是面向对象&#xff1f;什么是类和对象类中的访问权限属性类的大小计算this 指针构造函数析构函数 总结撒花&#x1f49e; &#x1f60e;博客昵称&…

Node.js介绍;浏览器和Node.j架构区别;Node的安装与管理;JS代码执行方式;Node的输入与输出;全局对象;

目录 1_Node.js介绍1.1_概念1.2_浏览器和Node.j架构区别1.3_Node.js应用场景 2_Node的安装与管理2.1_安装2.2_Node的版本工具2.3_版本管理工具&#xff1a;n 3_JavaScript代码执行4_Node的输入与输出4.1_REPL4.2_Node程序传递参数4.3_Node的输出 5_全局对象5.1_常见的全局对象5…

FreeRTOS学习之路,以STM32F103C8T6为实验MCU(2-1:任务)

学习之路主要为FreeRTOS操作系统在STM32F103&#xff08;STM32F103C8T6&#xff09;上的运用&#xff0c;采用的是标准库编程的方式&#xff0c;使用的IDE为KEIL5。 注意&#xff01;&#xff01;&#xff01;本学习之路可以通过购买STM32最小系统板以及部分配件的方式进行学习…

大数据实时链路备战 —— 数据双流高保真压测 | 京东云技术团队

一、大数据双流建设 1.1 数据双流 大数据时代&#xff0c;越来越多的业务依赖实时数据用于决策&#xff0c;比如促销调整&#xff0c;点击率预估、广告分佣等。为了保障业务的顺利开展&#xff0c;也为了保证整体大数据链路的高可用性&#xff0c;越来越多的0级系统建设双流&…

java之juc

juc是java.util.current的简写&#xff0c;意思是并发编程。 锁是什么&#xff1f;如何判断锁的是谁&#xff1f; 生产者和消费者问题 synchronized版本 package com.demo.juc.pc;/*** 线程之间的通信问题&#xff0c;生产者和消费者问题&#xff01;* 线程交替执行** a b …

ubuntu初始化/修改root密码

1.登录ubuntu后&#xff0c;使用sudo passwd root命令&#xff0c;进行root密码的初始化/修改&#xff0c;注&#xff1a;这里需要保证两次输入的密码都是同一个&#xff0c;才可成功 ubuntugt-ubuntu22-04-cmd-v1-0-32gb-100m:~/ocr$ sudo passwd root New password: Retype…

Docker 安全 Docker HTTPS请求过程与配置

Docker 容器安全注意点 尽量别做的事 尽量不用 --privileged 运行容器&#xff08;授权容器root用户拥有宿主机的root权限&#xff09; 尽量不用 --network host 运行容器&#xff08;使用 host 网络模式共享宿主机的网络命名空间&#xff09; 尽量不在容器中运行 ssh 服务 尽…

文件按关键字分组-切割-染色-写入excel

1. 背景 针对下面的文件data.csv&#xff0c;首先根据fid进行排序&#xff0c;然后分组&#xff0c;使相同fid的记录放到同一个excel文件中&#xff0c;并对每列重复的数据元素染上红色。 fid,user_id -1000078398032092029,230410010036537520 -1000078398032092029,23042301…

Gitlab 备份与恢复

备份 1、备份数据&#xff08;手动备份&#xff09; gitlab-rake gitlab:backup:create2、备份数据&#xff08;定时任务备份&#xff09; [rootlocalhost ]# crontab -l 00 1 * * * /opt/gitlab/bin/gitlab-rake gitlab:backup:create 说明&#xff1a;每天凌晨1点备份数据…

什么是 HTTP 长轮询?

什么是 HTTP 长轮询&#xff1f; Web 应用程序最初是围绕客户端/服务器模型开发的&#xff0c;其中 Web 客户端始终是事务的发起者&#xff0c;向服务器请求数据。因此&#xff0c;没有任何机制可以让服务器在没有客户端先发出请求的情况下独立地向客户端发送或推送数据。 为…