【数据结构初阶】--- 顺序表

顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。
这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会在使用数组的时候用到。
那么,今天就带你深度剖析线性表(数组)的底层原理
这节知识只是个开胃菜,但也属于要必备的知识,所以各位同学不要松懈哦

引入

顺序表有静态的也有动态的

  • 静态顺序表,静,无非就是初始化这个静态表后,这个静态表的空间就不能变化了,例如:
 struct SeqList
* {
* 		int arr[20];
* 	    int size;
* };
  • 当初始化线性表后,这个数组存的数据就不能更改了
  • 那有人会想,我用个宏常量替换数组的大小,到时后想扩大或缩小,更改宏常量就行了
    #define N 20 int arr[N];
  • 这个想法不错,但也只能在线性表初始化之前更改,如果你初始化后,你正在使用线性表的功能时,内存不够用了怎么办

那么,接下来我所编写的代码就是动态顺序表,当你不够用内存的时候,会自动给你开辟,当看到这里,如果前面【C语言进阶】— 动态内存管理,你看过,那接下来的知识易如反掌,甚至不用看我对代码的注释就可以清楚,没看过的朋友,强烈推荐,很重要!!!数据结构会经常用到这篇的知识


1. 顺序表在内存中的存储方式

是一块连续的内存

在这里插入图片描述

2. 顺序表的定义

这里是用结构体定义了一个顺序表类型

typedef int SeqListType;//因为顺序表中存储的数据不见得都是int型,也可能是别的类型数据,所以,想存放别的数据时只需把int换成你想存的数据的类型//动态顺序表
typedef struct SeqList
{SeqListType* data;//指针指向的是SeqListType这个类型的数据int size;//记录当前有效储存数据的个数int capacity;//data开辟数组空间的容量
}SeqList;

初始化、销毁、打印功能

//初始化顺序表(开辟空间,赋初值)
void SLInit(SeqList* ps)
{//开辟一块大小为sizeof(SeqListType) * 4的内存,并把首地址传给指针ps->dataps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);if (ps->data == NULL){perror("malloc");return;}ps->size = 0;ps->capacity = 4;
}
//销毁空间,因为是在堆区开辟的数据,用完要用free函数释放
void SLDestory(SeqList* ps)
{free(ps->data);ps->data = NULL;ps->size = 0;ps->capacity = 0;
}void SLPrint(SeqList* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->data[i]);}printf("\n");
}

3. 顺序表的功能实现

扩容功能,先跳过看头插法

//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{if (ps->capacity == ps->size){//用realloc给ps->data开辟一个原来2倍的空间SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);if (new_ps != NULL)//判断返是否开辟成功,若为NULL则开辟失败{ps->data = new_ps;new_ps = NULL;ps->capacity *= 2;}else{perror("realloc");return;}}
}

3.1 头插法、尾插法

//头插法
void SLPushFront(SeqList* ps, SeqListType x)
{//检查目前容量,不足就扩容CheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->data[i + 1] = ps->data[i];}ps->data[0] = x;ps->size++;
}//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{CheckCapacity(ps);ps->data[ps->size] = x;ps->size++;
}

3.2 头删法、尾删法

//头删法
void SLPopFront(SeqList* ps)
{//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************assert(ps->size > 0);for (int i = 1; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{assert(ps != NULL);assert(ps->size > 0);ps->size--;
}

3.3 给指定位置插入数据

void SLInsert(SeqList* ps, int position, SeqListType x)
{assert(ps != NULL);//判断给的位置是否越界,若越界,程序执行完会告诉你这里出的问题assert(position >= 1 && position <= ps->size);CheckCapacity(ps);for (int i = ps->size - 1; i >= position - 1; i--){ps->data[i + 1] = ps->data[i];}ps->data[position - 1] = x;ps->size++;
}

3.4 指定位置删除数据

void SLErase(SeqList* ps, int position)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);assert(ps->size > 0);for (int i = position; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}

3.5 查找指定数据

int SLFind(SeqList* ps, SeqListType x)
{assert(ps != NULL);for (int i = 0; i < ps->size; i++){if (ps->data[i] == x)return i;}return -1;
}

4.顺序表的优缺点

4.1 优点

因为它跟数组一样可以进行下表访问,所以当你要查询数据时时间复杂度为O(1),是常数级,访问速度很快很方便,这与它的内存是一片连续的内存密切相关,因为当你访问数组下标为i的数据时,arr[i]本质是*(arr+i),首元素地址+i解引用

4.3 缺点

也正因它的内存是连续的,所以当你要删除、插入数据时必须要移动后面的数据,时间复杂度是O(N)级别的。

5 完整代码

