顺序表专题(C语言)

文章目录

前言

在C语言的学习中,数组和指针是必不可少的基础,但当你试图管理大量数据、构建高效程序时,单纯依赖这些工具往往力不从心。数据结构正是为了解决数据组织和操作问题而设计的"工具箱"。顺序表是最直观、最基础的一种数据结构。它的顺序存储方式类似于我们在生活中摆放物品的顺序排列,这就像在书架上排放书籍,每本书都有固定的位置。本文将以通俗易懂的方式,深入探讨顺序表的基本概念、分类及其在C语言中的实现细节,帮助你由浅入深地认识这一重要数据结构。


一、数据结构相关概念

类比说明:书架与数据结构

想象你有一个书架,上面整齐地摆着书。每本书占据一个固定的位置,这样你可以通过位置编号快速找到想要的书。这正好和数据结构中的概念类似------

  • 数据就像书籍:有内容但没有特定的组织时,查找起来会非常费力。
  • 数据结构就像书架:通过合理的组织,使数据能够高效存取。

在程序设计中,数据结构主要负责两个方面:

  1. 数据的组织:如何将数据存储到内存中,如顺序存储或链式存储。
  2. 数据的操作:提供插入、删除、查找等功能,就像你在书架上增加或拿走一本书。

掌握数据结构的本质,能够帮助你写出更高效、可扩展的代码。


二、线性表

基本概念

线性表是一种基础的数据结构,其所有元素按照线性顺序排列,每个数据元素都有一个前驱(除第一个外)和后继(除最后一个外)。生活中我们经常遇到的例子如队列、栈等,都是线性表的一种。

两种实现方式对比

线性表常见两种实现方式:顺序存储链式存储

  • 顺序存储(顺序表)

    用一段连续的内存存储数据,可以直接通过下标访问。这种方式访问速度快,但在中间插入或删除元素时,需要移动大量数据,成本较高。

  • 链式存储(链表)

    通过节点和指针链接,可以灵活插入和删除元素,但随机访问性能较低,因为需要从头开始逐一查找。

对新手来说,当数据量不大或者主要操作是查找时,顺序表是一个很好的选择,而链表则适用于频繁插入和删除的场景。


三、顺序表的概念及结构

1. 顺序表的定义

顺序表是一种通过连续内存空间存储数据的线性表实现方式。在C语言中,这通常意味着使用数组来存储元素,同时记录当前有效元素个数。

2. 静态顺序表的基本结构

下面是一个使用固定大小数组实现的简单顺序表(静态顺序表):

c 复制代码
#define MAXSIZE 100  // 定义顺序表的最大容量

typedef struct {
    int data[MAXSIZE];  // 使用数组存储顺序表元素
    int length;         // 顺序表当前包含的元素个数
} SqList;
关键点解析:
  • 连续内存 :数组在内存中是连续的,这使得随机访问(例如 data[i])非常高效。
  • 容量固定:这里数组大小固定为100,因此当数据量接近上限时无法自动扩展,这也是静态顺序表的局限。

3. 结构体成员解释

  • data:存放具体的数据元素。
  • length:记录当前顺序表中实际存放的数据个数,而不是数组的总容量。

四、顺序表分类

根据存储方式及扩展策略,顺序表可以分为以下两种类型:

类型 特点 优缺点
静态顺序表 使用固定大小的数组,实现简单,内存申请一次到位。 优点:访问快、实现简单。缺点:容量固定,不能动态扩容。
动态顺序表 利用动态内存分配(如 malloc)实现数组,根据需要自动扩容。 优点:容量灵活,适应实际需求。缺点:需要处理内存分配和释放,额外的扩容处理会影响性能。

对于实际开发,动态顺序表更为常用,因为它可以根据数据量自动扩展,避免固定容量导致的数据溢出。


五、动态顺序表的实现

这里给出源码,自己可以去写一个测试源文件,调用这些函数进行测试
sequentialtable.h

c 复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


typedef int type;
typedef struct sqlList
{
	type* arr;
	int size;
	int capacity;
}SL;
//顺序表初始化
void SLInit(SL* p);
//顺序表销毁
void DeleteSL(SL* p);

//检查空间是否充足
void CapacityCheak(SL* p);

//尾插
void PushBack(SL* p, type x);

//头插
void PushFront(SL* p, type x);

//头删
void PopFront(SL* p);

//尾删
void PopBack(SL* p);

//在任意位置插入
void SLInsert(SL* p, int pos, type x);

//在任意位置删除
void SLErase(SL* p, int pos);

//查找
int SLFind(SL* p, type x);

//打印
void Print(SL* p);

sequentialtable.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"sequentialtable.h";

void SLInit(SL* p)
{
	p->arr = NULL;
	p->size = p->capacity = 0;
}

