C语言数据结构深入探索顺序表

 更新时间:2022年05月12日 08:31:46   作者:Iceevov  
大家好,今天给大家带来的是顺序表,我觉得顺序表还是有比较难理解的地方的,于是我就把这一块的内容全部整理到了一起,希望能够给刚刚进行学习数据结构的人带来一些帮助,或者是已经学过这块的朋友们带来更深的理解,我们现在就开始吧

1.顺序表的概念及结构

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

顺序表分为两类:

静态顺序表:使用定长数组储存元素

struct SeqList
{
    int data;//存储的数据
    int size;//记录数据个数
    int 1000;//给定当前顺序表的总容量为1000
};

动态顺序表:使用动态开辟的数组存储

struct SeqList
{
    int data;//存储的数据
    int size;//记录数据个数
    int capacity;//顺序表的总容量
};

2.增删查改的实现

当我们需要储存的数据数目不确定时我们使用动态顺序表更佳,所以下面就用动态顺序表来实现增删查改。

2.1扩容

首先我们动态顺序表想要实现自动扩容,当当前数据量size等于总容量capacity时我们就需要自动增容,具体就是使用malloc函数开辟一定数量的空间,假如我们设定每次扩充二倍,代码如下:

//增容
void SLCheckcapacity(SL* pc)
{
	assert(pc != NULL);
	//检查容量,满了就扩容
	if (pc->sz == pc->capacity)
	{
		//一次扩容二倍,如果初始为0就先扩容到4
		int newcapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
		//注意类型转换
		SLDatatype* tmp = (SLDatatype*)realloc(pc->data, sizeof(SLDatatype) * newcapacity);
		if (tmp == NULL)
		{
			perror("SLCheckcapacity::realloc");
			exit(-1);
		}
		//讲开辟的空间tmp给到数组
		pc->data = tmp;
		pc->capacity = newcapacity;
	}
}

2.2插入数据

插入数据时我们有三种情况,头插尾插和中间任意位置插。

2.2.1尾插

先从最简单的尾插开始,我们尾插时需要记录下当前的size,这样插入的时候就可以直接找到尾部,我们需要注意的是,我们插入之前需要判断一下当前的容量满了没有,如果满了就需要扩容,没满就可以直接插入。

//尾插
void SLPushBack(SL* pc, SLDatatype x)
{
	assert(pc != NULL);
	//需要判断是否需要增容
	SLCheckcapacity(pc);
	pc->data[pc->sz] = x;
	pc->sz++;
}

2.2.2头插

头插相对来说要复杂一点,当头上没有数据时,我们就可以看成尾插直接插入,当头上有数据时,我们为了避免数据的覆盖,需要将所有数据向后移动,再放入在头部,在我们向后移动数据时我们也需要判断是否满容了。

//头插
void SLPushFront(SL* pc, SLDatatype x)
{
	assert(pc != NULL);
	SLCheckcapacity(pc);
	//挪动数据
	int end = pc->sz - 1;
	while (end >= 0)
	{
		pc->data[end + 1] = pc->data[end];
		--end;
	}
	//插入数据
	pc->data[0] = x;
	pc->sz++;
}

2.2.3任意位置插入

我们任意位置插入时有三种情况,当在第一个位置时就是头插可以调用头插的函数,在最后一个位置时就是尾插,就调用尾插的函数,当我们在中间的时我们需要找到需要插入的位置,然后将数据从这个位置开始向后挪动,再插入进去。

//任意位置插入
void SLInsert(SL* pc, int pos, SLDatatype x)
{
	assert(pc);
    //判断pos是否在有效数据范围内
	assert(pos >= 0 && pos <= pc->sz);
	SLCheckcapacity(pc);
    //挪动数据
	int end = pc->sz - 1;
	while (end >= pos)
	{
		pc->data[end+1] = pc->data[end];
		--end;
	}
	pc->data[pos] = x;
	pc->sz++;
}

2.3删除数据

删除数据和上面的插入数据差不多,我们也需要凑够三个方面来撕开,头部删除,尾部删除,中间任意位置删除,当我们删除数据时我们只需要将这个数据后的数据依次向前挪动,覆盖住这个数据即可。这里我们不能用free函数去释放那块地址的空间来删除,因为顺序表的物理地址是连续的。链表可以,下一章会介绍。

2.3.1尾删

尾部后面没有数据那么就把最后一个数据制成0就好了

//尾删
void SLPopBack(SL* pc)
{
	assert(pc != NULL);
	//不能删多了
	assert(pc->sz > 0);
	pc->sz--;
}

2.3.2头删

将从第二个位置开始的数据往前挪动,这里需要注意,删除需要检查,以免越界。

//删除需要检查,删多了会越界
//头删
void SLPopFront(SL* pc)
{
	assert(pc != NULL);
	//检查
	assert(pc->sz > 0);
	//从第二个元素开始从后往前挪直接将其覆盖
	int begin = 0;
	while (begin <= pc->sz)
	{
		pc->data[begin-1] = pc->data[begin];
		++begin;
	}
	pc->sz--;
}

