目录

一:课程的目标

二:需要准备的储备知识

三:数据结构的相关概念

1.什么是数据结构?

2.常见的数据结构

1. 数组

2. 链表

3. 栈

3.为什么数据结构很重要?

四:定义顺序表的结构

第1种:静态顺序表(不推荐使用)

第2种:动态顺序表(推荐使用)

优缺点对比表

五:顺序表的具体介绍

步骤:

1.顺序表初始化

2.顺序表的销毁


一:课程的目标

C语言语法基础到数据结构与算法,前面我们基本已经掌握了扎实的C语言基础,为什么要学习数据结构的课程?-----为了通讯录项目

二:需要准备的储备知识

简单的理解,通讯录具备增加、删除、修改、查找联系人等操作,要实现通讯录项目有两个技术关键

  1. C语语法基础
  2. 数据结构之顺序表和链表

三:数据结构的相关概念

1.什么是数据结构?

数据结构是计算机科学中一个非常核心的概念。简单来说,它是计算机存储、组织数据的方式。

就像生活中我们需要用合理的方式来整理物品一样(比如用衣架挂衣服、用书架摆书、用收纳盒放杂物),计算机也需要用特定的结构来管理数据,以便能高效地访问和修改它们。

一个核心公式

在计算机领域,有一个著名的公式:

程序 = 数据结构 + 算法

  • 数据结构:解决数据在计算机中如何存储和组织的问题。

  • 算法:解决数据在计算机中如何处理和计算的问题。

两者相辅相成。选择合适的数据结构,往往能让算法的效率大大提升。

生活中的类比

想象一下管理一家图书馆的藏书:

  1. 如果不使用数据结构:书买回来随便往架子上堆。结果找一本书可能需要翻遍整个图书馆,耗时极长。

  2. 如果使用数据结构:

    • 你决定按类别摆放(文学、历史、科学)——这是一种结构。

    • 在每一类里,按书名的拼音首字母排序——这是更精细的结构。

    • 你把所有书的存放位置记录在一个电子表格里——这也是结构。

有了这个结构,读者来找书,你只需要查一下电子表格,就能直接走到对应的书架位置,几秒钟就能找到。

数据结构在计算机中的作用,就是这个道理。

2.常见的数据结构

数据结构通常分为线性结构和非线性结构。以下是几种最基础的:

1. 数组

就像一排连在一起的信箱,每个信箱都有编号(索引)。

  • 特点:在内存中占据一块连续的空间。

  • 优点:查找速度快(只要知道编号,就能直接找到)。

  • 缺点:增加或删除一个数据比较麻烦(可能需要移动后面所有的数据)。

2. 链表

就像寻宝游戏,每张线索纸条上都写着下一个线索的藏匿地点。

  • 特点:数据在内存中随意存放,每个数据(节点)都附带一个“指针”,指向下一个数据的位置。

  • 优点:增加或删除数据非常灵活、快速(只需要修改指针指向)。

  • 缺点:查找数据较慢(必须从第一个开始,顺着指针一个个找下去)。

3. 栈

像一个装羽毛球的圆筒。你只能从最上面拿球,也只能把球放回最上面。

  • 特点:后进先出。

  • 应用:比如浏览器的“后退”按钮(最后看的页面,后退时最先出现)。

3.为什么数据结构很重要?
  1. 效率:处理海量数据时,不同的结构速度天差地别。用错结构,程序可能会慢到无法使用。

  2. 资源利用:好的数据结构可以节省内存空间,或者用空间换取宝贵的时间。

  3. 问题建模:现实世界的问题是复杂的,数据结构提供了在代码中表达这种复杂关系(如网络、层级关系)的工具。

总的来说,数据结构是程序设计的基础功。无论是开发操作系统、游戏,还是编写一个简单的手机App,理解和选择合适的数据结构,都是写出高效、健壮代码的关键。

四:定义顺序表的结构

第1种:静态顺序表(不推荐使用)

代码如下:

 #define N 100  //宏的定义
struct Seqlist
{
	int arr[N];
	int size; 
};
第2种:动态顺序表(推荐使用)

代码如下:

typedef int SLDataType; //重新命名*arr的类型,这里可以是 int *arr,也可以是其他的类型 ,此外SLDataType的中文意思是‘顺序表的数据类型’
typedef struct Seqlist //  Seqlist是结构体的名称,即‘顺序表’它是两个单词的简写
{
	SLDataType* arr; //SLDataType中文是‘顺序表数据类型的意思’(这里可以是 int *arr) 因为我们存入的数据类型可能是字符类型,通过指针地址可以更加应对动态空间的变化,所以这里是*arr数组
	int size;   //有效的数据个数,即我们目前要真正存入的数据
	int capacity; //空间大小,即当我们申请新的空间之后的数组空间大小

}SL; //SL是结构体,将Seqlist替换为SL结构体,不是命名
//上面这行代码 == typedef struct Seqlist SL ;  (二选一就行了)
优缺点对比表
特性静态顺序表动态顺序表
大小固定,不可改变可变,可自动扩容
内存分配编译时或早于运行前运行时动态分配
存储位置栈或全局数据区堆
执行效率高(无需考虑扩容,直接存取)稍低(扩容时有内存拷贝开销)
空间利用率容易浪费(提前申请太多)或溢出随用随取,利用率高,但需要管理
使用复杂度简单较复杂(需要实现扩容逻辑或管理内存)

总结

  • 当你明确知道数据量不会很大,且数据量基本固定不变时,可以使用静态顺序表,简单高效。

  • 当你无法预知数据规模,或者数据量变化较大时,动态顺序表是你的最佳选择,它以少量的性能开销换取了宝贵的内存灵活性和安全性。

五:顺序表的具体介绍

步骤:
  1. 顺序表初始化
  2. 顺序表的销毁
  3. 头部和尾部的插入
  4. 头部和尾部的删除

接下来我会从上面的几个步骤一一给大家进行详细的解析

(以下的所有代码都是在多个文件中进行了的,注意区分我写的代码文件是哪个,这里我们会有三个文件,一个头文件,一个链表文件,一个测试文件)

1.顺序表初始化

代码如下

Seqlist.h的文件

void SLInit(SL *ps); //传入一个参数然后进行初始化 //SLInit中文的意思是‘顺序表的初始化’

Seqlist.h的文件

//顺序表的初始化 (类似于结构体的初始化)
void SLInit(SL *ps) //这里我们给结构体的参数命名为 ps ,由于我们传过来的是sl的地址,所以我们必须用指针来接收
{
	//结构体非指针的初始化,即使用点 . 来进行解引用
	//ps.arr = NULL;
	//ps.size = 0;
	//ps.capacity = 0;
 
	//但是由于我们接收的是指针地址,所以我们必须用指针的解引用,即使用 -> 进行解引用,而不是点 .
	ps->arr = NULL;
	ps->size = 0;
	ps->capacity = 0;

}

test.c的文件

void SLTest01() //SLTest01中文意思‘顺序表的测试01’
{
	SL sl; //这里我们给SL进行取名字,即sl
	SLInit(&sl); //初始化,这里必须传递址才行,因为实参sl是传值过去,此时数据还没有初始化,所以会冲突
}
	

完整代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>


typedef int SLDataType; // 数据元素类型
typedef struct SeqList 
{
    SLDataType* arr;    // 动态数组指针
    int size;           // 当前元素个数
    int capacity;       // 当前容量
} SL;

// 现在编译器已经知道 SL 是什么了,再声明函数
void SLInit(SL* ps);

void SLInit(SL* ps) 
{   // 定义
    ps->arr = NULL;
    ps->size = 0;
    ps->capacity = 0;
}

初始化完成后的内存状态:

名称         值                             类型
sl           {arr=0x00000000...}     SL
 arr           0x00000000 <NULL>    int*
size          0                               int
capacity    0                                int

2.顺序表的销毁

代码如下:

void SLDestroy(SL* ps);

代码如下:

//顺序表的销毁 (向系统申请空间的时候,必须的释放空间,否则会导致空间泄漏)
void SLDestroy(SL* ps)
{
	if (ps->arr)  //等价于 if(ps->arr != NULL)
	{
		free(ps->arr); //释放空间
	}
	ps->arr = NULL;
	ps->size ;
	ps->capacity = 0;
}

代码如下:


void SLTest02()
{
	SL sl;
	SLInit(&sl);  // 先初始化

	SLDestroy(&sl);  // 直接销毁

	// 在这里打个断点,监视 sl 的成员变量
	// 看 arr、size、capacity 是否都变成了 0/NULL
}

完整的代码如下:

void SLDestroy(SL* ps);

//顺序表的销毁 (向系统申请空间的时候,必须的释放空间,否则会导致空间泄漏)
void SLDestroy(SL* ps)
{
	if (ps->arr)  //等价于 if(ps->arr != NULL)
	{
		free(ps->arr); //释放空间
	}
	ps->arr = NULL;
	ps->size;
	ps->capacity = 0;
}

void SLTest02()
{
	SL sl;
	SLInit(&sl);  // 先初始化

	SLDestroy(&sl);  // 直接销毁

	// 在这里打个断点,监视 sl 的成员变量
	// 看 arr、size、capacity 是否都变成了 0/NULL
}

监视结果就是:三个成员全部是 0/NULL,前后没变化。

后面的步骤我们放到下一章来给大家讲解!!!!

更多推荐