C语言 超详细顺序表的模拟实现实例建议收藏

目录
  • 概念及结构
  • 接口实现
    • 1 顺序表的动态存储
    • 2 顺序表初始化
    • 3 顺序表的销毁
    • 4 顺序表的尾插
    • 5 顺序表的尾删
    • 6 顺序表的头插
    • 7 顺序表的头删
    • 8 顺序表容量的检查与扩容
    • 9 顺序表任意位置的插入
    • 10 顺序表任意位置的删除
    • 11 顺序表的打印
    • 12 顺序表元素的查找
    • 13 顺序表元素的修改

概念及结构

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

顺序表一般可以分为:

静态顺序表:使用定长数组存储元素,元素的数目无法进行修改。

//顺序表的静态存储
#define N 7
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType array[N];//定长数组
	size_t size;//有效数据的个数
}SeqList;

动态顺序表

//顺序表的动态存储
typedef struct SeqList
{
	SLDataType* array;//指向动态开辟的数组,空间不够可以增容
	size_t size;//有效数据的个数
	size_t capacity;//容量空间的大小
};

接口实现

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

1 顺序表的动态存储

typedef int SLDataType;//顺序表中存储的数据,此处假设是int型
typedef struct SeqList
{
	int* a;//指向动态开辟的数组空间,空间可以随时增容
	int size;//存储数据个数
	int capacity;//存储空间大小
}SL,SeqList;

2 顺序表初始化

void SeqListInit(SeqList* psl);//声明
void SeqListInit(SeqList* psl)
{
	assert(psl);//进行断言是因为当psl为NULL时,下面的操作将无法进行,因为空指针是无法进行解引用的。
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}//函数实现

注意:进行断言是因为当psl为NULL时,下面的操作将无法进行,因为空指针是无法进行解引用的,后面也是如此。

3 顺序表的销毁

void SeqListDestroy(SeqList* psl);
void SeqListDestroy(SeqList* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->capacity = psl->size = 0;
}

注意:free后面括号中的指针必须是malloc开辟出来的那块空间,且不能有任何的偏差(即指针不能发生移动)。

下面进行举例:

像上面这样使用是完全没有问题的,但是像下面这样进行使用就出现了问题:

tmp进行自增操作后,就指向了下图所示位置:

free的位置是tmp++后的位置,这不符合C语言的规定,且即使正常的释放掉了,前面的那一块int空间也将引起内存泄漏问题,即动态开辟的内存忘记释放。

4 顺序表的尾插

void SeqListPushBack(SeqList* psl,SLDataType x);//声明
void SeqListPushBack(SeqList* psl, SLDataType x)
{
	assert(psl);
	//如果满了,就进行扩容
	SeqListCheckCapacity(psl);
	psl->a[psl->size] = x;
	psl->size++;
}

5 顺序表的尾删

void SeqListPopBack(SeqList* psl);
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	if(psl->size > 0)
	{
		psl->size -= 1;
	}
}

6 顺序表的头插

void SeqListPushFront(SeqList* psl, SLDataType x);
void SeqListPushFront(SeqList* psl, SLDataType x)
{
	assert(psl);
	SeqListCheckCapacity(psl);
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end+1] = psl->a[end];
		--end;
	}
	psl->a[0] = x;
	psl->size++;
}

顺序表的头插会涉及到后续元素的移动,头插时要将顺序表中的元素从后面开始进行移动,因为从前面开始移动的话会出现覆盖现象。下面是图示:

7 顺序表的头删

同理,如果想要元素不被覆盖,就只能从前向后进行移动。

void SeqListPopFront(SeqList* psl);
void SeqListPopFront(SeqList* psl)
{
	//挪出数据,腾出头部空间
	//方法一:从1开始移动
	/*assert(psl);
	if (psl->size > 0)
	{
		int begin = 1;
		while (begin < psl->size)
		{
			psl->a[begin - 1] = psl->a[begin];
			begin++;
		}
		psl->size--;
	}*/
	//方法二:从0开始移动
	assert(psl);
	if (psl->size > 0)
	{
		int begin = 0;
		while (begin < psl->size - 1)
		{
			psl->a[begin] = psl->a[begin + 1];
			begin++;
		}
		psl->size--;
	}
}

下图是两种移动方式的区别:

问:为什么不可以直接将指针进行加1操作?

  • free释放空间时的指针和malloc开辟空间的指针必须相同
  • mallo开辟的空间存在浪费,即0的那块空间被浪费,无法进行使用,因为那块空间是被合法申请的。

8 顺序表容量的检查与扩容

void SeqListCheckCapacity(SeqList* psl);
void SeqListCheckCapacity(SeqList* psl)
{
	assert(psl);
	if (psl->capacity == psl->size)
	{
		size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			psl->a = tmp;
			psl->capacity *= 2;
		}
	}
}

注意点1:此处考虑使用的是如果容量不够,就将容量扩容为原容量的两倍,但是最开始的容量是0,所以要考虑到最开始为0的情况。

注意点2:要对扩容是否成功进行检测,即判断刚申请的空间是否为空。

9 顺序表任意位置的插入

void SeqListInsert(SeqList* psl,size_t pos,SLDataType x);
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);
	//较为温和的检查方式
	/*if (pos > psl->size)
	{
		exit(-1);
	}*/
	assert(pos <= psl->size);//较为暴力的检查方式
	SeqListCheckCapacity(psl);
	size_t end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end-1];
		--end;
	}
	psl->a[pos] = x;
	psl->size++;
}

注意:

问:为什么end从psl->size开始?

答:此处需要注意的是pos和end的类型,为什么呢?因为两者都是无符号类型,所以尤其注意当end到了-1的时候,就会变成一个很大的数字,此时如果以此作为下标进行访问,一定会出现越界访问内存的情况,考虑一种极端情况,当pos为0的时候,end最小的时候就是为0,所以此时不会出现越界的情况,但是如果end是从psl->size - 1开始的话,那么while循环体内的语句就变成下面这样:

while(end > pos)
{
	psl->a[end+1] = psl->a[end];
	--end;
}

最后end的最小值会变成-1,但是因为end是size_t类型,所以会变成一个很大的数字,在whle()循环条件判定时条件始终是满足的,同时在进入循环体内之后,会出现越界访问内存的操作。所以两种情况的图如下所示:

10 顺序表任意位置的删除

void SeqListErase(SeqList* psl, size_t pos);
void SeqListErase(SeqList* psl, size_t pos)
{
	assert(psl);
	assert(pos <= psl->size);
	size_t begin = pos+1;
	while (begin < psl->size)
	{
		psl->a[begin-1] = psl->a[begin];
		++begin;
	}
	psl->size--;
}

11 顺序表的打印

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

12 顺序表元素的查找

int SeqListFind(SeqList* psl,SLDataType x);
int SeqListFind(SeqList* psl, SLDataType x)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
			return i;//找到了对应元素,返回相应的下标
	}
	return -1;//说明没有找到对应的元素
}

13 顺序表元素的修改

void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
void SeqListModify(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size);
	psl->a[pos] = x;
}