5.1 头文件 SeqList.h 中的代码

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SeqListType;//动态顺序表
typedef struct SeqList
{SeqListType* data;int size;int capacity;
}SeqList;//初始化顺序表
void SLInit(SeqList* ps);
//销毁顺序表
void SLDestory(SeqList* ps);
//打印顺序表
void SLPrint(SeqList* ps);
//头插法插入数据
void SLPushFront(SeqList* ps, SeqListType x);
//尾插法
void SLPushBack(SeqList* ps, SeqListType x);
//头删法
void SLPopFront(SeqList* ps);
//尾删法
void SLPopBack(SeqList* ps);
//指定位置插入数据
void SLInsert(SeqList* ps, int position, SeqListType x);
//指定位置删除
void SLErase(SeqList* ps, int position);
//查找数据
int SLFind(SeqList* ps, SeqListType x);

5.2 函数实现的文件 SeqList.c

#define	_CRT_SECURE_NO_WARNINGS#include"SeqList.h"void SLInit(SeqList* ps)
{ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);if (ps->data == NULL){perror("malloc");return;}ps->size = 0;ps->capacity = 4;
}void SLDestory(SeqList* ps)
{free(ps->data);ps->data = NULL;ps->size = 0;ps->capacity = 0;
}void SLPrint(SeqList* ps)
{for (int i = 0; i < ps->size; i++){printf("%d ", ps->data[i]);}printf("\n");
}//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{if (ps->capacity == ps->size){//用realloc给ps->data开辟一个原来2倍的空间SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);if (new_ps != NULL){ps->data = new_ps;new_ps = NULL;ps->capacity *= 2;}else{perror("realloc");return;}}
}void SLPushFront(SeqList* ps, SeqListType x)
{//检查目前容量,不足就扩容CheckCapacity(ps);for (int i = ps->size - 1; i >= 0; i--){ps->data[i + 1] = ps->data[i];}ps->data[0] = x;ps->size++;}
//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{CheckCapacity(ps);ps->data[ps->size] = x;ps->size++;
}void SLPopFront(SeqList* ps)
{//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************assert(ps->size > 0);for (int i = 1; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{assert(ps != NULL);assert(ps->size > 0);ps->size--;
}void SLInsert(SeqList* ps, int position, SeqListType x)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);CheckCapacity(ps);for (int i = ps->size - 1; i >= position - 1; i--){ps->data[i + 1] = ps->data[i];}ps->data[position - 1] = x;ps->size++;
}void SLErase(SeqList* ps, int position)
{assert(ps != NULL);assert(position >= 1 && position <= ps->size);assert(ps->size > 0);for (int i = position; i < ps->size; i++){ps->data[i - 1] = ps->data[i];}ps->size--;
}int SLFind(SeqList* ps, SeqListType x)
{assert(ps != NULL);for (int i = 0; i < ps->size; i++){if (ps->data[i] == x)return i;}return -1;
}

这两套代码不能直接运行,主函数int main(){}中的内容自己写,就剩调用函数了,让自己动动手敲几行代码吧,好的程序员一定是需要实践的,尽管顺序表这一节相对不难,但里面有些边界值的判断,只有你亲手敲代码的时候才能真正进入思考,加油各位!

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

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

相关文章

c++调用动态库LNK2019无法解析的外部符号LNK1120无法解析的外部命令

严重性 代码 说明 项目 文件 行 禁止显示状态 错误 LNK1120 6 个无法解析的外部命令 ConsoleApplication1 D:\vs_qt_project\ConsoleApplication1\x64\Debug\ConsoleApplication1.exe 1 严重性 代码 说明 项目 文件 行 …

应用层——HTTP协议(自己实现一个http协议)——客户端(浏览器)的请求做反序列化和请求分析,然后创建http向响应结构

应用层&#xff1a;之前我们写的创建套接字&#xff0c;发送数据&#xff0c;序列化反序列化这些都是在写应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 之前的网络计算机是我们自定义的协议&#xff1a;传输的数据最终是什么样的结…

高级文件操作

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Python内置的os模块除了可以对目录进行操作&#xff0c;还可以对文件进行一些高级操作&#xff0c;具体函数如表4所示。 表4 os模块提供的与文件相…

【AI基础】第三步:纯天然保姆喂饭级-安装并运行chatglm2-6b

chatglm2构建时使用了RUST&#xff0c;所以在安装chatglm2之前&#xff0c;先安装RUST。 此系列文章列表&#xff1a; 【AI基础】第一步&#xff1a;安装python开发环境-windows篇_下载安装ai环境python-CSDN博客 【AI基础】第一步&#xff1a;安装python开发环境-conda篇_mini…

知识图谱的应用---智慧司法

文章目录 智慧司法典型应用 智慧司法 智慧司法是综合运用人工智能、大数据、互联网、物联网、云计算等信息技术手段&#xff0c;遵循司法公开、公平、公正的原则&#xff0c;与司法领域业务知识经验深度融合&#xff0c;使司法机关在审判、检查、侦查、监管职能各方面得到全面的…

【数据结构】图论入门

引入 数据的逻辑结构&#xff1a; 集合&#xff1a;数据元素间除“同属于一个集合”外&#xff0c;无其他关系线性结构&#xff1a;一个对多个&#xff0c;例如&#xff1a;线性表、栈、队列树形结构&#xff1a;一个对多个&#xff0c;例如&#xff1a;树图形结构&#xff1…

