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

线性表之顺序表

 目录

  一   线性表

             1概念:

              2分类

              3特点

二     顺序表

              1概念

              2结构

              3分类

              4静态线性表(使用定长数组存储元素

                                   4.1结构

                                  4.2 静态顺序表缺陷

              5  动态顺序表(利用动态内存管理实现内存的变化)

                                  5.1结构【因为动态顺序表的空间是变化的所以这里相当于静态顺序表多了一个用于存储空间大小的变量】    

                                  5.2动态顺序表中涉及到的方法:【初始化和销毁头/部插⼊删除 / 尾部插⼊删除///指定位置之前插⼊/删除/打印数据】         

一     线性表

          1概念:线性表是n个具有相同特性的数据元素的有限序列。线性表是⼀种在实际中⼴泛使 ⽤的数据结构。【这里的相同特性指的是在物理结构和逻辑结构】
          2分类:常⻅的线性表:顺序表、链表、栈、队列、字符串...
          3特点:线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储

二     顺序表

           1概念:顺序表是⽤⼀段物理地址连续的存储单元依次存储数据元素的线性结构,⼀般情况下采⽤数组存储。【因为数组在物理地址上是连续的】

既然顺序表是由数组组成的那么顺序表与数组的区别?

顺序表的底层结构是数组,它只是对数组进行了封装,从而实现了常⽤的增删改查等接⼝
          2结构:

         

        3分类:因为数组的大小分为固定的和可以改变大小的,所以顺序表分为静态顺序表(底层数组的大小是固定的)和动态顺序表 (底层数组的大小是可以改变大小的)。
       4静态线性表(使用定长数组存储元素
       4.1结构

这里我们分别对结构体和其中存储元素类型进行取了别名

对结构体取别名的原因:方便我们后续的调用

对结构体中存储元素类型取别名的原因:因为在后续我们不知道我们存储元素的类型如果我们存储的数据类型不是整型那么我们就要对各个涉及的类型进行更改很是麻烦

代码实现

typedef int SLDatatype;

#define N 9

typedef struct SeqList
{
	SLDatatype a[N];	//定长数组
	int size;			//有效数据个数
}SL;

4.2 静态顺序表缺陷

因为静态顺序表的空间是固定的那么存在空间给大/和给小其带来的缺陷

1空间给少:空间给少了不够⽤

2空间给多:空间给多了造成空间浪费

5  动态顺序表(利用动态内存管理实现内存的变化)
    5.1结构【因为动态顺序表的空间是变化的所以这里相当于静态顺序表多了一个用于存储空间大小的变量】

代码实现

typedef int SLDatatype;

typedef struct SeqList
{
	SLDatatype* a;
	int size;		//有效数据个数
	int capacity;	//空间容量
}SL;

5.2动态顺序表中涉及到的方法:【初始化和销毁头/部插⼊删除 / 尾部插⼊删除///指定位置之前插⼊/删除/打印数据】

这里我们一般用到三个文件一个头文件(用于函数的声明和定义)和两个源文件(分别用于函数的实现和测试)

SeqList.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//定义顺序表的结构
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* arr;
	int size;//有效数据个数
	int capacity;//空间大小
}SL;

//typedef struct SeqList SL;

//初始化
void SLInit(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType x);

//头插
void SLPushFront(SL* ps, SLDataType x);

//尾删
void SLPopBack(SL* ps);

//头删
void SLPopFront(SL* ps); 

//查找数据找到了返回下标,找不到返回小于0的数
void SLFind(SL* ps, SLDataType x);

//指定位置之前插入数据
void SLErase(SL* ps, int pos, SLDataType x);

//指定位删除数据
void SLInsert(SL* ps, int pos);

//销毁
void SLDesTroy(SL* ps);

//打印
void SLPrint(SL s);

判断空间是否满了【扩容】
void SLCheckCapacity(SL* ps);

SeqList.c

#include "SeqList.h"

//初始化这里我们要考虑的是我们传参时是传参还是传址因为我们要对顺序表中的变量值进行改变所以我们这里用传址
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}


