当前位置: 首页 > news >正文

数据结构(C语言):顺序表

目录

一、线性表是什么

线性表的存储结构

二、顺序表

总结



一、线性表是什么

在C语言中,线性表(Linear List)是一种最基本的数据结构之一,它是具有相同数据类型的n(n≥0)个数据元素的有限序列。线性表中的数据元素按照线性顺序排列,每个数据元素都有唯一的前驱和后继(头尾元素除外),使得数据元素之间存在一对一的线性关系。

线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

线性表的存储结构

在C语言中,线性表主要有两种存储方式:

  1. 顺序存储结构(数组)
    • 优点:随机访问速度快,存储空间连续,容易实现。
    • 缺点:插入和删除操作需要移动大量元素,可能会造成内存浪费或溢出
    • 实现:使用数组(如 int arr[100];)来存储线性表的元素。
  2. 链式存储结构(链表)
    • 优点:插入和删除操作方便,只需修改指针,不会浪费存储空间
    • 缺点:随机访问速度慢,需要额外的存储空间来存储指针。
    • 实现:使用结构体和指针来构建链表节点,每个节点包含数据域和指针域。

二、顺序表

1. 概念:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下,采用数组存储

注意:

        顺序表和数组的区别顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口

 而数组分为两种,一种是静态的,一种是动态的,导致顺序表也分为两种,底层用动态数组的就是动态顺序表,底层用静态数组的就是静态顺序表

1.动态顺序表

#define N 100
typedef int DataType;
typedef struct SqList {DataType* arr;int size;      //当前数据的长度int capaity;    //顺序表的容量
}SL;

2.静态顺序表 

#define N 100
typedef int DataType;
typedef struct SqList {DataType arr[N];int size;      //当前数据的长度int capaity;    //顺序表的容量
}SL;

 在这里,因为静态数组存在一些内存上的缺陷空间给少了就不够用,给多了就造成空间的浪费,我们就选用动态数组当作我们顺序表的底层存储结构

三、顺序表的实现

这里,我们列举了顺序表的初始化、尾插、尾删、头删、查找、插入、销毁 这8个函数来实现顺序表中涉及到的一些常用的实现方法,来满足我们平常对顺序表的一些操作。

假如,我们实例化一个顺序表s1下面的操作均争对s1展开

SL s1;

 seqList.h头文件所有的函数

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define N 100
typedef int DataType;
typedef struct SqList {DataType* arr;int size;      //当前数据的长度int capaity;    //顺序表的容量
}SL;
//1.初始化
void SLInit(SL* ps);
//2.尾插法
void SLPushBack(SL* ps, DataType e);
//3.头插法
void SLPushFront(SL* ps, DataType e);
//4.尾删
void SLPopBack(SL* ps);
//5.头删
void SLPopFront(SL* ps);
//5.查找x,查找到了就返回下标
int SLFind(SL* ps, DataType x);
//6.在指定位置插入数据
void SLInsert(SL* ps, int pos, DataType e);
//7.删除指定位置的数据
void SLErase(SL* ps, int pos);
//8.销毁
void SLDesTroy(SL* ps);

 1. 初始化顺序表 s1

//1.初始化顺序表
void SLInit(SL* ps) {ps->arr = NULL;ps->size = ps->capaity = 0;
}
void SLcheckCapacity(SL* ps) {//空间不够,先增加空间(增加的是DataType),在赋值if (ps->size == ps->capaity) {//容量=0时,容量变为4,否则 容量 = capaity * 2int newCapacity = ps->capaity == 0 ? 4 : ps->capaity * 2;//申请空间DataType* temp = (DataType*)realloc(ps->arr, newCapacity * sizeof(DataType));if (temp == NULL) {perror("realloc fail!");    //申请空间失败exit(1);}ps->arr = temp;        //将临时地址的给 ps->arrps->capaity = newCapacity;   //将新的空间大小赋值给capaity}
}

在这里,我们需要对顺序表s1进行操作,所以一级指针去接收s1的地址将s1的地址给指针ps

 

注意:这里不能用值传递,否则会创建一个临时顺序表psps是s1的临时拷贝,对ps的操作不能改变原顺序表s1 


 1.尾插法:直接在当前有效值的后面加上要插入的数据。

2.头插法:遍历当前的数据后移一位,把下标为0的位置插入数据。

尾删和头删与上图类似,这里就不画图示意了。 

//2.尾插法
void SLPushBack(SL* ps, DataType e) {SLcheckCapacity(ps);    //此时,传的是指针(地址)ps->arr[(ps->size)++] = e;
}
void SLPushFront(SL* ps, DataType e) {SLcheckCapacity(ps);for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = e;(ps->size)++;
}