到此这篇关于C语言 超详细顺序表的模拟实现实例建议收藏的文章就介绍到这了,更多相关C语言 顺序表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言 数据结构之数组模拟实现顺序表流程详解

    目录 线性表和顺序表 线性表 顺序表 静态顺序表 动态顺序表 代码已经放在Gitee上,需要可以小伙伴可以去看看 用C语言数组模拟实现顺序表 Gitee 线性表和顺序表 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列,这是我们广泛使用的数据结构. 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储. 常见的线性表:顺序表.链表.栈.队列.字符串- 顺序表 顺序表是用一段物理地址连

  • C语言实现动态顺序表的实现代码

    C语言实现动态顺序表的实现代码 顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构.线性表采用顺序存储的方式存储就称之为顺序表.顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中. 静态实现:结构体内部只需两个成员,其中一个为固定大小(MAX)的数组,用来存放我们的数据.数组大小我们可以通过在头文件中改变MAX的值来改变. 动态实现:在内存中开辟一块空间,可以随我们数据数量的增多来扩容. 来看看动态的顺序表实现: 1.seqli

  • C语言使用顺序表实现电话本功能

     简介: 用顺序表实现电话本的功能(C语言) 电话本具有如下4个功能: 1.创建一个电话本,电话本里面包含名字和电话号码 2.在指定位置插入一个名字和电话号码 3.在指定位置删除一个名字和电话号码 4.打印电话本 代码: //其中那个color函数是我为了美观加上去的,如果感觉不需要的话可以将代码中所有有关color的都删掉即可 #include <iostream> #include <cstdio> #include <cstring> #include <a

  • C语言实现的顺序表功能完整实例

    本文实例讲述了C语言实现的顺序表功能.分享给大家供大家参考,具体如下: seqlist.h #ifndef __SEQLIST_H__ #define __SEQLIST_H__ #include<cstdio> #include<malloc.h> #include<assert.h> #define SEQLIST_INIT_SIZE 8 #define INC_SIZE 3 //空间增量的大小 typedef int ElemType; typedef struc

  • C语言顺序表的实现代码

    本文实例为大家分享了C语言实现顺序表的具体代码,供大家参考,具体内容如下 seqlist.h #ifndef __SEQLIST_H__ #define __SEQLIST_H__ #include<cstdio> #include<malloc.h> #include<assert.h> #define SEQLIST_INIT_SIZE 8 #define INC_SIZE 3 //空间增量的大小 typedef int ElemType; typedef stru

  • C语言实现顺序表的基本操作指南(注释很详细)

    目录 创建一个结构体用于存放顺序表相关数据 初始化顺序表 插入元素 先检查容量是否够用 删除元素 元素修改 查找元素 排序元素 元素反转 源码 SeqList.c test.c SeqList.h 总结 创建一个结构体用于存放顺序表相关数据 #define SEQTYPE int typedef struct SeqList { SEQTYPE* data; int size; //有效数据个数 int capacity; //容量 }SeqList; 初始化顺序表 void SeqListIn

  • C语言实现动态顺序表详解

    目录 什么是顺序表? 1. 定义顺序表结构体: 2. 初始化顺序表: 3. 销毁顺序表: 4. 打印顺序表: 5. 判断容量+扩容: 6. 头插数据: 7. 尾插数据: 8. 指定下标位置插入数据: 9. 删除数据: 10. 尾删数据: 11. 指定下标位置删除数据: 12. 查找数据: 13. 修改数据: 14. 源代码: 1. SeqList.h: 2. SeqList.cpp: 3. test.cpp: 15. 测试: 总结 什么是顺序表? 顺序表是在计算机内存中以数组的形式保存的线性表,

  • 新手向超详细的C语言实现动态顺序表

    目录 一.各个函数接口的实现 1.1 不太好''李姐''的"容量检测函数" 1.2 在任意位置插入的函数"坑!" 1.3 在任意位置删除数据的函数 1.4 其余简单的接口函数 二.顺序表结构体声明与定义 三.头文件的调用 一.各个函数接口的实现 1.1 不太好''李姐''的"容量检测函数" 对顺序表进行插入数据时,需要判断顺序表的容量是否充足,增加数据的同时需要反复地检测容量,所以推荐直接将以上步骤封装成一个函数. 函数实现算法:若容量大小 ==

  • C语言 超详细顺序表的模拟实现实例建议收藏

    目录 概念及结构 接口实现 1 顺序表的动态存储 2 顺序表初始化 3 顺序表的销毁 4 顺序表的尾插 5 顺序表的尾删 6 顺序表的头插 7 顺序表的头删 8 顺序表容量的检查与扩容 9 顺序表任意位置的插入 10 顺序表任意位置的删除 11 顺序表的打印 12 顺序表元素的查找 13 顺序表元素的修改 概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储.在数组 上完成数据的增删查改. 顺序表一般可以分为: 静态顺序表:使用定长数组存储元素,元素

  • C语言 超详细顺序表的模拟实现实例建议收藏

    目录 概念及结构 接口实现 1 顺序表的动态存储 2 顺序表初始化 3 顺序表的销毁 4 顺序表的尾插 5 顺序表的尾删 6 顺序表的头插 7 顺序表的头删 8 顺序表容量的检查与扩容 9 顺序表任意位置的插入 10 顺序表任意位置的删除 11 顺序表的打印 12 顺序表元素的查找 13 顺序表元素的修改 概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储.在数组 上完成数据的增删查改. 顺序表一般可以分为: 静态顺序表:使用定长数组存储元素,元素

  • C语言超详细讲解栈与队列实现实例

    目录 1.思考-1 2.栈基本操作的实现 2.1 初始化栈 2.2 入栈 2.3 出栈 2.4 获取栈顶数据 2.5 获取栈中有效元素个数 2.6 判断栈是否为空 2.7 销毁栈 3.测试 3.1测试 3.2测试结果 4.思考-2 5.队列的基本操作实现 5.1 初始化队列 5.2 队尾入队列 5.3 队头出队列 5.4 队列中有效元素的个数 5.5 判断队列是否为空 5.6 获取队头数据 5.7 获取队尾的数据 5.8 销毁队列 6.测试 6.1测试 6.2 测试结果 1.思考-1 为什么栈用

  • C语言超详细讲解顺序表的各种操作

    目录 顺序表是什么 顺序表的结构体 顺序表的接口函数 顺序表相关操作的菜单 顺序表的初始化 添加元素 陈列元素 往最后加元素 往前面加元素 任意位置加元素 删除最后元素 删除前面元素 删除任意元素 整体代码(fun.h部分) 整体代码(fun.cpp部分) 整体代码(主函数部分) 结果展示 顺序表是什么 顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素.使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数

  • C语言 超详细模拟实现单链表的基本操作建议收藏

    目录 1 链表的概念及结构 2 链表的分类 3 链表的实现无头+单向+非循环链表增删查改实现 3.1 链表的定义 3.2 链表数据的打印 3.3 链表的尾插 3.4 链表空间的动态申请 3.5 链表的头插 3.6 链表的尾删 3.7 链表的头删 3.8 链表任意位置的前插入 3.9 链表任意位置的后插入 3.10 链表的任意位置的删除 3.11 链表的任意位置的前删除 3.12 链表的任意位置的后删除 3.13 链表的销毁 3.14 链表的总结 1 链表的概念及结构 概念:链表是一种物理存储结构

  • C语言 超详细模拟实现单链表的基本操作建议收藏

    目录 1 链表的概念及结构 2 链表的分类 3 链表的实现无头+单向+非循环链表增删查改实现 3.1 链表的定义 3.2 链表数据的打印 3.3 链表的尾插 3.4 链表空间的动态申请 3.5 链表的头插 3.6 链表的尾删 3.7 链表的头删 3.8 链表任意位置的前插入 3.9 链表任意位置的后插入 3.10 链表的任意位置的删除 3.11 链表的任意位置的前删除 3.12 链表的任意位置的后删除 3.13 链表的销毁 3.14 链表的总结 1 链表的概念及结构 概念:链表是一种物理存储结构

  • C语言 超详细介绍与实现线性表中的无头单向非循环链表

    目录 一.本章重点 二.链表介绍 三.无头单向非循环链表常用接口实现 3.1动态申请一个节点 3.2单链表打印 3.3单链表尾插 3.4单链表的头插 3.5单链表的尾删 3.6单链表头删 3.7单链表查找 3.8单链表在pos位置之前插入x 3.9单链表删除pos位置的节点 四.在线oj训练 4.1移除链表元素(力扣) 4.2反转单链表(力扣) 一.本章重点 无头单向非循环链表介绍 无头单向非循环链表常用接口实现 在线oj训练 二.链表介绍 概念:链表是一种物理存储结构上非连续.非顺序的存储结构

  • C语言超详细讲解数据结构中的线性表

    目录 前言 一.分文件编写 1.分文件编写概念 2.代码展示 二.动态分布内存malloc 1.初识malloc 2.使用方法 三.创建链表并进行增删操作 1.初始化链表 2.在链表中增加数据 3.删除链表中指定位置数据 四.代码展示与运行效果 1.代码展示 2.运行效果 总结 前言 计算机专业都逃不了数据结构这门课,而这门课无疑比较难理解,所以结合我所学知识,我准备对顺序表做一个详细的解答,为了避免代码过长,采用分文件编写的形式,不仅可以让代码干净利落还能提高代码可读性,先解释部分代码的含义,

  • C语言超详细讲解线性表

    目录 1. 顺序表 1.1 管理结点 1.2 顺序表的插入 1.3 顺序表的删除 1.4 顺序表的扩容 2. 链表 2.1 定义 2.2 头部插入 2.3 尾部插入 2.4 任意位置插入 2.5 任意位置删除 2.6 虚头结点 1. 顺序表 顺序表是指用一段连续的地址,依次存放数据元素的线性数据结构.此种存储方式使得顺序表的物理结构与逻辑结构都是连续的. 与数组的区别:函数中的数组被存放在栈段中,而栈段有系统限制的大小(可使用ulimit -s查看系统限制的大小,单位为KB),因此顺序表往往使用

随机推荐