数据结构是由“数据”和“结构”两词组合而来。
常见的数值1、2、3、4…、学校保存的身份信息(姓名、性别、年龄、学历等等)、网页肉眼可以看到的信息(文字、图片、视频等等),这些都是数据。
当我们想要使用大量同一类型的数据时,通过手动定义大量的独立的变量对于程序来说,可读性非常差,我们可以借助数组这样的数据结构将大量的数据组织在一起,结构也可以理解为组织数据的方式。
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。 而最基础的数据结构就是数组。
顺序表是线性表的一种,那什么是线性表呢?
线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的⼀条直线。 但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表的最底层结构是数组,所以顺序表在逻辑结构上是线性的,在物理结构上也是线性的。 了解了线性表之后,那顺序表呢? 顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口。
使用定长数组存储元素。
//静态顺序表
struct Seqlist{
int a[100];
int size;
}
缺点: 空间初始时给小了,造成不够用; 空间初始给大了,造成了浪费。 在实际场景中,空间不够造成数据无法保存,就可能造成数据的丢失。
//动态顺序表
struct Seqlist{
int* a;
int size;//有效数据个数
int capacity;//空间大小
}
在这里用三个文件SeqList.h、SeqList.c、test.c分别实现。 在顺序表中实现多次扩容,所以用realloc可以进行容量大小的调整。 插入时都需要判断空间容量是否足够?顾可以单独定义一个函数SLCheckCapacity,用来判断容量是否足够,若不够,则扩容。
void SLCheckCapacity(SL* ps) {
if (ps->size == ps->capacity) {
//空间不足以再额外插入一个数据
//扩容
int newCapcity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapcity * sizeof(SLDataType));
if (tmp == NULL) {
perror("realloc fail!\n");
return 1;
}
ps->a = tmp;
ps->capacity = newCapcity;
}
}
尾部插入SLPushBack
//尾插
void SLPushBack(SL* ps, SLDataType x) {
//assert(ps != NULL);
//暴力的方式
assert(ps);
//柔和方式
//if (ps == NULL) {
// return;
//}
//1)空间足够,直接尾插
//2)空间不够,扩容
SLCheckCapacity(ps);
//直接插入数据
ps->a[ps->size++] = x;
}
头部插入SLPushFront
//头插
void SLPushFront(SL* ps, SLDataType x) {
assert(ps);
//判断空间是否足够,不够则扩容
SLCheckCapacity(ps);
//空间足够,历史数据后移一位
for (size_t i = ps->size; i > 0 ; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
尾部删除SLPopBack
void SLPopBack(SL* ps) {
//判断顺序表是否为空
assert(ps);
assert(!SLIsEmpty(ps));
//ps->a[ps->size - 1] = 0;
ps->size--;
}
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size; //顺序表中有效的数据个数
int capacity; //顺序表当前的空间大小
}SL;
//typedef struct SeqList SL;
//对顺序表进行初始化
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//头部/尾部 插入/删除
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
//void SLPopFront(SL* ps);
void SLPrint(SL* ps);
bool SLIsEmpty(SL* ps);
#include"SeqList.h"
void SLInit(SL* ps) {
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps) {
if(ps->a)
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps) {
if (ps->size == ps->capacity) {
//空间不足以再额外插入一个数据
//扩容
int newCapcity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapcity * sizeof(SLDataType));
if (tmp == NULL) {
perror("realloc fail!\n");
return 1;
}
ps->a = tmp;
ps->capacity = newCapcity;
}
}
//尾插
void SLPushBack(SL* ps, SLDataType x) {
//assert(ps != NULL);
//暴力的方式
assert(ps);
//柔和方式
//if (ps == NULL) {
// return;
//}
//1)空间足够,直接尾插
//2)空间不够,扩容
SLCheckCapacity(ps);
//直接插入数据
ps->a[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDataType x) {
assert(ps);
//判断空间是否足够,不够则扩容
SLCheckCapacity(ps);
//空间足够,历史数据后移一位
for (size_t i = ps->size; i > 0 ; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
//尾删
void SLPopBack(SL* ps) {
//判断顺序表是否为空
assert(ps);
assert(!SLIsEmpty(ps));
//ps->a[ps->size - 1] = 0;
ps->size--;
}
//void SLPopFront(SL* ps) {
//}
void SLPrint(SL* ps) {
for (size_t i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
bool SLIsEmpty(SL* ps) {
assert(ps);
//这样子是不对的,这里只能判断空间是否足够
//return ps->size == ps->capacity;
return ps->size == 0;
}
#include"SeqList.h"
void SLtest() {
SL sl;
SLInit(&sl);
//顺序表的具体操作
//SLPushBack(&sl, 1);
//SLPushBack(&sl, 2);
//SLPushBack(&sl, 3);
//SLPushBack(&sl, 4);//1 2 3 4
//SLPrint(&sl);
头插
//SLPushFront(&sl, 5);//5 1 2 3 4
//SLPushFront(&sl, 6);//6 5 1 2 3 4
//SLPushFront(&sl, 7);//7 6 5 1 2 3 4
//SLPrint(&sl);
//尾删
SLPopBack(&sl);
SLPrint(&sl);
SLPopBack(&sl);
SLPrint(&sl);
SLDestroy(&sl);
}
int main() {
SLtest();
return 0;
}