【初阶数据结构01】——顺序表专题

文章目录

前言

1.顺序表:数据结构的第一课

[1.1 为什么从顺序表开始?](#1.1 为什么从顺序表开始?)

[1.2 什么是顺序表?](#1.2 什么是顺序表?)

简单理解

两个关键点

[1.3 为什么需要顺序表?](#1.3 为什么需要顺序表?)

2.顺序表的两种形式

[2.1 静态顺序表:死板的收纳箱](#2.1 静态顺序表:死板的收纳箱)

[2.2 动态顺序表:智能的收纳箱](#2.2 动态顺序表:智能的收纳箱)

3.实现动态顺序表的关键操作

[3.1 初始化(使用上面创建的顺序表)](#3.1 初始化(使用上面创建的顺序表))

[3.2 顺序表的销毁](#3.2 顺序表的销毁)

[3.3 顺序表的打印](#3.3 顺序表的打印)

[3.4 顺序表的插入](#3.4 顺序表的插入)

[3.4.1 从顺序表表尾插入数据](#3.4.1 从顺序表表尾插入数据)

[3.4.2 从顺序表表头插入数据](#3.4.2 从顺序表表头插入数据)

[3.4.3 从指定位置插入](#3.4.3 从指定位置插入)

[3.5 删除指定位置的数](#3.5 删除指定位置的数)

[3.6 查找数字在顺序表中的位置](#3.6 查找数字在顺序表中的位置)

4.顺序表基本功能完整代码实现

[4.1 seqlist.h](#4.1 seqlist.h)

[4.2 seqlist.c](#4.2 seqlist.c)

[4.3 test.c](#4.3 test.c)

顺序表的优缺点

优点(为什么用顺序表)

缺点(为什么需要其他数据结构)

学习总结

核心收获

实战预告:基于顺序表实现通讯录

你将完成:

项目价值:

下一步行动


前言

我们已经完成了C语言内容的知识,现在开始数据结构的学习。这部分的学习需要我们有较为扎实的C语言编程基础,有需要可以看看这里C语言基础内容学习。然后我们将来开始数据结构第一课------顺序表的学习。

1.顺序表:数据结构的第一课

1.1 为什么从顺序表开始?

如果你问我:"数据结构这么多,为什么先从顺序表开始?"我的回答是:因为顺序表是最接近你已有知识的数据结构

你已经学过数组,而顺序表就是"会呼吸的数组":

  • 数组:固定大小,装满了就没办法

  • 顺序表:不够了可以自己扩容,用完了可以缩小

这种"进化"正是数据结构的魅力所在。

1.2 什么是顺序表?

简单理解

想象你要管理一个班级的学生名单:

  • 数组方式:预订50个座位,就算只有20个学生,也要占50个位置

  • 顺序表方式:先准备20个座位,来新学生就加椅子,有学生转学就撤椅子

顺序表 = 数组 + 智能管理

两个关键点

  1. 物理上连续:数据在内存中一个挨着一个存放

  2. 逻辑上连续:数据之间有顺序关系(第1个、第2个...)

1.3 为什么需要顺序表?

看这个例子:

cpp 复制代码
// 用数组管理学生成绩(问题版)
int scores[100];  // 固定100个位置
int count = 0;    // 记录实际有多少学生

void add_score(int score) {
    if (count < 100) {
        scores[count] = score;
        count++;
    } else {
        printf("满了!加不进去了!\n");
    }
}

问题

  • 最多只能有100个学生

  • 如果只有30个学生,浪费70个位置

  • 如果有101个学生,无法处理

顺序表的解决方案

cpp 复制代码
// 顺序表方式:动态调整大小
int* scores = malloc(10 * sizeof(int));  // 先申请10个位置
int capacity = 10;  // 当前容量
int size = 0;       // 实际使用量

void add_score(int score) {
    if (size == capacity) {  // 满了就扩容
        capacity *= 2;
        scores = realloc(scores, capacity * sizeof(int));
    }
    scores[size] = score;
    size++;
}

2.顺序表的两种形式

2.1 静态顺序表:死板的收纳箱

cpp 复制代码
//静态数据表
struct seqlist
{
	int arr[n];
	int len;//当前长度,有效数据的个数
};

特点:大小固定,不够就溢出,多了就浪费

2.2 动态顺序表:智能的收纳箱

cpp 复制代码
typedef int sldatatype;
typedef struct seqlist
{
	sldatatype* arr;
	int len;
	int capacity;
}SL;//不需要进行struct sqlist s1;这样的声明,直接SL s1即可
//typedef struct seqlist seqlist;

这里typedef int sldatatype;是为了方便数据替换。

特点:按需分配,不够就扩容,用不完可缩容

3.实现动态顺序表的关键操作

3.1 初始化(使用上面创建的顺序表)

cpp 复制代码
void seqInit(SL* sl)
{
	sl->arr = NULL;
	sl->len =sl->capacity = 0;
}

这里应该很好理解,就是把指针设为空指针,顺序表的长度与容量设为0,当然也可以将容量设置为4或者其他非零值,那就需要利用malloc函数进行对应大小的内存申请。

3.2 顺序表的销毁

cpp 复制代码
void seqDestory(SL* sl)
{
	if (sl->arr)
	free(sl->arr);
	sl->arr = NULL;
	sl->len = sl->capacity = 0;
}

这里就是利用free释放前面申请的空间,否则会发生内存泄漏。

3.3 顺序表的打印

cpp 复制代码
void seqprint(SL* sl)
{
	for (int i = 0;i < sl->len;i++)
	{
		printf("%d ", sl->arr[i]);
	}
	printf("\n");
}

为了方便观察,我们需要一个函数对顺序表进行打印。

3.4 顺序表的插入

3.4.1 从顺序表表尾插入数据
cpp 复制代码
void seqpushback(SL* sl, sldatatype data)
{
	assert(sl);
	seqcheck(sl);
	sl->arr[sl->len] = data;
	sl->len++;
}
3.4.2 从顺序表表头插入数据
cpp 复制代码
void seqpushfront(SL* sl, sldatatype data)
{
	assert(sl);
	seqcheck(sl);
	for (int i = (sl->len)-1;i > 0;i--)
	{
		sl->arr[i+1] = sl->arr[i];
	}
	sl->arr[0] = data;
	sl->len++;
}

这里需要将数据先往后移,sl->arr[i+1] = sl->arr[i];使得下标为0的位置可以插入数据,同时数据不会丢失。

3.4.3 从指定位置插入
cpp 复制代码
void seqInsert(SL* sl, int index, sldatatype data)
{
	assert(sl);
	assert(index>=0 && index<=sl->len);
	seqcheck(sl);
	for (int i = sl->len - 1;i>=index;i--)
	{
		sl->arr[i + 1] = sl->arr[i];
	}
	sl->arr[index] = data;
	sl->len++;
}

这里就是把下标为index的数全部往后移,为了防止数据的丢失,从表尾开始移。

注意:都要对顺序表的有效长度进行更新

3.5 删除指定位置的数

cpp 复制代码
void seqDelete(SL* sl, int index)
{
	assert(sl);
	assert(index >= 0 && index < sl->len);
		for (int i = index;i < sl->len - 1;i++)
		{
			sl->arr[i] = sl->arr[i + 1];
		}
		sl->len--;
}

其实就是从ndex+1开始,把前面的值用后面的值覆盖。

3.6 查找数字在顺序表中的位置

cpp 复制代码
void seqgetlen(SL* sl,sldatatype* data)
{
	assert(sl);
	for (int i = 0;i < sl->len;i++)
	{
		if (sl->arr[i] == data)
		{
			printf("找到了,所在位置数组下标为%d\n", i);
			return;
		}
	}
	printf("没有找到\n");
}

这里就是利用for循环遍历整个数组,查找对应的数。

4.顺序表基本功能完整代码实现

这里为了方便代码的观看以及修改,用3个文件进行实现

4.1 seqlist.h

cpp 复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int sldatatype;
typedef struct seqlist
{
	sldatatype* arr;
	int len;
	int capacity;
}SL;
void seqInit(SL* sl);
void seqDestory(SL* sl);
void seqpushback(SL* sl, sldatatype data);
void seqpushfront(SL* sl,sldatatype data);
void seqcheck(SL* sl);
void seqprint(SL* sl);
void seqinsert(SL* sl, int index, sldatatype data);
void seqdelete(SL* sl, int index);
void seqgetlen(SL* sl,sldatatype* data);

4.2 seqlist.c

cpp 复制代码
#include"seqlist.h"
void seqInit(SL* sl)
{
	sl->arr = NULL;
	sl->len =sl->capacity = 0;
}
void seqDestory(SL* sl)
{
	if (sl->arr)
	free(sl->arr);
	sl->arr = NULL;
	sl->len = sl->capacity = 0;
}
void seqprint(SL* sl)
{
	for (int i = 0;i < sl->len;i++)
	{
		printf("%d ", sl->arr[i]);
	}
	printf("\n");
}
void seqcheck(SL* sl)
{
	if (sl->len == sl->capacity)
	{
		int newcapacity = sl->capacity==0 ? 4:2*sl->capacity;
		sldatatype* pr = (sldatatype*)realloc(sl->arr, newcapacity * sizeof(sldatatype));
		if (pr==NULL)
		{
			perror("realloc fail!");
			exit (1);
		}
		sl->capacity = newcapacity;
		sl->arr = pr;
	}
}
void seqpushback(SL* sl, sldatatype data)
{
	assert(sl);
	seqcheck(sl);
	sl->arr[sl->len] = data;
	sl->len++;
}
void seqpushfront(SL* sl, sldatatype data)
{
	assert(sl);
	seqcheck(sl);
	for (int i = (sl->len)-1;i > 0;i--)
	{
		sl->arr[i+1] = sl->arr[i];
	}
	sl->arr[0] = data;
	sl->len++;
}
void seqInsert(SL* sl, int index, sldatatype data)
{
	assert(sl);
	assert(index>=0 && index<=sl->len);
	seqcheck(sl);
	for (int i = sl->len - 1;i>=index;i--)
	{
		sl->arr[i + 1] = sl->arr[i];
	}
	sl->arr[index] = data;
	sl->len++;
}
void seqDelete(SL* sl, int index)
{
	assert(sl);
	assert(index >= 0 && index < sl->len);
		for (int i = index;i < sl->len - 1;i++)
		{
			sl->arr[i] = sl->arr[i + 1];
		}
		sl->len--;
}
void seqgetlen(SL* sl,sldatatype* data)
{
	assert(sl);
	for (int i = 0;i < sl->len;i++)
	{
		if (sl->arr[i] == data)
		{
			printf("找到了,所在位置数组下标为%d\n", i);
			return;
		}
	}
	printf("没有找到\n");
}

4.3 test.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"seqlist.h"
void test()
{
	SL sl;
	seqInit(&sl);
	seqpushback(&sl, 1);
	seqpushback(&sl, 1);
	seqpushback(&sl, 2);
	seqpushback(&sl, 3);
	seqprint(&sl);
	seqpushfront(&sl, 4);
	seqprint(&sl);
	seqInsert(&sl, 2, 5);
	seqprint(&sl);
	seqDelete(&sl, 2);
	seqprint(&sl);
	seqgetlen(&sl, 2);
	seqDestory(&sl); 
}
int main()
{
	test();
	return 0;
}
//整体流程
//初始化 销毁 打印 尾部插入 头部插入 尾部删除 头部删除 
//指定位置插入数据 指定位置删除数据 查找某个数在顺序表中的位置
//最好写完一个模块就进行测试

顺序表的优缺点

优点(为什么用顺序表)

  1. 随机访问快:知道下标就能直接找到数据

    cpp 复制代码
    // 瞬间找到第5个数据
    int value = list.data[4];
  2. 缓存友好:数据连续存放,CPU缓存命中率高

缺点(为什么需要其他数据结构)

  1. 中间插入/删除慢:要移动大量数据

  2. 扩容成本高:重新分配内存,复制所有数据

学习总结

通过本次学习,我们掌握了顺序表这一基础数据结构。顺序表本质是"智能数组",能够动态调整大小,解决了固定数组空间浪费或不足的问题。我们实现了动态顺序表的核心功能:初始化、销毁、增删改查,并理解了其优缺点。

核心收获

  1. 理解了数据组织的思维方式:如何根据需求选择数据结构

  2. 掌握了动态内存管理:malloc/realloc/free的合理使用

  3. 培养了工程实践能力:从单个功能到完整系统的构建

实战预告:基于顺序表实现通讯录

理论需要实践验证,知识需要应用巩固。

在下一篇文章中,我们将把刚学的顺序表知识应用到实际项目------实现一个完整的通讯录系统

你将完成:

  1. 数据结构设计:用顺序表存储联系人信息

  2. 功能模块实现:添加、删除、查找、修改联系人

  3. 系统集成:将各个功能组合成完整可用的程序

项目价值:

  • 将抽象的数据结构知识转化为具体应用

  • 体验从零构建完整系统的过程

  • 为后续更复杂的数据结构学习打下坚实基础

下一步行动

准备好你的开发环境,我们将从需求分析开始,一步步构建一个真正可用的通讯录系统。这不仅是技术实践,更是思维训练------学会如何用数据结构解决实际问题。

下一篇,我们将一起完成这个项目,体验"学以致用"的成就感!

欢迎在评论区交流讨论,如果觉得有帮助,请点赞收藏支持!

更多C语言技术文章,请访问我的博客主页:莱克边澄-CSDN博客

相关推荐
rainbow688913 小时前
深入解析C++STL:map与set底层奥秘
java·数据结构·算法
wangjialelele13 小时前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先
xuxie9914 小时前
day 21 双向链表以及循环链表
数据结构·链表
历程里程碑15 小时前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
梵刹古音15 小时前
【C语言】 指针与数据结构操作
c语言·数据结构·算法
爱敲代码的TOM17 小时前
数据结构总结
数据结构
皮皮哎哟18 小时前
数据结构:嵌入式常用排序与查找算法精讲
数据结构·算法·排序算法·二分查找·快速排序
堕27419 小时前
java数据结构当中的《排序》(一 )
java·数据结构·排序算法
2302_8138062219 小时前
【嵌入式修炼:数据结构篇】——数据结构总结
数据结构