数据结构是由“数据”和“结构”两词组合而来。
什么是数据?
常见的数值1、2、3、4…、教务系统里保存的用户信息(姓名、性别、年龄、学历等等)、网页里肉眼可以看到的信息(文字、图片、视频等等),这些都是数据。
什么是结构?
当我们想要大量使用同⼀类型的数据时,通过手动定义大量的独立的变量对于程序来说,可读性非常差,我们可以借助数组这样的数据结构将⼤量的数据组织在⼀起,结构也可以理解为组织数据的方式。
概念: 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由哪部分构成,以什么方式构成,以及数据元素之间呈现的结构。
总结: 1)能够存储数据(如顺序表、链表等结构) 2)存储的数据能够方便查找
程序中如果不对数据进行管理,可能会导致数据丢失、操作数据困难、野指针等情况。通过数据结构,能够有效将数据组织和管理在⼀起。按照我们的方式任意对数据进行增删改查等操作。
最基础的数据结构:数组。
【思考】有了数组,为什么还要学习其他的数据结构?
假定数组有10个空间,已经使用了5个,向数组中插入数据的步骤:
结论: 最基础的数据结构能够提供的操作已经不能完全满足复杂算法实现。
线性表:
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的⼀条直线;但是在物理结构上并不⼀定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表:
逻辑结构是线性的、物理结构是连续的。
顺序表和数组的区别:
顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口。
概念:使用定长数组存储元素
//静态顺序表
#define N 100
typedef int SLDataType;//顺序表中数组类型不一定是整型,如果要变为字符类型,就可以在这里直接改;如果不这样定义,就要在代码中改很多次
struct SeqList
{
SLDataType a[N];//定长数组
int size;//有效数据个数
};
静态顺序表缺陷:空间给少了不够⽤,给多了造成空间浪费
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* arr;//存储数据的底层结构
int capacity;//记录顺序表的空间大小
int size;//记录顺序表当前有效的数据个数
}SL;
//typedef struct SeqList SL;
void SLInit(SL* ps)
{
assert(ps);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newCapacity = 0 == ps->capacity ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
if (NULL == tmp)
{
perror("realloc fail!");
exit(1);
}
//扩容成功
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void SLPushBack(SL* ps, SLDataType x)
{
//断言 -- 粗暴的解决方式
//assert(ps != NULL);
assert(ps);
//if判断 -- 温柔的解决方式
//if (NULL == ps)
//{
// return;
//}
//空间不够,扩容
SLCheckCapacity(ps);
//空间足够,直接插入
ps->arr[ps->size++] = x;
//ps->size++;
}
注: 扩容的原则:成倍数的增加(1.5倍、2倍)
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
//判断是否扩容
SLCheckCapacity(ps);
//旧数据往后挪动一位
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
//顺序表不为空
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
//不为空执行挪动操作
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
//pos及之后的数据往后挪动一位,pos空出来
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
//pos以后的数据往前挪动一位
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
int SLFind(SL* ps, SLDataType x)
{
//加上断言对代码的健壮性更好
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (x == ps->arr[i])
{
return i;
}
}
return -1;
}
void SLDestroy(SL* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
完整代码:
//SeqList.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
静态顺序表
//
//#define N 100
//
//typedef int SLDataType;
//
//struct SeqList
//{
// SLDataType a[N];
// int size;
//};
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* arr;//存储数据的底层结构
int capacity;//记录顺序表的空间大小
int size;//记录顺序表当前有效的数据个数
}SL;
//typedef struct SeqList SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);//保持接口一致性
//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
//顺序表的头部/尾部删除
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//指定位置之前插入数据
//删除指定位置数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);
//SeqList.c
#include "SeqList.h"
//初始化和销毁
void SLInit(SL* ps)
{
assert(ps);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newCapacity = 0 == ps->capacity ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
if (NULL == tmp)
{
perror("realloc fail!");
exit(1);
}
//扩容成功
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDataType x)
{
//断言 -- 粗暴的解决方式
//assert(ps != NULL);
assert(ps);
//if判断 -- 温柔的解决方式
//if (NULL == ps)
//{
// return;
//}
//空间不够,扩容
SLCheckCapacity(ps);
//空间足够,直接插入
ps->arr[ps->size++] = x;
//ps->size++;
}
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
//判断是否扩容
SLCheckCapacity(ps);
//旧数据往后挪动一位
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
//顺序表的头部/尾部删除
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
//顺序表不为空
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
//不为空执行挪动操作
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
//pos及之后的数据往后挪动一位,pos空出来
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
//删除指定位置数据
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
//pos以后的数据往前挪动一位
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//在顺序表中查找x
int SLFind(SL* ps, SLDataType x)
{
//加上断言对代码的健壮性更好
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (x == ps->arr[i])
{
return i;
}
}
return -1;
}
void SLDestroy(SL* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
//test.c
#include "SeqList.h"
void slTest01()
{
SL sl;
SLInit(&sl);
//测试尾插
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPrint(&sl);
//SLPushBack(&sl, 5);
//SLPrint(&sl);
//SLPushBack(NULL, 6);
//头插
//SLPushFront(&sl, 5);
//SLPushFront(&sl, 6);
//SLPushFront(&sl, 7);
//SLPrint(&sl);
//尾删
//SLPopBack(&sl);
//SLPopBack(&sl);
//SLPopBack(&sl);
//SLPopBack(&sl);
//SLPrint(&sl);
//头删
//SLPopFront(&sl);
//SLPopFront(&sl);
//SLPopFront(&sl);
//SLPopFront(&sl);
//SLPrint(&sl);
//指定位置插入
//SLInsert(&sl, 0, 100);
//SLPrint(&sl);
//SLInsert(&sl, sl.size, 200);
//SLPrint(&sl);
//SLInsert(&sl, 100, 300);
//SLPrint(&sl);
//删除指定位置的数据
//SLErase(&sl, 0);
//SLPrint(&sl);
//SLErase(&sl, sl.size - 1);
//SLPrint(&sl);
SLErase(&sl, 1);
SLPrint(&sl);
}
void slTest02()
{
SL sl;
SLInit(&sl);
//测试尾插
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLPrint(&sl);
//测试查找
//int ret = SLFind(&sl, 3);
int ret = SLFind(&sl, 30);
if (ret < 0)
{
printf("数据不存在,查找失败!");
}
else
{
printf("数据找到了,在下标为%d位置\n", ret);
}
}
int main()
{
//slTest01();
slTest02();
return 0;
}