目录
1.顺序表的介绍
顺序表是在 计算机内存 中以 数组 的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用 顺序存储结构 的线性表通常称为顺序表。. 顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
2.动态顺序表的准备工作
1.需要的头文件和文件
整个顺序表需要三个文件分别是SeqList.h SeqList.c test.c.
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
2.创建顺序表
顺序表的创建先是重新定义命名顺序表中的元素类型,这里我用的是int,这样做的好处是可以随时修改顺序表的类型,并且只需要通过SLDataType 就可以创建数据元素类型,较为方便。
后面的是重新定义的一个结构体SeqList可以写成SL,其中的成员包含数据SLDataType的指向a,整个顺序表的长度size,顺序表的容量capacity。
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity;
}SL;
3.接口函数
接口函数就是将顺序表的每一种操作的函数,都放在一起先定义一下,想要调用的时候可以直接去调用。下面是几个比较常见常用的接口函数。
//打印顺序表
void SeqListPrint(SL* ps);
//顺序表销毁
void SeqListDestroy(SL* ps);
//初始化线性表
void SeqListInit(SL* ps);
//尾插法
void SeqListPushBack(SL* ps, SLDataType x);
//尾删法
void SeqListPopBack(SL* ps);
//头插法
void SeqListPushFront(SL* ps, SLDataType x);
//尾插法
void SeqListPopFront(SL* ps);
//增容
void SeqListCheckCapacity(SL* ps);
//找到了返回x下标位置,找不到返回-1
int SeqListFind(SL* ps, SLDataType x);
//指定pos下标位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);
//删除pos位置的数据
void SeqListErase(SL* ps, int pos);
3.接口函数的实现
1.初始化顺序表
初始顺序表就是将结构体指针ps指向的空间全部置为空,并且顺序表的长度和容量也置为0.
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
2.打印线性表
打印线性表就是通过一个for循环将全部的数据打印出来即可,打印完后换行即可。
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
3.销毁线性表
销毁线性表就是将线性表中的内容释放掉,然后将其和初始化一样的操作,空间置为空,顺序表的长度和容量都置为0。
void SeqListDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
4.顺序表扩容
对于一些添加元素的操作,如果顺序表的已经满了并且容量也已经达到了上限,这就需要对顺序表进行扩容,以防止顺序表的越界。
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)//判断长度和容量是否相等
{
//这里用newcapacity代表新的容量,如果原本容量是0的话就将新的容量变成4,
//不然就是原本的容量乘2,这里用到了一个三木运算符的操作。
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
//用realloc函数来动态开辟空间来
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1); //如果没有开辟成功直接就是结束程序
}
ps->a = tmp; //tmp就是新的顺序表空间大小
ps->capacity = newcapacity; // newcapacity就是新的顺序表的容量
}
}
4.四个基本功能
1.插入数据
1.头插法
头插法需要先扩容,然后是定义一个end是在顺序表的最后一个元素,头插的思想就是将最后一个数往后移一位,前面的数字也依次往后移一位,这样就能将开头的位置空出来然后插入需要插入的元素就行,再将顺序表的长度加一即可。
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps); //首先需要扩容
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
2.尾插法
也是一样,插入数据最先要做的就是扩容。
尾插法就是将整个顺序表的最后一个元素的后面一个位置插入要插入的元素,ps->[ps->size]就是代表的最后一个元素的后一个位置,再将顺序表的长度加一即可。
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
3.指定位置插入元素
函数的参数是在顺序表指针ps,在位置pos上插入元素x,后面的元素向后移一位
可以先通过assert来判断指针,和pos位置的合法性,同样其他的接口函数也可以这样做。如果pos大于顺序表的长度就打印数组越界并且结束程序。
定义一个end是顺序表的最后一个元素,用一个判断条件是end>=pos的条件的while循环来将pos位置后的元素向后移一位,end--即可。最后再将顺序表的长度加一即可。
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos <= ps->size && pos >= 0 && pos);
if (pos > ps->size)
{
printf("位置越界\n");
return;
}
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
2.删除元素
1.头删法
先用assert来判断顺序表的长度是否大于0,如果不是的话直接就结束程序。
定义一个begin是顺序表的第二个元素,然后就是将第二个元素前移到第一个,后面的元素依次前移,这样第一个元素就可以被删除,顺序表的长度再减去1,这就是头删法的基本思想。
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
2.尾删法
先判断顺序表长度的合法性,直接将顺序表的长度减一即可,就是代表着删除了最后一个元素。
注释掉的if也是用来判断顺序表长度的合法性的。
void SeqListPopBack(SL* ps)
{
assert(ps->size > 0);
//ps->a[ps->size - 1] = 0;
ps->size--;
或者
//
//if (ps->size > 0)
//{
// //ps->a[ps->size - 1] = 0;
// ps->size--;
//}
}
3.删除指定位置的元素
先是assert来判断指针和pos位置的合法性。定义一个begin是指定位置的后一个元素,用一个判断条件是begin<pos->size的while循环语句,将begin位置的元素前移到begin-1,然后再将begin++就可将全部pos位置往后的全部元素的位置前移,并且将pos位置的元素删除。最后再将顺序表的长度减一即可。
void SeqListErase(SL* ps, int pos)
{
assert(ps);
assert(pos && pos < ps->size&& pos >= 0);
int begin = pos + 1;
if (pos > ps->size)
{
printf("位置越界\n");
return;
}
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
3.查找元素
1.找到了返回x下标位置,找不到返回-1
先判断ps指向的合法性。定义begin为0,通过for循环遍历整个顺序表,如果有和x相同的元素就返回begin也就是元素的下标,否则返回-1,就是代表着未找到的意思。
int SeqListFind(SL* ps, SLDataType x)
{
assert(ps);
int begin = 0;
for (begin = 0; begin < ps->size; begin++)
{
if (ps->a[begin] == x)
return begin;
}
return -1;
}
4.修改元素
1.修改指定位置的元素
assert判断指针合法性和长度合法性。然后是将pos位置的元素换成x即可。
void SeqLlistModify(SL* ps, int pos, SLDataType x)
{
assert(ps);
asssert(ps->size > pos);
ps->a[pos] = x;
}
5.整体的代码
1.SeqList.h
#define _CRT_SECURE_NO_WARNINGS 1
静态顺序表
特点:如果满了就不让插入,难以确定给多大合适。
//
//#define N 1000
//typedef int SLDataType;
//
//typedef struct SeqList
//{
// SLDataType a[N];
// int size; //表示数组中存储了多少个数据
//}SL;
//
接口函数————命名跟着STL走,
//void SeqListInit(SL* ps);
//void SeqListPushBack(SL* ps, SLDataType x);
//void SeqListPopBack(SL* ps);
//void SeqListPushFront(SL* ps, SLDataType x);
//void SeqListPopFront(SL* ps);
//动态顺序表
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define N 1000
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity;
}SL;
//打印顺序表
void SeqListPrint(SL* ps);
//顺序表销毁
void SeqListDestroy(SL* ps);
//初始化线性表
void SeqListInit(SL* ps);
//尾插法
void SeqListPushBack(SL* ps, SLDataType x);
//尾删法
void SeqListPopBack(SL* ps);
//头插法
void SeqListPushFront(SL* ps, SLDataType x);
//尾插法
void SeqListPopFront(SL* ps);
//增容
void SeqListCheckCapacity(SL* ps);
//找到了返回x下标位置,找不到返回-1
int SeqListFind(SL* ps, SLDataType x);
//指定pos下标位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);
//删除pos位置的数据
void SeqListErase(SL* ps, int pos);
//修改下标pos的元素
void SeqLlistModify(SL* ps, int pos, SLDataType x);
2.SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqLlistModify(SL* ps, int pos, SLDataType x)
{
assert(ps);
asssert(ps->size > pos);
ps->a[pos] = x;
}
void SeqListErase(SL* ps, int pos)
{
assert(ps);
assert(pos && pos < ps->size&& pos >= 0);
int begin = pos + 1;
if (pos > ps->size)
{
printf("位置越界\n");
return;
}
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos <= ps->size && pos >= 0 && pos);
if (pos > ps->size)
{
printf("位置越界\n");
return;
}
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
//找到某个数的下标
int SeqListFind(SL* ps, SLDataType x)
{
assert(ps);
int begin = 0;
for (begin = 0; begin < ps->size; begin++)
{
if (ps->a[begin] == x)
return begin;
}
return -1;
}
//头删
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
//头插
void SeqListPushFront(SL* ps, SLDataType x)
{
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
//尾删
void SeqListPopBack(SL* ps)
{
assert(ps->size > 0);
//ps->a[ps->size - 1] = 0;
ps->size--;
或者
//
//if (ps->size > 0)
//{
// //ps->a[ps->size - 1] = 0;
// ps->size--;
//}
}
//销毁
void SeqListDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//打印
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//初始化
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
//扩容
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
//尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
3.test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化+尾插+尾删
void TestSeqList1()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
SeqListPopBack(&s1);
SeqListPopBack(&s1);
SeqListPrint(&s1);
}
//初始化+尾插+尾插
void TestSeqList2()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
SeqListPushBack(&s1, 10);
SeqListPushBack(&s1, 20);
SeqListPushBack(&s1, 30);
SeqListPushBack(&s1, 40);
SeqListPrint(&s1);
}
//初始化+尾插+头插
void TestSeqList3()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
SeqListPushFront(&s1, 10);
SeqListPushFront(&s1, 20);
SeqListPushFront(&s1, 30);
SeqListPushFront(&s1, 40);
SeqListPrint(&s1);
}
//初始化+尾插+头删
void TestSeqList4()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
SeqListPopFront(&s1);
SeqListPopFront(&s1);
SeqListPrint(&s1);
}
//初始化+尾插+找数字
void TestSeqList5()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
int ret = SeqListFind(&s1, 8);
if (ret != -1)
printf("下标为%d\n", ret);
else
printf("未找到\n");
}
//初始化+尾插+在pos位置插入x
void TestSeqList6()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
SeqListInsert(&s1, 2, 8);
SeqListPrint(&s1);
}
//初始化+尾插+删除pos位置元素
void TestSeqList7()
{
SL s1;
SeqListInit(&s1);
SeqListPushBack(&s1, 1);
SeqListPushBack(&s1, 2);
SeqListPushBack(&s1, 3);
SeqListPushBack(&s1, 4);
SeqListPushBack(&s1, 5);
SeqListPrint(&s1);
SeqListErase(&s1, 5);
SeqListPrint(&s1);
}
int main()
{
//TestSeqList1();
//TestSeqList2();
//TestSeqList3();
//TestSeqList3();
//TestSeqList4();
//TestSeqList5();
//TestSeqList6();
//TestSeqList7();
return 0;
}