void DeleteSL(SL* p)
{
	if (p->arr)
	{
		free(p->arr);
	}

	p->arr = NULL;
	p->size = p->capacity = 0;
}

void CapacityCheak(SL* p)
{
	
	if (p->size == p->capacity)
	{
		int newCapacity = p->capacity == 0 ? 4 : 2 * p->capacity;
		type* tmp = (type*)realloc(p->arr, newCapacity * sizeof(type));
		if (tmp == NULL)
		{
			
			perror("realloc");
			exit(1);
		}
		
		p->arr = tmp;
		p->capacity = newCapacity;
	}
}

void PushBack(SL* p, type x)
{
	assert(p);

	CapacityCheak(p);

	p->arr[p->size++] = x;
}

void PushFront(SL* p, type x)
{
	assert(p);
	CapacityCheak(p);

	for (int i = p->size; i > 0; i--)
	{
		p->arr[i] = p->arr[i - 1];
	}

	p->arr[0] = x;
	++p->size;
}

void PopFront(SL* p)
{
	assert(p);
	assert(p->arr);

	for (int i = 0; i < p->size - 1; i++)
	{
		p->arr[i] = p->arr[i + 1];
	}
	--p->size;
}

void PopBack(SL* p)
{
	assert(p);
	assert(p->arr);
	
	--p->size;
}

void SLInsert(SL* p, int pos, type x)
{
	assert(p);
	assert(pos >= 0 && pos <= p->size);

	for (int i = p->size; i > pos; i--)
	{
		p->arr[i] = p->arr[i - 1];
	}
	p->arr[pos] = x;
	++p->size;
}

void SLErase(SL* p, int pos)
{
	assert(p);
	assert(pos >= 0 && pos <= p->size);

	int i = 0;
	for (i = pos; i < p->size -1; i++)
	{
		p->arr[i] = p->arr[i + 1];
	}
	--p->size;
}

int SLFind(SL* p, type x)
{
	assert(p);

	int i = 0;
	for (i = 0; i < p->size; i++)
	{
		if (p->arr[i] == x)
		{
			return x;
		}
	}
	return -1;
}

void Print(SL* p)
{
	int i = 0;
	for (i = 0; i < p->size; i++)
	{
		printf("%d ", p->arr[i]);
	}
	printf("\n");
}

总结

本文从基础概念开始,详细解释了数据结构、线性表以及顺序表的原理和实现。主要内容包括:

  1. 数据结构概念:通过现实生活中的类比(书架)来理解数据的组织和操作原理。
  2. 线性表简介:讨论了线性表的特点以及顺序表和链表两种实现方式的优缺点。
  3. 顺序表基本结构:介绍了静态顺序表的实现方法,包括数组存储和固定容量的概念。
  4. 顺序表分类:比较了静态顺序表与动态顺序表的不同特点及各自适用场景。
  5. 动态顺序表实现:提供了详细的C语言代码示例,讲解了初始化、插入、删除和内存释放的每一步实现细节和注意事项。

对于初学者来说,理解这些概念和代码实现不仅可以帮助你更好地掌握C语言的基础知识,同时也为日后学习更高级的数据结构(如链表、树、图等)奠定坚实的基础。通过动手实践和不断调试,你将逐步体会到数据结构与算法在实际开发中的巨大威力。

希望这篇文章能让你对顺序表有一个更深入的认识,如果你有任何疑问或需要进一步讨论的地方,欢迎留言交流,持续学习和实践才是进步的关键!


如果你需要完整的代码示例、编译运行方式或更详细的图示说明,请告诉我,我会进一步提供帮助。

相关推荐
whoarethenext13 分钟前
qt的基本使用
开发语言·c++·后端·qt
atec200035 分钟前
使用uv管理python项目环境
开发语言·python·uv
是僵尸不是姜丝3 小时前
每日算法:洛谷U535992 J-C 小梦的宝石收集(双指针、二分)
c语言·开发语言·算法
小画家~3 小时前
第二十二: go与k8s、docker相关编写dockerfile
开发语言·golang·kubernetes
anlogic3 小时前
Java基础 4.12
java·开发语言
海涛高软3 小时前
qt mapFrom返回的QPoint和event->pos()区别和globalPos区别
开发语言·qt·命令模式
lauo4 小时前
智体知识库:ai-docs对分布式智体编程语言Poplang和javascript的语法的比较(知识库问答)
开发语言·前端·javascript·分布式·机器人·开源
xiegwei4 小时前
Kotlin 和 spring-cloud-function 兼容问题
开发语言·kotlin·springcloud
Alt.94 小时前
SpringMVC基础二(RestFul、接收数据、视图跳转)
java·开发语言·前端·mvc
寒页_4 小时前
2025年第十六届蓝桥杯省赛真题解析 Java B组(简单经验分享)
java·数据结构·经验分享·算法·蓝桥杯