//判断空间是否满了【扩容】因为不管是什么有关插入的操作都要判断空间是否满了
// 所以我们把它用于一个函数包装方便后续调用
void SLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)//判断空间是否满了的条件即当顺序表中的有效数据个数==总的数据个数
		                        //但是我们这里要注意一种情况(当顺序表中的有效数据个数==总的数据个数==0时所带来的问题)
	{
		int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//保证变量的个数!=0
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType));
		//这里我们需要注意几个问题
		//1用realloc扩容时第二个参数的单位是字节所以我们要用变量的个数与变量的空间大小乘积用于空间的扩容且要保证变量的个数!=0
		//2用realloc扩容时可能失败所以后续我们要检查是否扩容成功
		//3扩容的倍数扩:若每次增加的空间较小,可能导致频繁扩容,效率低下
		              //若每次开辟的空间较大,可能存在空间浪费-- > 扩容一般成倍数增加内存[一般成2倍]
		if (tmp == NULL)//检验是否扩容成功
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = NewCapacity;
	}
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);//这里我们防止顺序表是空的所以我们运用断言
	//判断空间是否满了
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;
	
}

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	++ps->size;
}

//尾删
void SLPopBack(SL* ps)
{
	assert(ps && ps->size);//这里顺序表不能为空所以我们要判断有效数据个数是否为0保证有数据可删
	--ps->size;
	/*
	不需要给ps->arr[size - 1]修改值
	ps->size--后,ps->arr[size - 1]的值并不会影响其他操作
	*/
}

//头删
void SLPopFront(SL* ps)
{
	assert(ps && ps->size);
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	--ps->size;
}

//查找 
// 找到了,返回下标没找到,返回 - 1
void SLFind(SL* ps, SLDataType x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;
		}
	}
	return -1;
}

//指定位置之前插入数据
void SLErase(SL* ps, int pos, SLDataType x)
{
	/*
	pos可以直接输入值,也可以为SLFind的返回值
	0 <= pos < size所以我们后面要判断pos的范围
	*/
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	++ps->size;
}

//指定位置删除数据
void SLInsert(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;
}

//打印
void SLPrint(SL s)
{
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.arr[i]);
	}
}

//销毁(原因记住有借不还再借就难有借有还再借就不难)
void SLDesTroy(SL* ps)
{
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
	
}

本篇文章就到此结束,欢迎大家订阅我的专栏,欢迎大家指正,希望有所能帮到读者更好了线性表之顺序表相关知识 ,觉得有帮助的还请三联支持一下~后续会不断更新C/C++相关知识,我们下期再见。


http://www.kler.cn/a/548746.html

相关文章:

  • 【2025最新计算机毕业设计】基于SpringBoot+Vue高校社团管理系统 【提供源码+答辩PPT+文档+项目部署】
  • DeepSeek专题:以专业角度详细讲讲Deepseek-R1的高质量数据合成过程⌛
  • 机试刷题_字符串的排列【python】
  • 容器运行常见数据库
  • python电影数据分析及可视化系统建设
  • 深入学习Linux命令行中的各种替换操作(命令替换、参数替换、进程替换)
  • MySQL常见错误码及解决方法(1130、1461、2003、1040、2000、1049、1062、1129、2002、1690等)
  • (萌新入门)如何从起步阶段开始学习STM32 ——2 我应该学习HAL库还是寄存器库?
  • 【自学笔记】计算机视觉基础知识点总览-持续更新
  • 使用JavaScript实现深浅拷贝
  • 单链表的概念,结构和优缺点
  • openharmony系统移植之显示驱动框架从framebuffer升级为drm(linux-5.10)
  • 【JVM系列】谈一谈JVM调优
  • 【Leetcode】解锁二分查找:突破解题瓶颈的关键技巧
  • Java和JavaScript当中的json对象和json字符串分别讲解
  • 栈的深度解析:从基础实现到高级算法应用——C++实现与实战指南
  • 清华大学×DeepSeek 使用手册 2.0:《DeepSeek如何赋能职场应用?》(文末附下载链接)
  • DeepSeek-R1论文阅读及蒸馏模型部署
  • SpringBoot:使用spring-boot-test对web应用做单元测试时如何测试Filter?
  • C++11新特性之final