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

线性表之顺序表

 目录

  一   线性表

             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++相关知识,我们下期再见。

相关文章:

  • 《LeetCode 763. 划分字母区间 | 高效分割字符串》
  • linux-5.10.110内核源码分析 - bcm2711 pcie BAR地址分配
  • 牛客寒假训练营3
  • 芯片引脚描述或电路原理图中的Ipd、Ipu是什么意思?
  • fps武器系统6:随机弹道
  • vLLM专题(四)-故障排除
  • vue中如何动态的增减组件的类名(class)
  • OpenPose
  • 计算机世界的寻宝游戏:用C语言解密五大查找算法
  • 数据仓库与数据湖的协同工作:智慧数据管理的双引擎
  • 类和对象(5)——抽象类和接口
  • 云存储:云计算储存
  • 安卓设备调试h5页面(调试)
  • 量化交易入门指南
  • 《Stable Diffusion绘画完全指南:从入门到精通的Prompt设计艺术》-配套代码示例
  • 位运算,双指针,二分,排序算法
  • WeMos D1+PIR+Android 的小场景制作
  • freertos源码分析DAY12 (软件定时器)
  • 【第14章:神经符号集成与可解释AI—14.1 神经符号AI系统的基本原理与实现方法】
  • 一款简单的弹窗打赏页HTML源码
  • 言短意长|西湖大学首次“走出西湖”
  • 为发期刊,高校学者偷贩涉密敏感数据!国安部披露间谍案细节
  • 850亿元!2025年中央金融机构注资特别国债(一期)拟第一次续发行
  • 丹麦召见美外交官,强调“不能容忍”美在格陵兰岛间谍活动
  • 国博馆刊|北朝至唐初夏州酋豪李氏家族的发展与身份记忆
  • 巴基斯坦信德省首府卡拉奇发生爆炸