数据结构的概念:
数据结构是计算机存储、组织数据的方式
最基础的数据结构:数组
数组不满足于复杂场景数据的管理
(1)数组仅存储同类型的数据 int a[100] 、char a[100]
(2)数组可提供的接口不足以支撑复杂场景的数据处理
顺序表是线性表的一种
线性表
指的是具有相同特性的一类数据结构的统称
在逻辑结构上一定是线性的,但在物理结构上不一定是线性的
人为想象的 内存存储上
顺序表的底层结构是数组,所以顺序表在逻辑结构上是线性的,在物理结构上也是线性的
数组分为定长数组和动态数组
int a[100] 数组的大小不确定
动态内存开辟
顺序表的分类
顺序表分为两种:静态顺序表和动态顺序表
静态顺序表
静态顺序表: 缺点:空间给小了不够用,给多了造成空间浪费
struct SeqList{ .h文件 .c文件
int a[100]; 用于结构的实现和声明 函数的具体实现
int size;//有效数据个数 函数的声明
}
动态顺序表
动态顺序表:
Typedef struct SeqList{
Typedef* a; //指向动态开辟的数组
int size; //有效数据的个数
int capacity; //容量空间大小
}
接下来,我们就来探究一下什么是尾插、头插、尾删、头删
注意:我们在进入的时候,需要先使用断言判断是否为NULL(暴力判断)
另外,无论是尾插还是头插这些,我们都必须在使用后,把申请的空间释放掉,然后把指向该地址的指针设置为NULL空指针
尾插
顾名思义就是从内存尾部对内存进行插入数据
1、判断顺序表是否有足够的空间
(1)有足够的空间就可以直接插入 ps -> a[ps -> size]插入数据就可以了
(2)空间不够,需要扩容,扩容怎么扩?申请多少个空间?
每次增加一个数据,就申请一块空间,假如现在要插入100个数据,则需要申请100次
如果频繁地扩容,会降低程序的性能
注:ps -> a[ps -> size] 是有效的数据个数=最后一个数据的下一个位置
在堆区处:
加入所占的20个字节的空间不足,它会在其他空间地址处申请一块21字节的空间,然后把数据拷贝一份到该空间处,然后把那块20字节空间的内存释放掉,从而来实现扩容
所以在增容的时候,默认一般以2倍或者1.5倍进行扩容
扩容又分为原地扩容和异地扩容
原地扩容
若扩容是时候,后面的空间足够让该数组进行扩容,就进行原地扩容
异地扩容
如果后面的空间被其他的内存占据,无法进行扩容,就会使用异地扩容,在其他的空间开辟一块空间,满足扩容后的需求,从而来达到扩容
此时就会把原数组拷贝一份放到新空间的地方,然后系统自动把旧空间的地址释放掉,进行接下来的扩容步骤
顺序表会存在多次的扩容操作,所以要用到动态内存开辟的函数:
malloc realloc calloc
申请一段连续的空间 可以进行容量的大小 跟malloc类似
调整,一般选用该函数 增加了初始化的操作
头插
顾名思义就是从内存头部对内存进行插入数据
1、判断顺序表是否有足够的空间
(1)有足够的空间就可以让数组内的数依次先往后面移一个位置,然后往头部插入
(2)若空间不够,需要扩容,进入扩容函数
尾删
尾删的话直接在数组后面让size--就好了,此时最后一个数字会被自动覆盖
头删
头删就是把第一个数删除掉之后,让后面的数依次往前移动一位
任意插入
任意插入需要在尾插头插的基础上,再加上一个任意插入的位置postion,来表示插入的位置,然后在这个位置的基础上往后移动一位后,再来进行插入操作
任意删除
和任意插入相反,这个是先进行删除操作,然后接着再进行从后往前移动一位,把前面的空缺bu'shan
test.c
#include<stdio.h>
#include"SeqList.h"
//long long Fib(size_t N)
//{
// if (N < 3)
// return 1;
//
// return Fib(N - 1) + Fib(N - 2);
//}
//
//int main()
//{
// printf("%lld\n", Fib(6));
//
// return 0;
//}
//void func1()
//{
// int a = 0;
// printf("%p\n", &a);
//}
//
//void func2()
//{
// int b = 0;
// printf("%p\n", &b);
//}
void TestSL1()
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
SLPushBack(&sl, 6);
SLPushBack(&sl, 7);
SLPushBack(&sl, 8);
SLPushBack(&sl, 9);
SLPrint(&sl);
SLPushFront(&sl, 10);
SLPushFront(&sl, 20);
SLPushFront(&sl, 30);
SLPushFront(&sl, 40);
SLPrint(&sl);
SLDestroy(&sl);
}
void TestSL2()
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
SLPrint(&sl);
SLPopBack(&sl);
SLPrint(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLPrint(&sl);
//SLPopBack(&sl);
//SLPrint(&sl);
SLPushFront(&sl, 10);
SLPushFront(&sl, 20);
SLPushFront(&sl, 30);
SLPushFront(&sl, 40);
SLPrint(&sl);
SLDestroy(&sl);
}
// 多画图
// 写一个函数,编译一个 测试一个 -> 一步一个脚印
void TestSL3()
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
SLPopFront(&sl);
SLPrint(&sl);
//SLPopFront(&sl);
//SLPrint(&sl);
}
void TestSL4()
{
//SL* ptr = NULL;
//SLInit(ptr);
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
SLPrint(&sl);
SLInsert(&sl, 2, 20);
SLPrint(&sl);
SLInsert(&sl, 6, 20);
SLPrint(&sl);
SLInsert(&sl, 0, 20);
SLPrint(&sl);
SLInsert(&sl, 10, 20);
SLPrint(&sl);
SLDestroy(&sl);
}
void TestSL5()
{
SL sl;
SLInit(&sl);
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPushBack(&sl, 5);
SLPrint(&sl);
SLErase(&sl, 3);
SLPrint(&sl);
SLErase(&sl, 3);
SLPrint(&sl);
//SLErase(&sl, 3);
//SLPrint(&sl);
}
int main()
{
TestSL5();
/*int* p1 = (int*)malloc(40);
printf("%p\n", p1);
int* p2 = (int*)realloc(p1, 800);
printf("%p\n", p2);*/
// 越界一定报错吗?
//int a[10];
越界读基本不会报错
//printf("%d\n", a[10]);
//printf("%d\n", a[11]);
越界写可能会报错
a[10] = 1;
a[11] = 1;
//a[15] = 1;
return 0;
}
SeqList.c
#include"SeqList.h"void SLInit(SL* psl)
{
assert(psl);
psl->a = NULL;
psl->size = 0;
psl->capacity = 0;
}
void SLDestroy(SL* psl)
{
assert(psl);
if (psl->a != NULL)
{
free(psl->a);
psl->a = NULL;
psl->size = 0;
psl->capacity = 0;
}
}
void SLPrint(SL* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->a[i]);
}
printf("\n");
}
void SLCheckCapacity(SL* psl)
{
assert(psl);
if (psl->size == psl->capacity)
{
int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType)*newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
psl->a = tmp;
psl->capacity = newCapacity;
}
}
void SLPushBack(SL* psl, SLDataType x)
{
assert(psl);
SLCheckCapacity(psl);
psl->a[psl->size] = x;
psl->size++;
}
void SLPushFront(SL* psl, SLDataType x)
{
assert(psl);
SLCheckCapacity(psl);
// 挪动数据
int end = psl->size - 1;
while (end >= 0)
{
psl->a[end + 1] = psl->a[end];
--end;
}
psl->a[0] = x;
psl->size++;
}
void SLPopBack(SL* psl)
{
assert(psl);
// 空
// 温柔的检查
/*if (psl->size == 0)
{
return;
}*/
// 暴力检查
assert(psl->size > 0);
//psl->a[psl->size - 1] = -1;
psl->size--;
}
// 10:47
void SLPopFront(SL* psl)
{
assert(psl);
// 暴力检查
assert(psl->size > 0);
int begin = 1;
while (begin < psl->size)
{
psl->a[begin - 1] = psl->a[begin];
++begin;
}
psl->size--;
}
// 注意pos是下标
// size是数据个数,看做下标的话,他是最后一个数据的下一个位置
void SLInsert(SL* psl, int pos, SLDataType x)
{
assert(psl);
assert(pos >= 0 && pos <= psl->size);
SLCheckCapacity(psl);
// 挪动数据
int end = psl->size - 1;
while (end >= pos)
{
psl->a[end + 1] = psl->a[end];
--end;
}
psl->a[pos] = x;
psl->size++;
}
void SLErase(SL* psl, int pos)
{
assert(psl);
assert(pos >= 0 && pos < psl->size);
// 挪动覆盖
int begin = pos + 1;
while (begin < psl->size)
{
psl->a[begin - 1] = psl->a[begin];
++begin;
}
psl->size--;
}
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
//typedef int shujuleixing;
// sequence list
typedef struct SeqList
{
SLDataType* a;
int size; // 有效数据
int capacity; // 空间容量
}SL;
void SLInit(SL* psl);
void SLDestroy(SL* psl);
void SLPrint(SL* psl);
void SLCheckCapacity(SL* psl);
// 头尾插入删除
void SLPushBack(SL* psl, SLDataType x);
void SLPushFront(SL* psl, SLDataType x);
void SLPopBack(SL* psl);
void SLPopFront(SL* psl);
// 任意下标位置的插入删除
void SLInsert(SL* psl, int pos, SLDataType x);
void SLErase(SL* psl, int pos);