目录:
线性表
- 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
- 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
顺序表
概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改
分类
🎈静态顺序表:使用定长数组存储元素
缺点:只能被动的修改数组大小,太大了浪费,太小了不够用
静态顺序表
#define N 100
typedef int SLDataType;
struct SeqList
{
SLDataType a[N];
int size;//存储的数据的个数
};
🎈动态顺序表:使用动态开辟的数组存储
优点:根据实际需要自己开辟内存空间
//动态顺序表
typedef struct SeqList
{
SLDataType* a;
int size;//存储的数据的个数
int capacity;//存储空间的大小
}SL;
顺序表接口实现
🧨使用的平台是VS2019
🧨pro.c(主函数以及测试程序)
🧨SeqList.c(顺序表各个功能函数的实现)
🧨SeqList.h(顺序表函数的声明,头文件的引用)
SeqList.h中函数的声明
SeqList.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;//存储的数据的个数
int capacity;//存储空间的大小
}SL;
void SeqListInit(SL* ps1);//顺序表初始化
void SeqListDestroy(SL* ps1);//顺序表的销毁
void SLPrint(SL* ps1);//顺序表打印
//头插头删 尾插尾删
void SLPushBack(SL* ps1, SLDataType x);//尾部插入
void SLPushFront(SL* ps1, SLDataType x);//头部插入
void SLPopBack(SL* ps1);//尾部删除
void SLPopFront(SL* ps1);//头部删除
// 顺序表查找,返回下标,没有找到返回-1
int SLFind(SL* ps1, SLDataType x);
// 顺序表在pos位置插入x
void SLInsert(SL* ps1, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SLErse(SL* ps1, size_t pos);
//修改在pos位置的值
void SLModify(SL* ps1, size_t pos, SLDataType x)
SeqList.c
初始化顺序表
void SeqListInit(SL* ps1)
{
assert(ps1);
ps1->a = NULL;
ps1->size = 0;
ps1->capacity = 0;
}
1.首先应当在主函数中定义一个结构体类型的变量,将该变量的地址传过来,形式参数用结构体类型指针接收,让后通过结构体类型变量ps1指向的内容可以修改结构体成员的值,如果传值调用,无法修改数据
2.assert防止传过来的是空指针
3.然后将指针变量a、size和capacity全部初始化为0
销毁顺序表
void SeqListDestroy(SL* ps1)
{
assert(ps1);
free(ps1->a);
ps1->a = NULL;
ps1->size = 0;
ps1->capacity = 0;
}
1.因为后续要涉及到动态内存的开辟,所以需要先释放,然后再将其他成员变量置为空
打印顺序表中的内容
void SLPrint(SL* ps1)
{
assert(ps1);
for (int i = 0; i < ps1->size; i++)
{
printf("%d ", ps1->a[i]);
}
}
检查顺序表中的容量大小
void CheckCapacity(SL* ps1)
{
if (ps1->size == ps1->capacity)
{
int newcapacity = ps1->capacity == 0 ? 4 : ps1->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps1->a, newcapacity * (sizeof(SLDataType)));
if (tmp == NULL)
{
perror("tmp:");
return;
// exit(-1);
}
ps1->a = tmp;
ps1->capacity = newcapacity;
}
}
1.如果数据的个数等于空间的大小,说明空间已经满了
2.第一次执行时 ps1->capacity=newcapacity=0,所以newcapacity=4,由于realloc在使用时,如果第一参数是空,则与malloc的使用方法一致,所以第一次开辟了4个整型,即16个字节的空间
3.然后判断返回值是否为空,将开辟的空间的地址赋给ps1->a,将存储空间的大小赋值
4.当以后再进来时,ps1->capacity=newcapacity=0的条件不成立,所以将空间乘2,然后再开辟这些空间大小的整型空间
顺序表尾部插入
void SLPushBack(SL* ps1, SLDataType x)
{
assert(ps1);
//检查容量
CheckCapacity(ps1);
ps1->a[ps1->size] = x;
ps1->size++;
}
1.在插入之前先检查容量,如果容量小就扩容
2.然后将要插入的值x放在ps1->size的位置位置上,size的数值加1
顺序表头部插入
void SLPushFront(SL* ps1, SLDataType x)
{
assert(ps1);
//检查容量
CheckCapacity(ps1);
//移动数据,从后向前移动
int end = ps1->size - 1;
while (end >= 0)
{
ps1->a[end+1] = ps1->a[end];
end--;
}
ps1->a[0] = x;
ps1->size++;
}
1.头部插入也是先检查容量
2.使用end将数组的最后一个元素依次向后挪动,最后空出第一元素的位置
3.再将x放进去,size的数量加1
顺序表尾部删除
void SLPopBack(SL* ps1)//尾部删除
{
assert(ps1);
if (ps1->size == 0)
{
return;
}
ps1->size--;
}
1.删除时首先确保元素不为空
2.直接将size–即可,如果再插入其他元素,便可以将size后的元素覆盖,不需要额外处理
顺序表头部删除
void SLPopFront(SL* ps1)//头部删除
{
assert(ps1);
if (ps1->size == 0)
{
return;
}
int begin = 0;
while (begin < ps1->size - 1)
{
ps1->a[begin] = ps1->a[begin + 1];
++begin;
}
//int i = 0;
//for (i = 0; i < ps1->size-1; i++)
//{
// ps1->a[i] = ps1->a[i+1];
//}
ps1->size--;
}
1.首先确保元素不能为空
2.头部删除直接将后面的元素覆盖前面的元素,size–
顺序表查找
int SLFind(SL* ps1, SLDataType x)// 顺序表查找,返回下标,没有找到返回-1
{
assert(ps1);
for (int i = 0; i < ps1->size; i++)
{
if (ps1->a[i] == x)
{
return i;
}
}
return -1;
}
1.直接利用循环遍历一遍,找到x,返回i,否则返回-1
顺序表插入
void SLInsert(SL* ps1, size_t pos, SLDataType x)// 顺序表在pos位置插入x
{
assert(ps1);
assert(pos <= ps1->size);
CheckCapacity(ps1);
size_t end = ps1->size;
while (end > pos)
{
ps1->a[end] = ps1->a[end - 1];
--end;
}
ps1->a[pos] = x;
ps1->size++;
}
1.首先检查ps1是否为空,检查pos的值小于size的数量
2.检查容量,大小不合适就扩容
3.将end的值赋为元素的数量
4.当end>pos,将前一个元素赋给后一个元素,直至等与pos
5.将x的值放在pos的位置,size++
顺序表删除
void SLErase(SL* ps1, size_t pos)// 顺序表删除pos位置的值
{
assert(ps1);
assert(ps1->size);
size_t begin = pos;
while (begin < ps1->size - 1)
{
ps1->a[begin] = ps1->a[begin + 1];
begin++;
}
ps1->size--;
}
1.把将要删除的值pos赋值给begin
2.通过循环遍历,将后一个元素赋值给前一个元素
3.最后size–
顺序表修改
void SLModify(SL* ps1, size_t pos, SLDataType x)
{
assert(ps1);
assert(ps1->size);
ps1->a[pos] = x;
}
1.直接修改在pos位置上的值