前言
本文对初学嵌入式数据结构的铁铁大部分都是适用的,只对栈,队列,线性表进行阐述,其他的现阶段未考虑,学无止境啊铁铁。。。
地位:
数据结构是位于编程语言之上的,因为一个程序要依托于数据结构才能叫程序 ,否则就是“shi山”。(程序等于数据结构+算法),真是铁打的辅助,流水的C。
学习好处:
- 一方面可以增强我们自己的编程能力,比如原本对你的代码量会有很大的提升和编程思维,比如对复用思想的实践。
- 另一方面就是对于阅读别人的代码,比如RTOS的应用代码,或者底层代码,又或者是外设的驱动代码都会比之前更加简单,对于理解其更深层次的内容有莫大的好处
- 还有一方面就是对数据的管理,井然有序,效率大幅度提高
学习方法:
学习一个数据结构就是要从逻辑分类和储存方式和内存操作(增删改查,申请和释放内存,初始化等)三个方面来学习。
逻辑分类:
分线性和非线性,线性就是一对一,非线性就是一对多或者多对多。
- 线性逻辑关系:栈,队列,线性表(list)
- 非线性:树,图形。
栈(Stack):
- 栈是一种具有后进先出(LIFO)特性的数据结构。
- 栈的基本操作包括压栈(Push)和弹栈(Pop),分别用于将元素放入栈顶和从栈顶移除元素。
- 除了基本的压栈和弹栈操作,栈还常常包括其他一些辅助操作,比如查看栈顶元素(Top)以及判断栈是否为空等。
- 栈常用于实现函数调用、表达式求值、递归算法等。
队列(Queue):
- 队列是一种具有先进先出(FIFO)特性的数据结构。
- 队列的基本操作包括入队(Enqueue)和出队(Dequeue),分别用于将元素放入队尾和从队头移除元素。
- 类似于栈,队列也包括其他一些辅助操作,比如查看队头元素和队尾元素以及判断队列是否为空等。
- 队列常用于实现广度优先搜索(BFS)、缓冲区等场景。
线性表(List):
- 线性表是一种由一系列按照顺序排列的元素组成的数据结构。
- 线性表的基本操作包括插入(Insert)、删除(Delete)、查找(Search)等,可以根据元素的位置进行操作。
- 线性表可以是顺序存储结构(比如数组)或者链式存储结构(比如链表)。
- 线性表常用于存储一系列元素,并提供了丰富的操作来对这些元素进行管理
以下是逻辑非线性 ,本文暂时不考虑
树(Tree):
- 树是一种由节点和边组成的层次结构,通常有一个根节点,并且每个节点最多有一个父节点和多个子节点。
- 树的节点之间的关系是有向的,并且不存在环路。
- 树常用于模拟层次关系,比如文件系统、组织结构等。
- 树的一些重要术语包括根节点(Root)、父节点(Parent)、子节点(Child)、叶节点(Leaf)、深度(Depth)、高度(Height)等。
图(Graph):
- 图是由节点(顶点)和边组成的集合,节点之间的关系可以是任意的。
- 图可以分为有向图和无向图,有向图的边是有方向的,而无向图的边是没有方向的。
- 图可以是连通的(Connected)或者非连通的,连通图中任意两个节点之间都存在路径。
- 图常用于模拟网络、社交关系、交通网络等。
- 图的一些重要概念包括度数(Degree)、路径(Path)、环路(Cycle)、连通分量(Connected Component)等。
储存方式分:
对顺序储存和链式储存着重阐述
顺序储存:开辟类似数组的内存空间(地址连续)在任意位置都可以进行内存管理进而完成对指定位置的数据进行管理。
优点:
- 存储密度高:由于数据元素是连续存放的,没有额外的指针或链接,因此存储密度高,节省了存储空间。
- 访问效率高:由于数据元素的地址是连续的,因此可以通过简单的计算直接访问指定位置的元素,访问效率高。
缺点:
- 插入和删除效率低:由于顺序存储需要保持数据元素的连续性,因此在插入和删除元素时,需要移动大量的元素,导致效率低下。
- 内存浪费:在顺序存储中,如果预留了固定大小的空间,而实际存储的元素数量较少,就会造成内存浪费。
链式储存(单双):首先是首节点的创建,然后一个节点包含data数据和节点next两部分,每个节点的节点指针(next)指向下一个节点的地址(就是整个节点),以此往后。值得注意的是最后一个节点的节点指针是NULL(空指针)。所以你可以看到,只要给你一个头结点,就可以根据next依次往下找到各个节点块了。注意其内存分配不是连续的
优点:
- 灵活性高:链式存储可以动态地分配和释放内存空间,因此具有灵活性,能够更好地适应数据结构的变化。
- 插入和删除效率高:相比顺序存储,链式存储在插入和删除元素时不需要移动大量的元素,只需要改变指针的指向,因此插入和删除效率较高。
- 内存利用率高:链式存储可以根据需要动态地分配内存空间,不会造成内存浪费,因此内存利用率较高。相对于顺序存储,因为顺序存储是固定的,可能存在用不完的情况,但是链式存储就不用担心这个,因为其是按照一个个的节点来动态释放和分配内存。
缺点:
- 存储密度低:链式存储每个数据元素都需要额外的指针来指向下一个元素的地址,因此存储密度较低,会消耗额外的内存空间。
- 访问效率低:由于链式存储中数据元素的地址不是连续的,因此无法像顺序存储那样通过简单的计算直接访问指定位置的元素,而是需要通过遍历链表的方式访问,导致访问效率较低。
- 不支持随机访问:链式存储中无法像数组那样通过下标直接访问指定位置的元素,只能通过顺序遍历或者按照特定规则查找访问元素,因此不支持随机访问。
索引储存:类似于书的目录的索引效果,其核心思想是通过建立索引结构来提高数据的检索效率。在索引存储中,会额外建立一个或多个索引结构,这些索引记录了数据项的位置信息或者关键属性,以便快速定位和访问数据。常见的索引结构包括B树、B+树、哈希表等。
优点:
- 快速检索:索引存储通过建立索引结构,可以快速定位到所需数据的位置,从而提高检索效率。
- 支持随机访问:由于索引可以直接指向数据的位置,因此支持随机访问,不需要遍历整个数据集。
- 灵活性高:索引存储可以根据需要建立不同类型的索引,以适应不同的查询需求,具有较高的灵活性。
缺点:
- 占用额外存储空间:索引存储需要额外的空间来存储索引结构,占用了一定的存储空间。
- 维护成本高:当数据发生变化时,索引结构需要及时更新以保持与数据的一致性,这会增加维护成本。
- 索引可能失效:如果索引结构设计不合理或者索引数据过于庞大,可能导致索引失效或者性能下降的问题
散列储存:就是存储的空间可以按照某种函数计算出来。使用散列函数将数据映射到存储空间的特定位置,以便快速地定位和访问数据。散列存储的核心思想是将数据项直接映射到一个固定大小的存储空间中,而不是通过索引结构来进行间接查找。
优点:
- 快速的数据访问:由于数据项被直接映射到存储空间的特定位置,因此可以实现快速的数据插入、查找和删除操作。
- 空间利用率高:相比于其他存储方式,散列存储通常可以更有效地利用存储空间,因为它不需要额外的索引结构。
- 适用于大规模数据集:散列存储适用于处理大规模数据集,因为它的性能不会随着数据规模的增加而显著下降。
缺点:
- 冲突处理复杂:由于散列函数的映射可能导致多个数据项映射到同一个位置,因此需要额外的冲突处理策略,例如链地址法或者开放定址法,增加了实现和维护的复杂性。
- 散列函数设计困难:选择合适的散列函数对于散列存储的性能至关重要,但是设计一个良好的散列函数并不是一件简单的事情,需要考虑数据分布的特点、散列值的均匀性等因素。
- 不支持范围查询:与索引存储不同,散列存储不支持范围查询操作,因为它只能直接定位到特定的数据项,无法按照顺序扫描存储空间。
内存操作:
增删改查,释放分配内存,初始化等。
代码
顺序表sqlist(线性表-顺序储存)
顺序表的功能测试在main函数哪里(想看结果的直接跳main)
就是线性表按照顺序结构进行储存
.h文件
先来sqlist.h文件,这样比较直观一点,可以看到这里.h文件主要是对各种基层功能进行定义,和宏定义预留接口(复用思想)。
#include<stdio.h>
#include<stdlib.h>
typedef int data_t;//自由定义data,如果后来data_t后来换成别的不再是int类型的变量,可以很方便的换,相当于留了一个接口(复用思想)
#define N 5
#define LOCATE_N 10
//定义结构体存放list_locate函数中查找顺序表与value相同的下标
typedef struct
{
int locate[LOCATE_N];//存放相同元素的下标
int same;//用于存放相同元素的个数
}Int_ARRay,*int_arrary;
typedef struct
{
data_t data[N];
int last;//指向顺序表当前的数据
} sqlist,*sqlink;
//有关参数问题都可以查看list_insert函数实现
//创建线性表
sqlink list_creat(void);
//释放空间
int list_free(sqlink L);
//判断是否空
int list_empty(sqlink L);
//判断是否满
int list_full(sqlink L);
//长度查询
int list_lenth(sqlink L);
//增 数据加一
int list_insert(sqlink L,int value,int pos);
//删 全删
int list_claer(sqlink L);
//删 删单个的 且长度减一
int list_delete(sqlink L,int pos);
//删 删除相同的元素,只保留一个
int list_delete_same(sqlink L);
//释放 Int_ARRay的内存
int Int_ARRary_free(int_arrary INT_arr);
//查 全查
int list_show(sqlink L);
//查 查单个的
int_arrary list_locate(sqlink L,int value);
//改
int list_change(sqlink L,int value,int pos);
.c文件
sqlist.c文件:对于.c文件来说就是各种基层功能的实现过程了。 我们先一个个分析然后给出整体.c代码
list_creat
首先就是创建线性表,也就是分配固定内存。详细的解释都在注释里面了,这里不在赘述,整体就是分配一块连续存储的内存,但是值得注意的是:这里初始化的时候last下标是从-1开始的,这就表示其创造了一个顺序表,但是下标是-1,不指向任何一个单元,因此初始化的是一个空表。如果last是0则表示初始化第一个位置的数据是0,这就不是空表了,不利于后续操作
示意图:
/*
*参数:无
*功能:创建线性表
*函数实现步骤
*1.申请内存
*2.初始化
*3.返回 (1)下标-1代表是空表(下标是0到N之间的数,表示表都有值因此选择-1)
这时返回NULL
* (2)返回L表示创建成功
*/
sqlink list_creat(void)
{
sqlink L;
L=(sqlink)malloc(sizeof(sqlist));//malloc前面加强制转换符号“(sqlink)”,使得void*转换为sqlist*类型
if(L==NULL)
{
printf("sqlist creat fieled\r\n");
return L;
}
//初始化
memset(L,0,sizeof(sqlist));//把从L地址开始的占sizeof(sqlist)字节的内存用0填充
L->last=-1;//初始坐标得从-1开始,如果是0,就表示第一个数字初始化即为0了
//返回
return L;
}
list_free
list_empty
list_full
list_lenth
接下来:释放空间,判断是否空表,判断是否满表,表的长度这四个函数一起分析.
- list_free:刚开始如果表都没创建成功,就是返回-1表示释放失败。接下来释放内存,指针L指向NULL,返回0表示释放成功。
- list_empty:下标为-1表示空表,有人可能要说了,表中的数字都是0不能表示空表吗?但是这样就和表中的数字为0,这两种情况就造成逻辑混淆了,因此下表为-1表示空表,而且还记得刚开始创建表的时候的逻辑了吗,这时候要逻辑一致。
- list_full:如果指针last加一等于N(数据长度)表示表满
- list_lenth:同上,表的长度就是last+1。而且在一开始last是-1的时候返回的是0,符合逻辑。
//释放空间
int list_free(sqlink L)
{
if(L==NULL)
return -1;
free(L);
L=NULL;
return 0;
}
/*功能:下标为-1表示空表
*返回:1 空,0 非空 表都创建失败
*/
int list_empty(sqlink L)
{
if(L==NULL)
return -1;
if(L->last==-1)
return 1;
return 0;
}
//判断是表否满
//返回值:1满 0未满
int list_full(sqlink L)
{
if(L->last+1==N)
return 1;
return 0;
}
/*
功能:返回表的长度
返回:-1失败,正常为返回长度
*/
int list_lenth(sqlink L)
{
if(L==NULL)
return -1;
return (L->last+1);
}
list_insert
整体步骤:
- 先判断表是否空表
- 接着判断pos的位置是否属于[0,last+1]范围内,因为是顺序存储,所以存储的时候要一个接一个的存储。关于这个last+1,能取这个数字是因为,这是顺序存储可以在last后一个位置插入数据
- 然后就是在要插入元素的pos位置后面的元素,即[pos,last]下标的元素整体往后移动一个单位,然后再pos位置换成value。注意这里是从最后面的last元素开始移动,和后面的list_delete删除函数区分开来
- 最后也是最重要,也是最容易忘记的步骤就是last指针要加1
插满元素示意图:
/*
参数:结构体指针,插入的值,插入的位置
功能:在[0,last+1]范围内插入一个数字,注意:如果表满了则插不了内容了
返回值:-1 插入失败,0 插入成功
*/
int list_insert(sqlink L,int value,int pos)
{
int i=0;
//判断是否full?
if(list_full(L)==1)
return -1;
//pos范围是[0,last+1]
if(pos>(L->last+1)||pos<0)
{
printf("pos not in range\r\n");
return -1;
}
//pos位置后面的元素均往后面移动(从最后一个元素开始往后移动)
for(i=(L->last);i>=pos;i--)
{
L->data[i+1]=L->data[i];
}
//pos位置换上 value
L->data[pos]= value;
//勿忘last++ !!!!
L->last++;
return 0;
}
list_claer
list_delete
接下来描述 list_claer和list_delete
list_claer:全清0,下标返回-1
list_delete:和上面的插入函数list_insert类似
- 先判断表是否空表和表是否创建成功
- 接着判断pos的位置是否属于[0,last]范围内
- 然后就是在要删除元素的pos位置后面的元素,即[pos+1,last]下标的元素整体往前移动,然后pos+1位置上的元素就覆盖掉pos位置上的元素了。注意这里是从最前面的pos+1元素开始往前面移动一个单位
- 最后也是最重要,也是最容易忘记的步骤就是last指针要减一
示意图:
/*功能:全清0 返回下标-1表示空表
返回:-1清空失败 0表示清空成功*/
int list_claer(sqlink L)
{
if(L==NULL)
return -1;
//清零
memset(L,0,sizeof(sqlist));
L->last=-1;
return 0;
}
/*
功能:删除[0,last+1]范围内的一个数字,然后表的长度减一,具体实现和insert类似
返回值:-1 删除失败,0 删除成功
*/
int list_delete(sqlink L,int pos)
{
int i=0;
if(L==NULL)
{
printf("L is NULL\r\n");
return -1;
}
//判断是否为空
if(L->last==-1)
{
printf("L is empty\r\n");
return -1;
}
if(pos>L->last||pos<0)
{
printf("pos not in range\r\n");
return -1;
}
//[pos+1,last]位置往前整体移动一个单位,最左边的(pos+1)往左边移动
for(i=pos+1;i<=L->last;i++)
{
L->data[i-1]=L->data[i];
}
//因为上面已经覆盖了所以不用赋值了
//更新last !!!1
L->last--;
return 0;
}
list_show
list_change
- list_show:for循环里面依次打印
- list_change:value替换L->data[pos]
/*
参数:表的结构体指针
功能:表的整体显示出来
*/
int list_show(sqlink L)
{
for(int i=0;i<=L->last;i++)
printf("%d ",L->data[i]);
return 0;
}
/*
功能:把某一个新元素替换原来的旧元素
返回值:-1修改失败,0修改成功
*/
int list_change(sqlink L,int value,int pos)
{
if(L==NULL||L->last==-1)
return -1;
L->data[pos]=value;
return 0;
}
最后说这三个函数:
list_locate
- 首先返回类型是int_arrary,这是定义了一个线性表(指针类型自由定义为int_arrary,可以去看.h函数中的相关定义)来储存L表中和value相等时的下标,和相同的个数
- 然后申请内存(注意后续要释放内存)
- 接下来在这个区间[0,last]依次查看是否有和value相同的数据,如果有就给结构体value_locate中的locate[]数组赋给相应的下标数字,并且same累加表示一共有多少和value相同的数据
- 最后返回 Int_ARRay结构体指针value_locate,表示结构体中的数据已经存放进去了
/*
功能:判断value值和线性表中的数据是否相同,
若相同则返回下标,并且返回相同的个数,用数组存起来
*/
int_arrary list_locate(sqlink L,int value)
{
int_arrary value_locate;
int i=0;
int j=0;
//value_locate->same=0;
// int cnt=0;//用于存放相同元素的总个数
value_locate=(int_arrary)malloc(sizeof(Int_ARRay));//为Int_ARRay申请内存
value_locate->same=0;
for(i=0;i<=L->last;++i)
{
if(L->data[i]==value)
{
value_locate->locate[j++]=i;
(value_locate->same)++;
}
}
return value_locate;
}
Int_ARRary_free
- 用于释放list_locate函数中value_locate=(int_arrary)malloc(sizeof(Int_ARRay));申请的内存,
- 或者用于单纯释放 Int_ARRay申请的内存
/*释放list_locate函数中结构体Int_ARRary的内存,
或者单纯释放结构体Int_ARRary内存*/
int Int_ARRary_free(int_arrary INT_arr)
{
if(INT_arr==NULL)
return -1;
free(INT_arr);
INT_arr=NULL;
return 0;
}
int list_delete_same
- 首先就是判断表是否创建成功或者表是否是空表
- 接下来在区间[0,N]范围内循环依次判断表是否有重复的数据,如果有则返回的value_locate中的same就会大于1,则从存放下标的数组locate中第二个下标开始执行循环list_delete函数,目是删除相同的元素
- 最后要释放内存,防止内存泄漏
示意图:
/*
功能:把L线性表中相同的元素就只保留一个
返回值:-1操作失败 0操作成功
*/
int list_delete_same(sqlink L)
{
int_arrary value_locate;
int i=0,j=0;
//如果为空表或者原本表就没申请成功 返回-1
if(L==NULL||L->last==-1)
return -1;
for(i=0;i<=N;i++)
{
//返回在L线性表中L->data[i]的下标
value_locate=list_locate(L,L->data[i]);
if(value_locate->same>1)//如果相同元素大于1,则存在相同项
{
//从相同元素的第二个下标开始,到相同元素总个数same结束
for(j=value_locate->locate[1];j<=value_locate->same;j++)
//从第二个下标循环依次执行 删除单个元素操作
list_delete(L,j);
}
else;
//printf("sam value:%d\r\n",value_locate->same);
//释放创建Int_ARRary时的内存 因为是个循环里面,如果不释放则容易造成内存泄漏的问题
Int_ARRary_free(value_locate);
}
return 0;
// printf("sam value:%d\r\n",value_locate->same);
}
sqlist.c
整体.C代码
#include<stdlib.h>
#include<stdio.h>
#include"sqlist.h"
#include"string.h"
/*
*参数:无
*功能:创建线性表
*函数实现步骤
*1.申请内存
*2.初始化
*3.返回 (1)下标-1代表是空表(下标是0到N之间的数,表示表都有值因此选择-1)
这时返回NULL
* (2)返回L表示创建成功
*/
sqlink list_creat(void)
{
sqlink L;
L=(sqlink)malloc(sizeof(sqlist));//malloc前面加强制转换符号“(sqlink)”,使得void*转换为sqlist*类型
if(L==NULL)
{
printf("sqlist creat fieled\r\n");
return L;
}
//初始化
memset(L,0,sizeof(sqlist));//把从L地址开始的占_Size字节的内存用_Val填充
L->last=-1;//初始坐标得从-1开始,如果是0,就表示第一个数字初始化即为0了
//返回
return L;
}
//释放空间
int list_free(sqlink L)
{
if(L==NULL)
return -1;
free(L);
L=NULL;
return 0;
}
/*功能:下标为-1表示空表
*返回:1 空,0 非空 表都创建失败
*/
int list_empty(sqlink L)
{
if(L==NULL)
return -1;
if(L->last==-1)
return 1;
return 0;
}
//判断是表否满
//返回值:1满 0未满
int list_full(sqlink L)
{
if(L->last+1==N)
return 1;
return 0;
}
/*
功能:返回表的长度
返回:-1失败,正常为返回长度
*/
int list_lenth(sqlink L)
{
if(L==NULL)
return -1;
return (L->last+1);
}
/*
参数:结构体指针,插入的值,插入的位置
功能:在[0,last+1]范围内插入一个数字,注意:如果表满了则插不了内容了
返回值:-1 插入失败,0 插入成功
*/
int list_insert(sqlink L,int value,int pos)
{
int i=0;
//判断是否full?
if(list_full(L)==1)
return -1;
//pos范围是[0,last+1]
if(pos>(L->last+1)||pos<0)
{
printf("pos not in range\r\n");
return -1;
}
//pos位置后面的元素均往后面移动(从最后一个元素开始往后移动)
for(i=(L->last);i>=pos;i--)
{
L->data[i+1]=L->data[i];
}
//pos位置换上 value
L->data[pos]= value;
//勿忘last++ !!!!
L->last++;
return 0;
}
/*功能:全清0 返回下标-1表示空表
返回:-1清空失败 0表示清空成功*/
int list_claer(sqlink L)
{
if(L==NULL)
return -1;
//清零
memset(L,0,sizeof(sqlist));
L->last=-1;
return 0;
}
/*
功能:删除[0,last+1]范围内的一个数字,具体实现和insert类似
返回值:-1 删除失败,0 删除成功
*/
int list_delete(sqlink L,int pos)
{
int i=0;
if(L==NULL)
{
printf("L is NULL\r\n");
return -1;
}
//判断是否为空
if(L->last==-1)
{
printf("L is empty\r\n");
return -1;
}
if(pos>L->last||pos<0)
{
printf("pos not in range\r\n");
return -1;
}
//[pos+1,last]位置往前整体移动一个单位,最左边的(pos+1)往左边移动
for(i=pos+1;i<=L->last;i++)
{
L->data[i-1]=L->data[i];
}
//因为上面已经覆盖了所以不用赋值了
//更新last !!!1
L->last--;
return 0;
}
int list_show(sqlink L)
{
for(int i=0;i<=L->last;i++)
printf("%d ",L->data[i]);
return 0;
}
/*
功能:判断value值和线性表中的数据是否相同,
若相同则返回下标,并且返回相同的个数,用数组存起来
*/
int_arrary list_locate(sqlink L,int value)
{
int_arrary value_locate;
int i=0;
int j=0;
//value_locate->same=0;
// int cnt=0;//用于存放相同元素的总个数
value_locate=(int_arrary)malloc(sizeof(Int_ARRay));//为Int_ARRay申请内存
value_locate->same=0;
for(i=0;i<=L->last;++i)
{
if(L->data[i]==value)
{
value_locate->locate[j++]=i;
(value_locate->same)++;
}
}
return value_locate;
}
int Int_ARRary_free(int_arrary INT_arr)
{
if(INT_arr==NULL)
return -1;
free(INT_arr);
INT_arr=NULL;
return 0;
}
/*
功能:把某一个新元素替换原来的旧元素
返回值:-1修改失败,0修改成功
*/
int list_change(sqlink L,int value,int pos)
{
if(L==NULL||L->last==-1)
return -1;
L->data[pos]=value;
return 0;
}
/*
功能:把L线性表中相同的元素就只保留一个
返回值:-1操作失败 0操作成功
*/
int list_delete_same(sqlink L)
{
int_arrary value_locate;
int i=0,j=0;
//如果为空表或者原本表就没申请成功 返回-1
if(L==NULL||L->last==-1)
return -1;
for(i=0;i<=N;i++)
{
//返回在L线性表中L->data[i]的下标
value_locate=list_locate(L,L->data[i]);
if(value_locate->same>1)//如果相同元素大于1,则存在相同项
{
//从相同元素的第二个下标开始,到相同元素总个数same结束
for(j=value_locate->locate[1];j<=value_locate->same;j++)
//从第二个下标循环依次执行 删除单个元素操作
list_delete(L,j);
}
else;
//printf("sam value:%d\r\n",value_locate->same);
//释放创建Int_ARRary时的内存 因为是个循环里面,如果不释放则容易造成内存泄漏的问题
Int_ARRary_free(value_locate);
}
return 0;
// printf("sam value:%d\r\n",value_locate->same);
}
main
main.c文件其中main函数主要任务就是调用任务.c函数,比如这里的sqlist.c。然后就是基于此的各种应用层的代码,比如test测试代码了。
还是先依次分析几个测试代码,最后给出main.c整体代码
test_insert
这个函数主要是测试插入功能( list_insert )的完整性
void test_insert()
{
L=list_creat();
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
//从0开始插入
list_insert(L,10,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,13,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("%d",list_lenth(L));
//释放内存
list_free(L);
}
测试结果:
可以看到数据14 13 12 11 10 最后一个5表示数据长度为5
test_delete
这个函数主要测试list_delete函数的作用
void test_delete()
{
L=list_creat();
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
//从0开始插入
list_insert(L,10,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,13,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
printf("sqlist len is:%d\r\n",list_lenth(L));
//删除某一位 并且数据位数减一
list_delete(L,1);
list_show(L);
printf("\r\n");
printf("sqlist len is:%d",list_lenth(L));
//释放内存
list_free(L);
}
测试结果:
可以看到第二个元素(下标为1)13被删除,且表的长度减一
test_change
这个函数主要是对list_change函数的测试
void test_change()
{
L=list_creat();
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
// 从0开始插入
list_insert(L,10,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,13,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
printf("len is:%d",list_lenth(L));
printf("\r\n");
list_change(L,3,2);
list_show(L);
printf("\r\n");
printf("len is:%d",list_lenth(L));
//释放内存
list_free(L);
}
测试结果:
可以看到第三个元素12被替换为3,且表的长度不变
test_value_locate
这个函数主要是对list_locate函数的测试
void test_value_locate()
{
int i=0;
L=list_creat();
int_arrary value_locate;
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
// 从0开始插入
list_insert(L,12,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,12,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
printf("len is:%d",list_lenth(L));
printf("\r\n");
value_locate=list_locate(L,12);
/*
判断逻辑 相同的值的下标一定是递增的,
(如0 2)如果有一个小于上一个下标则结束打印 且上一个下标和当前坐标还不能相等
注意:其下标都存在一个连续的数组中
*/
for(i=0;i<LOCATE_N;i++)
{
if((value_locate->locate[i+1]>value_locate->locate[i])&&(value_locate->locate[i+1]!=value_locate->locate[i]))
printf("same value locate:%d\r\n",value_locate->locate[i]);
else //用于判断最后一个下标
{
printf("same value locate:%d\r\n",value_locate->locate[i]);
i=LOCATE_N;//这时候直接退出循环
}
}
printf("same value:%d\r\n",value_locate->same);
//释放内存
list_free(L);
}
测试结果:
可以看到返回了相同元素12的下标1 ,2 ,4 并且相同的个数是3个
test_delete_same_vlaue
这个函数主要是对list_delete_same函数进行测试的
void test_delete_same_vlaue()
{
L=list_creat();
// int_arrary value_locate;
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
// 从0开始插入
list_insert(L,12,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,12,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
list_delete_same(L);
list_show(L);
printf("\r\n");
//释放内存
list_free(L);
}
测试结果:
可以看到12被清除的就剩一个独苗了。
main.c
最后给出整体main.c
#include <stdio.h>
#include"sqlist.h"
#include"string.h"
sqlink L;//全局变量
int a=1;
int b=2;
void test_insert();
void test_delete();
void test_change();
void test_value_locate();
void test_delete_same_vlaue();
int main(void)
{
//test_insert();
//test_delete();
//test_change();
//test_value_locate();
test_delete_same_vlaue();
return 0;
}
void test_insert()
{
L=list_creat();
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
//从0开始插入
list_insert(L,10,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,13,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("%d",list_lenth(L));
//释放内存
list_free(L);
}
void test_delete()
{
L=list_creat();
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
//从0开始插入
list_insert(L,10,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,13,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
printf("sqlist len is:%d\r\n",list_lenth(L));
//删除某一位 并且数据位数减一
list_delete(L,1);
list_show(L);
printf("\r\n");
printf("sqlist len is:%d",list_lenth(L));
//释放内存
list_free(L);
}
void test_change()
{
L=list_creat();
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
// 从0开始插入
list_insert(L,10,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,13,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
printf("len is:%d",list_lenth(L));
printf("\r\n");
list_change(L,3,2);
list_show(L);
printf("\r\n");
printf("len is:%d",list_lenth(L));
//释放内存
list_free(L);
}
void test_value_locate()
{
int i=0;
L=list_creat();
int_arrary value_locate;
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
// 从0开始插入
list_insert(L,12,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,12,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
printf("len is:%d",list_lenth(L));
printf("\r\n");
value_locate=list_locate(L,12);
/*
判断逻辑 相同的值的下标一定是递增的,
(如0 2)如果有一个小于上一个下标则结束打印 且上一个下标和当前坐标还不能相等
注意:其下标都存在一个连续的数组中
*/
for(i=0;i<LOCATE_N;i++)
{
if((value_locate->locate[i+1]>value_locate->locate[i])&&(value_locate->locate[i+1]!=value_locate->locate[i]))
printf("same value locate:%d\r\n",value_locate->locate[i]);
else //用于判断最后一个下标
{
printf("same value locate:%d\r\n",value_locate->locate[i]);
i=LOCATE_N;//这时候直接退出循环
}
}
printf("same value:%d\r\n",value_locate->same);
//释放内存
list_free(L);
}
void test_delete_same_vlaue()
{
L=list_creat();
// int_arrary value_locate;
// printf("%d\r\n",L->last);
// printf("%d\r\n",i);
// 从0开始插入
list_insert(L,12,0);
list_insert(L,11,0);
list_insert(L,12,0);
list_insert(L,12,0);
list_insert(L,14,0);
list_insert(L,15,2);
list_show(L);
printf("\r\n");
list_delete_same(L);
list_show(L);
printf("\r\n");
//释放内存
list_free(L);
}
这就是顺序表的整体内容了,剩下的后续补上
线性表之单链表
对于单链表来说,可以看到编程的基本功,比如对内存的掌握,还有就是对指针的灵活运用程度。所以说单链表很重要,应用范围也很广
- 注意:使用单链表的时候最好用堆来实现,而不是栈。这是因为在堆上方便内存管理,如果在栈上面就会容易造成,数据生命周期随函数结束而结束的情况。
- 通常来说头指针里面的data数据是不用的,设置为0,头结点就起到一个开始地址的作用。
- 尾指针的next是NULL
以下待整理
main
先main.c文件 清楚函数功能测试,最后给出整体main函数,main函数只是看测试结果,具体实现逻辑请移步.c文件
尾部插入-test_tail_insert();
关于linklist_tail_insert(h,value);在.c文件里面看详解
#include"linklist.h"
#include "stdio.h"
void test_tail_insert();
int main()
{
test_tail_insert();
return 0;
}
void test_tail_insert()
{
linklist h;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
//把链表里面的内容都打印出来
linklist_show(h);
//释放内存
linklist_free(h);
}
依次填入1 2 3 4 -1,其中-1表示停止输入
文件现象:
链表定位 -test_locate();
测试的是当我们输入链表的逻辑位置时,返回该位置的单结点(就是一个结点里面有一个data和一个next的那个)
注意:
if(p!=NULL)//防止段错误,即返回的是空指针,但是依然要打印。加上if判断之后就不会存在这个问题
printf("%d",p->data);
这两句代码 ,加入了if判断语句,是为了防止p为空地址,但是依然要操作,典型的段错误
void test_locate();
int main()
{
test_locate();
return 0;
}
void test_locate()
{
linklist h;
linklist p;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
linklist_show(h);
puts("");
p=linklist_locate(h,3);
if(p!=NULL)//防止段错误,即返回的是空指针,但是依然要打印。加上if判断之后就不会存在这个问题
printf("%d",p->data);
//释放内存
linklist_free(h);
}
依次输入:1 2 3 4 5 6 -1,其中-1仍表示结束输入
结果 :
可以看到返回了第四个单结点,并打印了这个结点的data数据
链表插入-test_insert();
测试的是在一个链表里面按照位置插入一个单结点
#include"linklist.h"
#include "stdio.h"
void test_insert();
int main()
{
test_insert();
return 0;
}
void test_insert()
{
linklist h;
linklist p;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
linklist_show(h);
puts("");
linklist_insert(h,3,5);
linklist_show(h);
//释放内存
linklist_free(h);
}
同理输入1 2 3 4 5 6
结果:
可以看到在第6个位置插入了3,也就是下标为5的位置插入了数据3
更换数据-test_change();
要测试的是:在单个链表的指定位置更换数据
#include"linklist.h"
#include "stdio.h"
void test_tail_insert();
void test_locate();
void test_insert();
void test_change();
void test_all_zero();
void test_delete();
void test_clear();
int main()
{
//test_tail_insert();
//test_locate();
//test_insert();
test_change();
//test_all_zero();
//test_delete();
//test_clear();
return 0;
}
void test_change()
{
linklist h;
linklist p;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
linklist_show(h);
puts("");
//第三个结点数据改为100,也就是下标为2的那个结点改数据
linklist_change(h,100,2);
linklist_show(h);
//释放内存
linklist_free(h);
}
依次输入1 2 3 4 5 6 -1
结果:
可以看到第三个位置上面的数据被替换为100
全更换数据-test_all_cahnge_data();
要测试的是把数据全更换0
#include"linklist.h"
#include "stdio.h"
void test_tail_insert();
void test_locate();
void test_insert();
void test_change();
void test_all_cahnge_data();
void test_delete();
void test_clear();
int main()
{
//test_tail_insert();
//test_locate();
//test_insert();
//test_change();
test_all_cahnge_data();
//test_delete();
//test_clear();
return 0;
}
void test_all_cahnge_data()
{
linklist h;
linklist p;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
linklist_show(h);
puts("");
//把结点全改为0
linklist_ALL_change(h,0);
linklist_show(h);
//释放内存
linklist_free(h);
}
输入1 2 3 4 5 6 -1
结果:
可以看到数据全部被替换成0了
删除单结点-test_delete();
要测试的是链表的某个结点删除
#include"linklist.h"
#include "stdio.h"
void test_tail_insert();
void test_locate();
void test_insert();
void test_change();
void test_all_cahnge_data();
void test_delete();
void test_clear();
int main()
{
//test_tail_insert();
//test_locate();
//test_insert();
//test_change();
//test_all_cahnge_data();
test_delete();
//test_clear();
return 0;
}
void test_delete()
{
linklist h;
linklist p;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
linklist_show(h);
puts("");
//把结点全改为0
linklist_delete(h,2);
linklist_show(h);
//释放内存
linklist_free(h);
}
输入:1 2 3 4 5 6 -1
结果:
可以看到第三个数据被删除了,有数据的链表也就剩下5个了。
删除链表-test_clear();
要测试的是把整个链表都销毁就剩下一个头指针
#include"linklist.h"
#include "stdio.h"
void test_tail_insert();
void test_locate();
void test_insert();
void test_change();
void test_all_cahnge_data();
void test_delete();
void test_clear();
int main()
{
//test_tail_insert();
//test_locate();
//test_insert();
//test_change();
//test_all_cahnge_data();
//test_delete();
test_clear();
return 0;
}
void test_clear()
{
linklist h;
linklist p;
int value;//输入的数据
h=linklist_creat();
if(h==NULL)
printf("h is NULL\r\n");
printf("input:");
while(value!=-1)
{
scanf("%d",&value);
if(value==-1)
break;
linklist_tail_insert(h,value);
printf("input:");
}
//linklist_show(h);
//puts("");
//把结点删除就剩下头结点
linklist_clear(h);
linklist_show(h);
//重新插入
puts("The following is reinsertion");
//尝试重新尾部插入数据
printf("input:");
//第二次输入,为了和第一次区分开,这里设置遇到-2结束
while(value!=-2)
{
scanf("%d",&value);
if(value==-2)
break;
linklist_tail_insert(h,value);
printf("input:");
}
//重新插入
linklist_show(h);
}
结果:
- 可以看到输入123后经过linklist_clear后删除的就剩下头结点了(什么也没显示),
- 然后在The following is reinsertion(翻译为:以下为重新插入)之后又输入了2 3 -2(这时候-2表示结束输入数据,上面-1表示结束输入数据),
- 最后又显示出来了2 3 。表示函数功能正常。
然后我又去看了一下地址,也是对的
贴上.h文件,清楚文件的功能。
#include "stdio.h"
typedef int data_t;//int定义为data_t型
typedef struct node
{
data_t data;
struct node *next;//指向下一个节点的指针,用这个指针就可以表示下一个节点
}linknode,*linklist;
/*
单链表的创建--对于单链表的创建来说应该是位于堆上的,
因为这样对于在堆上的链表我们好管理(动态分配),随着用户的输入,链表逐渐建立起来
如果是在栈上面的话链表就会随着复用程序结束而结束了(静态分配)
下面给出链表的在堆上分配和在栈上分配内存的代码
栈上: linknode A;
linklist p=&A;
堆上: linklist A=(linklist)malloc(sizeof(linknode));
*/
linklist linklist_creat();
//释放 内存
int linklist_free(linklist h);
//增 在尾部增加一个数
int linklist_tail_insert(linklist h,data_t value);
//增 在某一个位置增加一个数
int linklist_insert(linklist h,data_t value,int pos);
//删 全部删除,就剩头指针
linklist linklist_clear(linklist h);
//删 全部清除为0
int linklist_ALL_zero(linklist h);
//删 删除个别的
int linklist_delete(linklist h,int pos);
//查
int linklist_show(linklist h);
//查 返回指定位置的结点,类似顺序表一样
linklist linklist_locate(linklist h,int pos);
//改
int linklist_change(linklist h,int value,int pos );
由于鼠鼠要期末考试,所以后来的回头迅速补上