目录
(1)对SeqListInit初始化函数、SeqListPrint打印函数、SeqListPushBack尾插函数进行调试:
(2) 对SeqListPopBack尾删函数进行调试的过程:
(3) 对SeqListPushFront头插函数、SeqListPopFront头删函数进行调试的过程:
编辑(4) 对SeqListInsert在pos位置插入x的调试过程:
(5)对SeqListErase删除pos位置的值的调试过程:
(6)对 SeqListFind1、SeqListFind2顺序表查找函数的调试过程:
一、线性表
1.线性表定义
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
2.线性表特点
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。
二、顺序表
1.顺序表概念
顺序表是用一段物理地址连续的存储单元(即连续的物理空间)依次存储数据元素的线性结构,因为数组是连续的物理空间所以一般情况下采用数组存储。在数组上完成数据的增删查改。(注意:数据结构的顺序表的本质就是数组;数据结构的顺序表(即数组)与C语言中的数组最大的区别是顺序表中的元素要连续依次存储的,而C语言的数组中有的元素可以散乱(即任意位置)或者连续存储)
2.顺序表的两种结构
(1)静态顺序表:使用定长数组存储元素。(注意:定长数组指的是指定大小的静态数组)
解析:
①静态顺序表由2部分组成:
利用规定大小的静态数组 SLDataType arry[N]依次连续存储静态顺序表的元素(注意:静态数组的大小可以用宏定义,这样就可以随意就修改数组的大小)、
由于顺序表要求数据在数组中依次连续存储,所以我们需要用1个变量size_t size来记录静态顺序表中已经在数组中存放的数据个数(注意:静态数组的大小只是表示顺序表有多少空间存在数据)
②由于静态顺序表由2个不同数据(静态数组、size)组成,而且结构体类型是不同数据的集合,所以静态顺序表要用结构体进行声明定义。
(2)动态顺序表:使用动态开辟的数组存储。
三、动态顺序表的实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空
间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间
大小,所以下面我们实现动态顺序表。
1.初始化函数
//初始化函数
void SeqListInit(SeqList* ps)
{
assert(ps);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
2. 打印顺序表
依次循环打印size个元素即可。
//顺序表打印函数
void SeqListPrint(SeqList* ps)
{
//判断ps是否为空指针
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)//注意:ps->size是统计顺序表中已经存储数据的个数,所以最多可以访问到下标i = ps->size - 1的位置。
{
printf("%d ", ps->a[i]);
}
//换行
printf("\n");
}
3.销毁顺序表
因为顺序表所用的内存空间是动态开辟在堆区的,所以我们在使用完后需要及时对其进行释放,避免造成内存泄漏。若需要对数据进行保存,可以使用文件操作函数将数据保存到一个文件中,下次使用顺序表的时候先读取文件数据即可。
//顺序表销毁函数
void SeqListDestroy(SeqList* ps)
{
//判断ps是否为空指针。
assert(ps);//这里利用assert(ps)判断ps是否为空指针的作用是:即使主调函数传参时不小心或者错误的传了个空指针给被调函数,assert(ps)会进行报错并告诉我们什么代码那一行出现问题。
//写法1:
//if (ps->a)//若ps->a = NULL,则说明没有给指针ps->a分配动态内存空间,所以此时是不需要释放指针ps->a指向的空间的。
//{
// free(ps->a);
// ps->a = NULL;
// ps->capacity = 0;
// ps->size = 0;
//}
//写法2:可以不用if语句进行判断ps->a是否为空指针。
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->size = 0;
}
4.检查是否需要进行扩容
仔细想想,我们每次需要增加数据的时候,首先都应该先检查顺序表内元素个数是否已达顺序表容量上限。若已达上限,那么我们就需要先对顺序表进行扩容,然后才能增加数据。
注意:若传入realloc的指针为空指针(NULL),则realloc函数的作用相当于malloc函数。
//检查顺序表动态空间的容量是否充足,若已满,则增容
void SLCheckcapacity(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)//满了,需要增容
{
//判断顺序表容量是否为0,若为0,则先开辟用于存放4个元素的内存空间;若不为0,则需扩容为原来容量的两倍
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//注意:1.当ps->capacity = 0时表示ps->a = NULL,则此时realloc(ps->a, newcapacity * sizeof(SLDateType) <=> realloc(NULL, newcapacity * sizeof(SLDateType) <=> malloc(newcapacity * sizeof(SLDateType),
//所以我们既可以利用realloc像malloc函数一样申请动态空间,也可以利用realloc进行扩容。
//注意:2.再利用realloc函数进行扩容时不要立即把realloc的返回值赋值给ps->a,否则当realloc扩容失败后直接赋值给ps->a会让我们找不到之前动态空间,
//进而导致我们无法释放掉这部分动态空间进而导致内存泄露的问题,所以我们应该用个临时的指针tmp接收realloc的返回值,再判断完tmp不是空指针之后再把tmp的值赋值给ps->a。
SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
if (tmp == NULL)
{
perror("SLCheckcapacity realloc fail");
exit(-1);
}
ps->a = tmp;//
ps->capacity = newcapacity;
}
}
5.指定下标pos位置插入x
要做到在指定下标pos位置插入数据,首先我们需要得到一个下标pos位置,然后从该下标pos位置开始(包括该位置),其后的数据从后往前依次向后挪动一位,最后将数据插入到该下标pos位置。
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
//判断ps是否为空指针
assert(ps);
//判断插入位置的合法性
assert(pos >= 0 && pos <= ps->size);//由于顺序表要求数据是依次连续存储,所以最多在下标ps->size位置插入数据。
//检查顺序表的空间是否充足,若不充足则要进行扩容
SLCheckcapacity(ps);
int i = 0;
//在pos位置插入数据的过程:——>必须把顺序表下标为pos往后的所有元素都往后挪一步后再在下标pos的位置插入数据x。
for (i = ps->size - 1; i >= pos ; i--)
{
ps->a[i + 1] = ps->a[i];
}
//插入数据
ps->a[pos] = x;
ps->size++;
}
我们可以发现,头插函数和尾插函数实际上就是在下标为0的位置和下标为ps->size的位置插入数据,也就意味着我们可以统一使用该函数来实现头插函数和尾插函数, 如下所示:
//头插
void SeqListPushFront(SeqList* ps, SLDataType x)
{
SeqListInsert(ps, 0, x);//在下标为0的位置插入数据
}
//尾插
void SeqListPushBack(SeqList* ps, SLDataType x)
{
SeqListInsert(ps, ps->size, x);//在下标为ps->size的位置插入数据
}
6.尾插函数
尾插相对于头插就比较简单了,直接在表尾插入数据即可。
//尾插函数
void SeqListPushBack(SeqList* ps, SLDateType x)
{
//尾插函数写法1:
判断ps是否为空指针
//assert(ps);
由于尾插是在下标ps->size位置进行插入数据的,当ps->size < 0时若在下标ps->size位置插入数据x就会造成越界访问,所以在尾插之前判断插入位置ps->size的合法性。
//assert(ps->size >= 0);
检查顺序表的空间是否充足,若不充足则要进行扩容
//SLCheckcapacity(ps);
插入数据
//ps->a[ps->size] = x;
//ps->size++;
//尾插函数写法2:
SeqListInsert(ps, ps->size, x);
}
7.头插函数
要想在顺序表的表头插入数据,那么就需要先将顺序表原有的数据从后往前依次向后挪动一位,最后再将数据插入表头。
//头插函数
void SeqListPushFront(SeqList* ps, SLDateType x)
{
//头插函数写法1:
判读ps是否为空指针
//assert(ps);
判断插入位置的合法性
//assert(ps->size >= 0);//注意:由于头插是插入下标为0的位置,所以不用检查插入位置的合法性,所以这行检查插入位置的合法性代码可有可无。
检查顺序表的空间是否充足,若不充足则要进行扩容
//SLCheckcapacity(ps);
//int i = 0;
头插的过程——>数组所有元素都往后挪一步后并在首元素的位置插入一个数据来完成在顺序表头插一个元素
//for (i = ps->size - 1; i >= 0; i--)
//{
// ps->a[i + 1] = ps->a[i];
//}
插入数据
//ps->a[0] = x;
//ps->size++;
//头插函数写法2:
SeqListInsert(ps, 0, x);
}
8.指定删除下标pos位置的元素
要删除指定下标pos位置的数据,我们只需要从下标pos位置开始,其后的数据从前向后依次覆盖即可。
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
//判断ps是否为空指针
assert(ps);
//判断删除位置pos的合法性,即pos必须在下标为0~size的范围才可以插入数据,否则可能会发生越界访问。
assert((pos >= 0) && (pos < ps->size));
//判断顺序表中是否还有可以删除的数据
assert(ps->size > 0);//注意:SeqListErase函数内部的这行判断顺序表中是否还有可以删除数据的代码可有可无,因为当ps->size <= 0时程序是无法运行到这一行的,
//而且程序会在assert((pos >= 0) && (pos < ps->size))这行代码直接发生报错(注意:报错的原因是当pos = 0 && ps->size <= 0时,无法满足assert((pos >= 0) && (pos < ps->size))函数中的pos < ps->size导致assert发生报错)
int i = 0;
//删除pos位置值的过程->要删除指定下标pos位置的数据,我们只需要从下标pos位置开始,其后的数据从前向后依次覆盖即可。
for (i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
同样的道理,头删函数和尾删函数实际上也就是删除下标为0的位置和下标为ps->size - 1的位置的数据,也就意味着我们可以统一使用该函数来实现头删函数和尾删函数。
/头删
void SeqListPopFront(SeqList* ps)
{
SeqListErase(ps, 0);//删除下标为0的位置的数据
}
//尾删
void SeqListPopBack(SeqList* ps)
{
SeqListErase(ps, ps->size - 1);//删除下标为ps->size - 1的位置的数据
}
9.尾删函数
尾删就更简单了,直接将顺序表的元素个数减一即可。
//尾删函数
void SeqListPopBack(SeqList* ps)
{
//尾删函数写法1:
判断ps是否为空指针
//assert(ps);
判断顺序表中是否还有可以删除的数据
//assert(ps->size > 0);
尾删
注意:尾删的时候把顺序表最后一个元素的值设置为0是可以有可无的,因为ps->size是用来统计顺序表中存放元素的个数,所以我们只要用ps->size--就可以表示尾删顺序表中的一个元素。
//ps->a[ps->size - 1] = 0;
//ps->size--;
//尾删函数写法2:
SeqListErase(ps, ps->size - 1);
}
10.头删函数
要删除表头的数据,我们可以从下标为1的位置开始,依次将数据向前覆盖即可。
//头删函数
void SeqListPopFront(SeqList* ps)
{
//头删函数写法1:
判断ps是否为空指针
//assert(ps);
判断顺序表中是否还有可以删除的数据
//assert(ps->size > 0);
//int i = 0;
头删——>除了首元素,其它元素都要往前挪一步来完成头删顺序表的一个元素
//for (i = 1; i < ps->size; i++)
//{
// ps->a[i - 1] = ps->a[i];
//}
//ps->size--;
//头删函数写法2:
SeqListErase(ps, 0);
}
11.查找数据
//写法1:
// 顺序表查找函数——>解析:若在顺序表中查找到数据x则SeqListFind1函数返回数据x的下标,若没有查到数据x则SeqListFind1函数返回-1表示顺序表中不存在数据x。
//SeqListFind1的作用:SeqListFind1函数可以把查找到的数据x的下标传参给SeqListErase函数使得SeqListErase函数可以做到指定顺序表中的某个元素进行删除。
int SeqListFind1(SeqList* ps, SLDateType x)
{
//判断ps是否为空指针
assert(ps);
//查找数据x的过程——>从头遍历整个顺序表看一下顺序表中那个元素是我们要查找的数据x。
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
return i;
}
return -1;
}
//写法2:
//SeqListFind2的作用与SeqListFind1的作用类似,但是SeqListFind2是从顺序表下标为begin位置查找我们要查找的数据x。
int SeqListFind2(SeqList* ps, SLDateType x, int begin)
{
//判断ps是否为空指针
assert(ps);
//查找数据x的过程——>从下标begin开始遍历整个顺序表看一下顺序表中那个元素是我们要查找的数据x。
int i = 0;
for (i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
return i;
}
return -1;
}
查找函数的用法是与SeqListErase函数结合使用可以做到指定值进行删除。
下面testSeqList6()测试函数展示了SeqListFind1函数结合SeqListErase函数可以做到指定值进行删除、SeqListFind2函数结合SeqListErase函数可以做到删除序列中重复的值,如下图所示:
四、动态顺序表各个功能函数对应的调试代码
1.整个顺序表工程的程序
(1)SeqList.h
(注意:SeqList.h头文件是用来存放顺序表各个功能函数的声明、头文件包含)
SeqList.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//顺序表中存储的数据类型
typedef int SLDateType;
//顺序表的结构体类型
typedef struct SeqList
{
SLDateType* a;
int size;
int capacity;
}SeqList;
// 对数据的管理:增删查改
//初始化函数
void SeqListInit(SeqList* ps);
//顺序表销毁函数
void SeqListDestroy(SeqList* ps);
//顺序表打印函数
void SeqListPrint(SeqList* ps);
//尾插、尾删函数
void SeqListPushBack(SeqList* ps, SLDateType x);
void SeqListPopBack(SeqList* ps);
//头插、头删函数
void SeqListPushFront(SeqList* ps, SLDateType x);
void SeqListPopFront(SeqList* ps);
// 顺序表查找函数——>两种写法
int SeqListFind1(SeqList* ps, SLDateType x);//SeqListFind1函数是从顺序表下标0开始查找查找数据x。找到数据x函数返回数据x的下标,没有找到返回-1。
int SeqListFind2(SeqList* ps, SLDateType x,int begin);//SeqListFind2函数是从顺序表下标begin开始查找数据x。找到数据x函数返回数据x的下标,没有找到返回-1。
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);
(2)SeqList.c
(注意:SeqList.c源文件是用来存放顺序表各个功能函数的定义)
#include "SeqList.h"
//初始化函数
void SeqListInit(SeqList* ps)
{
assert(ps);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
//顺序表打印函数
void SeqListPrint(SeqList* ps)
{
//判断ps是否为空指针
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)//注意:ps->size是统计顺序表中已经存储数据的个数,所以最多可以访问到下标i = ps->size - 1的位置。
{
printf("%d ", ps->a[i]);
}
//换行
printf("\n");
}
//顺序表销毁函数
void SeqListDestroy(SeqList* ps)
{
//判断ps是否为空指针。
assert(ps);//这里利用assert(ps)判断ps是否为空指针的作用是:即使主调函数传参时不小心或者错误的传了个空指针给被调函数,assert(ps)会进行报错并告诉我们什么代码那一行出现问题。
//写法1:
//if (ps->a)//若ps->a = NULL,则说明没有给指针ps->a分配动态内存空间,所以此时是不需要释放指针ps->a指向的空间的。
//{
// free(ps->a);
// ps->a = NULL;
// ps->capacity = 0;
// ps->size = 0;
//}
//写法2:可以不用if语句进行判断ps->a是否为空指针。
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->size = 0;
}
//检查顺序表动态空间的容量是否充足,若已满,则增容
void SLCheckcapacity(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)//满了,需要增容
{
//判断顺序表容量是否为0,若为0,则先开辟用于存放4个元素的内存空间;若不为0,则需扩容为原来容量的两倍
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//注意:1.当ps->capacity = 0时表示ps->a = NULL,则此时realloc(ps->a, newcapacity * sizeof(SLDateType) <=> realloc(NULL, newcapacity * sizeof(SLDateType) <=> malloc(newcapacity * sizeof(SLDateType),
//所以我们既可以利用realloc像malloc函数一样申请动态空间,也可以利用realloc进行扩容。
//注意:2.再利用realloc函数进行扩容时不要立即把realloc的返回值赋值给ps->a,否则当realloc扩容失败后直接赋值给ps->a会让我们找不到之前动态空间,
//进而导致我们无法释放掉这部分动态空间进而导致内存泄露的问题,所以我们应该用个临时的指针tmp接收realloc的返回值,再判断完tmp不是空指针之后再把tmp的值赋值给ps->a。
SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
if (tmp == NULL)
{
perror("SLCheckcapacity realloc fail");
exit(-1);
}
ps->a = tmp;//
ps->capacity = newcapacity;
}
}
//尾插函数
void SeqListPushBack(SeqList* ps, SLDateType x)
{
//尾插函数写法1:
判断ps是否为空指针
//assert(ps);
由于尾插是在下标ps->size位置进行插入数据的,当ps->size < 0时若在下标ps->size位置插入数据x就会造成越界访问,所以在尾插之前判断插入位置ps->size的合法性。
//assert(ps->size >= 0);
检查顺序表的空间是否充足,若不充足则要进行扩容
//SLCheckcapacity(ps);
插入数据
//ps->a[ps->size] = x;
//ps->size++;
//尾插函数写法2:
SeqListInsert(ps, ps->size, x);
}
//尾删函数
void SeqListPopBack(SeqList* ps)
{
//尾删函数写法1:
判断ps是否为空指针
//assert(ps);
判断顺序表中是否还有可以删除的数据
//assert(ps->size > 0);
尾删
注意:尾删的时候把顺序表最后一个元素的值设置为0是可以有可无的,因为ps->size是用来统计顺序表中存放元素的个数,所以我们只要用ps->size--就可以表示尾删顺序表中的一个元素。
//ps->a[ps->size - 1] = 0;
//ps->size--;
//尾删函数写法2:
SeqListErase(ps, ps->size - 1);
}
//头插函数
void SeqListPushFront(SeqList* ps, SLDateType x)
{
//头插函数写法1:
判读ps是否为空指针
//assert(ps);
判断插入位置的合法性
//assert(ps->size >= 0);//注意:由于头插是插入下标为0的位置,所以不用检查插入位置的合法性,所以这行检查插入位置的合法性代码可有可无。
检查顺序表的空间是否充足,若不充足则要进行扩容
//SLCheckcapacity(ps);
//int i = 0;
头插的过程——>数组所有元素都往后挪一步后并在首元素的位置插入一个数据来完成在顺序表头插一个元素
//for (i = ps->size - 1; i >= 0; i--)
//{
// ps->a[i + 1] = ps->a[i];
//}
插入数据
//ps->a[0] = x;
//ps->size++;
//头插函数写法2:
SeqListInsert(ps, 0, x);
}
//头删函数
void SeqListPopFront(SeqList* ps)
{
//头删函数写法1:
判断ps是否为空指针
//assert(ps);
判断顺序表中是否还有可以删除的数据
//assert(ps->size > 0);
//int i = 0;
头删——>除了首元素,其它元素都要往前挪一步来完成头删顺序表的一个元素
//for (i = 1; i < ps->size; i++)
//{
// ps->a[i - 1] = ps->a[i];
//}
//ps->size--;
//头删函数写法2:
SeqListErase(ps, 0);
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
//判断ps是否为空指针
assert(ps);
//判断插入位置的合法性
assert(pos >= 0 && pos <= ps->size);//由于顺序表要求数据是依次连续存储,所以最多在下标ps->size位置插入数据。
//检查顺序表的空间是否充足,若不充足则要进行扩容
SLCheckcapacity(ps);
int i = 0;
//在pos位置插入数据的过程:——>必须把顺序表下标为pos往后的所有元素都往后挪一步后再在下标pos的位置插入数据x。
for (i = ps->size - 1; i >= pos ; i--)
{
ps->a[i + 1] = ps->a[i];
}
//插入数据
ps->a[pos] = x;
ps->size++;
}
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
//判断ps是否为空指针
assert(ps);
//判断删除位置pos的合法性,即pos必须在下标为0~size的范围才可以插入数据,否则可能会发生越界访问。
assert((pos >= 0) && (pos < ps->size));
//判断顺序表中是否还有可以删除的数据
assert(ps->size > 0);//注意:SeqListErase函数内部的这行判断顺序表中是否还有可以删除数据的代码可有可无,因为当ps->size <= 0时程序是无法运行到这一行的,
//而且程序会在assert((pos >= 0) && (pos < ps->size))这行代码直接发生报错(注意:报错的原因是当pos = 0 && ps->size <= 0时,无法满足assert((pos >= 0) && (pos < ps->size))函数中的pos < ps->size导致assert发生报错)
int i = 0;
//删除pos位置值的过程->要删除指定下标pos位置的数据,我们只需要从下标pos位置开始,其后的数据从前向后依次覆盖即可。
for (i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
SeqList.c
//写法1:
// 顺序表查找函数——>解析:若在顺序表中查找到数据x则SeqListFind1函数返回数据x的下标,若没有查到数据x则SeqListFind1函数返回-1表示顺序表中不存在数据x。
//SeqListFind1的作用:SeqListFind1函数可以把查找到的数据x的下标传参给SeqListErase函数使得SeqListErase函数可以做到指定顺序表中的某个元素进行删除。
int SeqListFind1(SeqList* ps, SLDateType x)
{
//判断ps是否为空指针
assert(ps);
//查找数据x的过程——>从头遍历整个顺序表看一下顺序表中那个元素是我们要查找的数据x。
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
return i;
}
return -1;
}
//写法2:
//SeqListFind2的作用与SeqListFind1的作用类似,但是SeqListFind2是从顺序表下标为begin位置查找我们要查找的数据x。
int SeqListFind2(SeqList* ps, SLDateType x, int begin)
{
//判断ps是否为空指针
assert(ps);
//查找数据x的过程——>从下标begin开始遍历整个顺序表看一下顺序表中那个元素是我们要查找的数据x。
int i = 0;
for (i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
return i;
}
return -1;
}
(3)text.c
(注意:①text.c源文件是用来测试顺序表各个功能函数的,所以text.c源文件存放了顺序表各个功能函数对应的测试函数;②当我们在写整个工程的各个函数接口时,要写一个调试一个函数接口,不要一直无厘头的写下去不然运行起来时很难发现到底哪个函数接口有问题。而且每个函数接口都应该有自己测试函数。在测试各个功能函数接口时不要在main函数中写菜单不让调试起来很麻烦。下面展示了顺序表各个功能函数对应各自序号的测试函数testSeqList。)
text.c
#include "SeqList.h"
//testSeqList1测试函数是用来测试SeqListInit初始化函数、SeqListPrint打印函数、SeqListPushBack尾插函数的。
void testSeqList1()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
//把数据尾插到顺序表中
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
SeqListPushBack(&sl, 5);
SeqListPushBack(&sl, 5);
SeqListPushBack(&sl, 5);
SeqListPushBack(&sl, 5);
//打印顺序表中的数据
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
}
//testSeqList2测试函数是用来测试SeqListPopBack尾删函数的。
void testSeqList2()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
//尾插
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPrint(&sl);
//尾删
SeqListPopBack(&sl);
SeqListPrint(&sl);
//尾插
SeqListPushBack(&sl, 8);
SeqListPrint(&sl);
//尾删
SeqListPopBack(&sl);
SeqListPopBack(&sl);
SeqListPopBack(&sl);
SeqListPopBack(&sl);
//SeqListPopBack(&sl);//若此时继续尾删的话,则会发生报错,所以我们要屏蔽掉这行代码
SeqListPrint(&sl);
//尾插
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 2);
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
}
//testSeqList3测试函数是用来测试SeqListPushFront头插函数、SeqListPopFront头删函数的。
void testSeqList3()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
//尾插
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPrint(&sl);
//头删
SeqListPopFront(&sl);
SeqListPopFront(&sl);
SeqListPopFront(&sl);
SeqListPopFront(&sl);
//SeqListPopFront(&sl);//若此时继续头删的话,则会发生报错,所以我们要屏蔽掉这行代码
SeqListPrint(&sl);
//尾插
SeqListPushBack(&sl, 10);
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
}
//testSeqList4测试函数是用来测试SeqListInsert在pos位置插入x的函数。
void testSeqList4()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
//尾插
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPrint(&sl);
//在pos位置插入数据
SeqListInsert(&sl, 2, 20);
SeqListPrint(&sl);
SeqListInsert(&sl, 5, 500);
SeqListPrint(&sl);
SeqListInsert(&sl, 0, 500);
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
}
//testSeqList5测试函数是用来测试SeqListErase删除pos位置的值函数。
void testSeqList5()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
//尾插
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPrint(&sl);
//在pos位置插入数据
SeqListErase(&sl,2);
SeqListPrint(&sl);
SeqListErase(&sl, 2);
SeqListPrint(&sl);
SeqListErase(&sl, 0);
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
}
//testSeqList6测试函数是用来测试SeqListFind1、SeqListFind2顺序表查找函数的。
void testSeqList6()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
//尾插
SeqListPushBack(&sl, 1);
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
SeqListPushBack(&sl, 7);
SeqListPushBack(&sl, 5);
SeqListPushBack(&sl, 8);
SeqListPushBack(&sl, 5);
SeqListPrint(&sl);
//SeqListFind函数写法1的测试用例
int pos = SeqListFind1(&sl, 5);
if(pos != -1)
SeqListErase(&sl,pos);
SeqListPrint(&sl);
//SeqListFind函数写法2的测试用例:
pos = SeqListFind2(&sl, 4,0);
if (pos != -1)
SeqListErase(&sl, pos);
SeqListPrint(&sl);
//利用SeqListFind函数写法2来删除顺序表的所有的5:
pos = SeqListFind2(&sl, 5, 0);
while (pos != -1)
{
SeqListErase(&sl, pos);
pos = SeqListFind2(&sl, 5, pos);
}
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
}
//int main()
//{
// //注意:我们在写整个工程的各个函数接口时,要写一个调试一个函数接口,不要一直无厘头的写下去不然运行起来时很难发现到底哪个函数接口有问题。
// //而且每个函数接口都应该有自己测试函数。在测试各个功能函数接口时不要在main函数中写菜单不让调试起来很麻烦。
// //顺序表各个功能函数接口的测试函数testSeqList。
// testSeqList1();
// testSeqList2();
// testSeqList3();
// testSeqList4();
// testSeqList5();
// testSeqList6();
//}
enum function
{
Exit,//退出
Tailin,//尾插
Tailde,//尾删
Headin,//头插
Headde,//头删
Print//打印
};
void menu()
{
printf("****************************************\n");
printf("****** 1.尾插数据 2.尾删数据 ******\n");
printf("****** 3.头插数据 4.头删数据 ******\n");
printf("****** 5.打印数据 0.退出 ******\n");
printf("****************************************\n");
}
int main()
{
//创建顺序表
SeqList sl;
//对顺序表进行初始化
SeqListInit(&sl);
int input = 0;
int data = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case Tailin://尾插
printf("请依次输入你要尾插的数据,以输入-1来结束输入:>");
scanf("%d", &data);
while (data != -1)
{
SeqListPushBack(&sl, data);
scanf("%d", &data);
}
break;
case Tailde://尾删
SeqListPopBack(&sl);
break;
case Headin://头插
printf("请依次输入你要尾插的数据,以输入-1来结束输入:>");
scanf("%d", &data);
while (data != -1)
{
SeqListPushFront(&sl, data);
scanf("%d", &data);
}
break;
case Headde://头删
SeqListPopFront(&sl);
break;
case Print://打印
SeqListPrint(&sl);
break;
case Exit://退出
printf("退出\n");
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
2.下面是我对顺序表各个功能函数调试的过程:
(1)对SeqListInit初始化函数、SeqListPrint打印函数、SeqListPushBack尾插函数进行调试:
(2) 对SeqListPopBack尾删函数进行调试的过程:
运行testSeqList2测试函数时发现程序发生报错,而报错的地方是SeqList.c源文件的第79行,而且报错的原因是ps->size > 0,说明若继续执行testSeqList2测试函数中的第64行的尾删函数的话则会发生报错,所以我们要屏蔽掉estSeqList2测试函数中的第64行的尾删代码。
屏蔽掉testSeqList2测试函数中的第64行的尾删代码后,testSeqList2测试函数运行正常。