2.3.3任意位置删除

任意位置删除我们只需要将我们需要删除的位置后面的数往前挪动就行了

//任意位置删除
void SLErase(SL* pc, int pos)
{
	assert(pc != NULL);
	assert(pos >= 0 && pos < pc->sz);
	int begin = pos;
	while (begin < pc->sz-1)
	{
		pc->data[begin] = pc->data[begin + 1];
		begin++;
	}
	pc->sz--;
}

2.4查找

我们给一个数据x来表示我们想要查找的数据,从前往后把顺序表遍历一遍,当给的X等于顺序表中的X时就找到了,返回当前位置的下标。

//查找
int SLFind(SL* pc, SLDatatype x)
{
	assert(pc != NULL);
	for (int i = 0; i < pc->sz; ++i)
	{
		if (pc->data[i] == x)
		{
			return i;
		}
	}
	printf("找不到\n");
	return;
}

2.5修改数据

当我们想要修改某一个地方的数据时,直接将那个位置的数据输入新的数据覆盖掉就行了。

//改数据
void SlModify(SL* pc, int pos, SLDatatype x)
{
	assert(pc != NULL);
	if (pos >= 0 && pos <= pc->sz)
	{
		pc->data[pos] = x;
	}
	else
	{
		printf("超出范围了\n");
	}
}

2.6销毁空间

当我们顺序表使用完成过后,我们需要注意的是,我们malloc的空间并没有得到释放,可能会造成内存泄漏等问题(可参考前面的博客 '动态内存开辟' ),释放内存就需要用到free函数

//销毁空间
void SLDestory(SL* pc)
{
	if (pc->data)
	{
		free(pc->data);
		pc->data = NULL;
		pc->capacity = pc->sz = 0;
	}
}

这里就简单的给大家介绍了动态顺序表的简单功能,在这里都是封装成接口函数使用的,下一章给大家介绍链表的实现。

到此这篇关于C语言数据结构深入探索顺序表的文章就介绍到这了,更多相关C语言顺序表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Cocos2d-x中使用CCScrollView来实现关卡选择实例

    Cocos2d-x中使用CCScrollView来实现关卡选择实例

    这篇文章主要介绍了Cocos2d-x中使用CCScrollView来实现关卡的选择实例,本文在代码中用大量注释讲解了CCScrollView的使用,需要的朋友可以参考下
    2014-09-09
  • C++产生随机数的实现代码

    C++产生随机数的实现代码

    本篇文章是对C++中产生随机数的实现代码进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++面试八股文之如何实现strncpy函数

    C++面试八股文之如何实现strncpy函数

    strncpy函数,主要用做字符串复制,将于字符从一个位置复制到另一个位置,那么如何实现一个strncpy函数,下面小编就来和大家简单讲讲吧
    2023-07-07
  • C++ STL容器与函数谓词示例分析讲解

    C++ STL容器与函数谓词示例分析讲解

    这篇文章主要介绍了C++ STL容器与函数谓词示例,STL是“Standard Template Library”的缩写,中文译为“标准模板库”。STL是C++标准库的一部分,不用单独安装
    2022-11-11
  • C++开发的Redis数据导入工具优化

    C++开发的Redis数据导入工具优化

    这篇文章主要介绍了C++开发的Redis数据导入工具优化方法的相关资料,需要的朋友可以参考下
    2015-07-07
  • C语言双向链表的表示与实现实例详解

    C语言双向链表的表示与实现实例详解

    这篇文章主要介绍了C语言双向链表的表示与实现,对于研究数据结构域算法的朋友有一定的参考借鉴价值,需要的朋友可以参考下
    2014-07-07
  • C语言文件操作函数freopen详细解析

    C语言文件操作函数freopen详细解析

    替换一个流,或者说重新分配文件指针,实现重定向。如果stream流已经打开,则先关闭该流。如果该流已经定向,则freopen将会清除该定向。此函数一般用于将一个指定的文件打开一个预定义的流:标准输入、标准输出或者标准出错
    2013-10-10
  • C++稀疏矩阵的各种基本运算并实现加法乘法

    C++稀疏矩阵的各种基本运算并实现加法乘法

    今天小编就为大家分享一篇关于C++稀疏矩阵的各种基本运算并实现加法乘法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • Linux中使用VS Code编译调试C++项目详解

    Linux中使用VS Code编译调试C++项目详解

    最近因为项目的需求,需要在Linux下开发C++相关项目,经过一番摸索最终实现了,下面这篇文章就给大家简单总结了一下如何通过VS Code进行编译调试的一些注意事项。有需要的朋友们可以参考借鉴,下面来跟着小编一起看看吧。
    2016-12-12
  • C语言深入讲解宏的定义与使用方法

    C语言深入讲解宏的定义与使用方法

    在 C 语言中,可以采用命令 #define 来定义宏。该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉
    2022-04-04

最新评论