文章目录
前言
作用:
顺序表在现实中有许多应用场景。例如,可以用来存储学生成绩信息,每个学生的成绩用一个结构体来表示,然后将每个学生的信息按照学号的顺序存储在顺序表中,这样可以方便地进行查找、排序和统计操作。另外,顺序表也可以用来实现图书馆借阅管理系统中的图书链表,每个节点存储一本书的相关信息,如书名、作者、出版日期等。借阅书籍时,可以通过链表的操作实现借书和还书的功能。
前提:
顺序表的实现我们需要用到结构体,指针,动态内存开辟相关的知识。
为了让代码更加清晰有逻辑性,方便,具有可移植性,我们可以创建3个分项目源文件test.c ,源文件SeqList.c 和头文件SeqList.h,分别完成1.顺序表的测试逻辑、2. 顺序表插入、删除数据等函数进行实现、3.顺序表中需要的数据类型和函数的声明。
一.概念及结构
概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
结构
1. 静态顺序表:
使用定长数组存储元素。
静态顺序表创建空间时为静态开辟,不用malloc函数,代码相对简单(一点点),不存在内存泄露问题。
2. 动态顺序表:
使用动态开辟的数组存储。
动态顺序表,动态开辟空间,使用相对灵活,相比于静态开辟空间也可以少空间浪费。
二.动态顺序表的实现
1.头文件SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define SLDataType int
typedef struct Seqlist
{
SLDataType* arr;
int size; //有效数据
int capacity; //空间容量
}SL;//定义结构体SL
//顺序表的初始化,销毁
void SLInit(SL* ps);
void SLDestory(SL* ps);
//检查空间是否足够
void SLCheckCapacity(SL* ps);
//头插尾插头删尾删特定位置删插
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
void SLErase(SL* ps, int pos);
void SLInsert(SL* ps, int pos, SLDataType x);
//打印
void SLPrint(SL* ps);
这里完成对顺序表中需要的数据类型和函数的声明。
2.源文件SeqList.c
初始化顺序表
void SLInit(SL* ps)
{
assert(ps);
ps->arr = NULL;
ps->size = 0;
ps->capacity = 0;
}
检查顺序表空间是否足够
void SLCheckCapacity(SL* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newCapacity);
assert(tmp);
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
当有效数据和空间容量相等时说明空间不足,有进行扩容
if (ps->size == ps->capacity)
?a:b是三目运算符,说明如果前面成立则返回?后的值,不成立则返回:后的值。
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
由于我们初始化ps->size=ps->capacity=0,故第一次返回值为4.
SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newCapacity);
assert(tmp);
ps->arr = tmp;
ps->capacity = newCapacity;
先用tmp开辟新空间,防止realloc开辟失败返回空指针使ps->arr被置空。最后将tmp赋给ps->arr,newCapacity赋给capacity.
顺序表的销毁
void SLDestory(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
}
ps->capacity = ps->size = 0;
}
将ps->arr置空,ps->size,ps->capacity置为0.
顺序表的打印
void SLPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
实现尾插
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
ps->size为顺序表最后一个数据的下一个位置,直接将要插入的值赋给ps->size,赋值完后记得有ps->size++(->是成员提取,说明是结构体ps中的size成员)
实现头插
void SLPushFront(SL* ps,SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size-1; i>=0; i--)
{
ps->arr[i+1] = ps->arr[i];
}
ps->arr[0] = x;
ps->size++;
}
这里的思路是将所有数据整体向后移动一位,在将ps->arr的首元素赋值为x
实现尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size > 0);
ps->size--;
}
由于顺序表的打印只打印到ps->arr[size-1],当size–后,最后一位不打印从而实现尾删。
实现头删
void SLPopFront(SL* ps)
{
assert(ps);
for (int i=1;i<=ps->size - 1 ;i++)
{
ps->arr[i-1] = ps->arr[i];
}
ps->size--;
}
ps->arr中第二为开始整体向前移动一位。
实现指定位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("输入错误");
return;
}
for (int end = ps->size - 1; end > pos;end--)
{
ps->arr[end+1] = ps->arr[end];
}
ps->arr[pos] = x;
ps->size++;
}
与头插类似,只是从指定位置的数开始整体向后移动一位,再插入x.
实现指定位置删除
void SLErase(SL* ps, int pos)
{
if (pos < 0 || pos>ps->size)
{
printf("输入错误");
return;
}
for (int end =pos; end<ps->size-1; end++)
{
ps->arr[end] = ps->arr[end + 1];
}
ps->size--;
}
指定位置为界限,前面的整体前移,或后面整体后移,将指定位置覆盖掉。
注意:前移的时候从左边开始移动,后移从右边开始移动。不然移动的数都会被一个数覆盖掉。
SeqList.c整体代码:
#include"SeqList.h";
void SLInit(SL* ps)
{
assert(ps);
ps->arr = NULL;
ps->size = 0;
ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newCapacity);
assert(tmp);
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void SLDestory(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
}
ps->capacity = ps->size = 0;
}
void SLPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
void SLPushFront(SL* ps,SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size-1; i>=0; i--)
{
ps->arr[i+1] = ps->arr[i];
}
ps->arr[0] = x;
ps->size++;
}
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size > 0);
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
for (int i=1;i<=ps->size - 1 ;i++)
{
ps->arr[i-1] = ps->arr[i];
}
ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("输入错误");
return;
}
for (int end = ps->size - 1; end > pos;end--)
{
ps->arr[end+1] = ps->arr[end];
}
ps->arr[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
if (pos < 0 || pos>ps->size)
{
printf("输入错误");
return;
}
for (int end =pos; end<ps->size-1; end++)
{
ps->arr[end] = ps->arr[end + 1];
}
ps->size--;
}
3.源文件test.c
#include"SeqList.h";
int main()
{
SL s;
SLInit(&s);
SLPushBack(&s , 2);//2
SLPushBack(&s , 3);//2 3
SLPushBack(&s , 4);//2 3 4
SLPushFront(&s, 1);//1 2 3 4
SLPopBack(&s);// 1 2 3
SLPopFront(&s);//2 3
SLInsert(&s, 1, 4);//2 4 3
SLErase(&s, 2);//2 4
SLPrint(&s);
SLDestory(&s);
return 0;
}
运行一下结果:
三.结束语
本篇到此结束,喜欢或者有帮助的话三连支持一下吧,欢迎指正不足之处,不胜感激!