3.  在指定位置插入:把这个位置之后的所有元素后移一位,然后在该位置插入数据。

//6.在指定位置插入数据
void SLInsert(SL* ps, int pos, DataType e) {assert(ps);assert(pos >= 0 && pos <= ps->size);SLcheckCapacity(ps);for (int i = ps->size; i > pos; i--) {ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = e;(ps->size)++;
}

  seqList.h头文件所有的函数的实现

  seqList.c文件

#include"SeqList.h"
//1.初始化顺序表
void SLInit(SL* ps) {ps->arr = NULL;ps->size = ps->capaity = 0;
}
void SLcheckCapacity(SL* ps) {//方法1./*if (ps == NULL) {return;}*///空间不够,先增加空间(增加的是DataType),在赋值if (ps->size == ps->capaity) {//容量=0时,容量变为4,否则 容量 = capaity * 2int newCapacity = ps->capaity == 0 ? 4 : ps->capaity * 2;//申请空间DataType* temp = (DataType*)realloc(ps->arr, newCapacity * sizeof(DataType));if (temp == NULL) {perror("realloc fail!");    //申请空间失败exit(1);}ps->arr = temp;        //将临时地址的给 ps->arrps->capaity = newCapacity;   //将新的空间大小赋值给capaity}
}
//2.尾插法
void SLPushBack(SL* ps, DataType e) {SLcheckCapacity(ps);    //此时,传的是指针(地址)ps->arr[(ps->size)++] = e;
}
void SLPushFront(SL* ps, DataType e) {SLcheckCapacity(ps);for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = e;(ps->size)++;
}
//3.尾删
void SLPopBack(SL* ps) {assert(ps && ps->size > 0);  //ps不为NULL,ps->size不为0(ps->size)--;
}
//4.头删
void SLPopFront(SL* ps) {assert(ps && ps->size > 0);for (int i = 0; i < ps->size - 1; i++) {ps->arr[i] = ps->arr[i + 1];}(ps->size)--;
}
//5.查找x,查找到了就返回下标
int SLFind(SL* ps, DataType x) {for (int i = 0; i < ps->size; i++) {if (ps->arr[i] == x)return i;}//没找到return -1;
}
//6.在指定位置插入数据
void SLInsert(SL* ps, int pos, DataType e) {assert(ps);assert(pos >= 0 && pos <= ps->size);SLcheckCapacity(ps);for (int i = ps->size; i > pos; i--) {ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = e;(ps->size)++;
}
//7.删除指定位置的数据
void SLErase(SL* ps, int pos) {assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++) {ps->arr[i] = ps->arr[i + 1];}(ps->size)--;
}
//8.销毁
void SLDesTroy(SL* ps) {if (ps->arr)      //判断地址是否合法free(ps->arr);ps->arr = NULL;ps->size = ps->capaity = 0;
}

 test.c测试文件

#include"SeqList.h"
void SLTest() {SL s1;}
int main() {SLTest();return 0;
}

总结

这里的顺序表,相关操作和数组的操作几乎一样,我们在操作原顺序表的时候,要注意存储容量是否满了,要是满了就扩容,只要是操作原顺序表就要传地址,不要传值。最后,希望各位大佬对本篇文章多多提出意见。


http://www.mrgr.cn/news/54928.html

相关文章:

  • Java 当中使用 “google.zxing ”开源项目 和 “github 的 qrcode-plugin” 开源项目 生成二维码
  • java如何部署web后端服务
  • 自动化工具:Ansible
  • Flink窗口分配器WindowAssigner
  • Vue前端开发2.2 数据绑定
  • 【ERROR】ubuntu source: not found
  • WPF 回到主线程
  • Egg.js 项目的合理 ESLint 配置文件模板
  • 锁的原理以及使用
  • 《知道做到》
  • 【MySQL核心面试题】MySQL 核心 - Explain 执行计划详解!
  • 如何用AI大模型提升挖洞速度
  • upload-labs Pass-04
  • 使用 NASM 和 Windows API 创建一个简单窗口的完整实例
  • 图幅结合表DWG转DXF,使用DXF文件进行批量影像分幅
  • 字面量优化、alignas和alignof、属性说明符和标准属性
  • Java方法的递归调用
  • 27.2 动态分片方案和它要解决的问题
  • template <typename T>详解
  • 【力扣打卡系列】滑动窗口与双指针(乘积小于K的子数组)
  • 动态规划-子数组系列——乘积最大子数组
  • 文心一言 VS 讯飞星火 VS chatgpt (373)-- 算法导论24.4 5题
  • SpringBoot3整合RocketMQ问题处理
  • Qt 实战(11)样式表 | 11.2、使用样式表
  • 单元化架构,分布式系统的新王!
  • Java学习教程,从入门到精通, Java 基础语法(4)