SpringBoot中的WebMvcConfigurationSupport和WebMvcConfigurer

在SpringBoot中可以通过以下两种方式来完成自定义WebMvc的配置&#xff1a; &#xff08;1&#xff09;继承WebMvcConfigurationSupport类 &#xff08;2&#xff09;实现WebMvcConfigurer接口 通过这两种方式完成的WebMvc配置存在差异&#xff0c;本文将对此作简单说明与区…

【Vue】单页应用程序介绍

通常基于Vue去开发一整个网站&#xff0c;开发出来的这整个网站应用&#xff0c;我们都会叫做单页应用程序 概念 单页应用程序&#xff1a;SPA【Single Page Application】是指所有的功能都在一个html页面上实现 我们可以将页面共用的部分封装成组件&#xff0c;底下要切换的也…

工具-金舟投屏软件: 手机如何投屏到电脑上 / Wi-Fi / USB

金舟安卓/iOS苹果投屏-正版软件下载中心 方法一、金舟投屏软件-wifi 1.1、准备工作 确保苹果手机和Windows电脑都连接到同一个Wi-Fi网络。 在Windows电脑上安装并打开金舟投屏软件。 1.2、操作步骤 在金舟投屏软件上选择“苹果手机投屏”功能。 在苹果手机上下滑屏幕&am…

New Work-flow of Circuit Bootstrapping

参考文献&#xff1a; [CGGI17] Chillotti I, Gama N, Georgieva M, et al. Faster packed homomorphic operations and efficient circuit bootstrapping for TFHE. ASIACRYPT 2017 (1): 377-408.[CDKS21] Chen H, Dai W, Kim M, et al. Efficient homomorphic conversion be…

dots_image 增强图像中的圆点特征

dots_image 增强图像中的圆点特征 1. dot_image 有什么用途&#xff1f;2. 点状字符的特征增强3. Halcon代码 1. dot_image 有什么用途&#xff1f; Enhance circular dots in an image. 这个算子可以增强图像中的圆点特征&#xff0c;例如下面的例子。 2. 点状字符的特征增强…

忆恒创源国产系列新品 —— PBlaze7 7A40 取得 PCI-SIG 兼容性认证

在此前报道中&#xff0c;我们曾预告了忆恒创源国产系列 PCIe 5.0 SSD 新品 —— PBlaze7 7A40&#xff0c;今天&#xff0c;这款 SSD 已经顺利通过 PCI-SIG 的严格测试并出现在 Integrators List 集成商列表当中&#xff0c;标志着距离 PBlaze7 7A40 的正式发布又近了一步。 正…

记录汇川:红绿灯与HMI-ST

项目要求&#xff1a; 子程序&#xff1a; 子程序&#xff1a; 实际动作如下&#xff1a; 红绿灯与HMI-ST

STM32项目分享:智能门禁锁系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板及元器件图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.c…

基于某评论的TF-IDF下的LDA主题模型分析

完整代码&#xff1a; import numpy as np import re import pandas as pd import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.decomposition import LatentDirichletAllocationdf1 pd.read_csv(小红书评论.csv) # 读取同目录下csv文件…

【Vue】组件化开发

文章目录 一、介绍二、根组件 App.vue 一、介绍 组件化&#xff1a;一个页面可以拆分成一个个组件&#xff0c;每个组件有着自己独立的结构、样式、行为。 好处&#xff1a;便于维护&#xff0c;利于复用 → 提升开发效率。 组件分类&#xff1a;普通组件、根组件。 根组件…

MySQL 高级 - 第十一章 | 索引优化与查询优化

目录 第十一章 索引优化与查询优化11.1 数据准备11.2 索引失效案例11.2.1 全值匹配10.2.2 最佳左前缀法则10.2.3 主键插入顺序10.2.4 计算、函数、类型转换&#xff08;自动或手动&#xff09;导致索引失效10.2.5 范围条件右边的列索引失效10.2.6 不等于&#xff08;! 或者 <…

删除目录

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 删除目录可以通过使用os模块提供的rmdir()函数实现。通过rmdir()函数删除目录时&#xff0c;只有当要删除的目录为空时才起作用。rmdir()函数的基本语…

牛客java基础(一)

A 解析 : java源程序只允许一个public类存在 &#xff0c;且与文件名同名 ; D hashCode方法本质就是一个哈希函数&#xff0c;这是Object类的作者说明的。Object类的作者在注释的最后一段的括号中写道&#xff1a;将对象的地址值映射为integer类型的哈希值。但hashCode()并不…

Linux性能优化实战

Linux性能优化实战 33 | 关于 Linux 网络&#xff0c;你必须知道这些&#xff08;上&#xff09;如何提高系统并发&#xff1f;&#xff08;8条&#xff09;如何理解分布式&#xff1f;如何理解云计算&#xff1f;如何理解微服务&#xff1f;TCP/IP 网络栈如何分层&#xff1f;…