这里引用度娘的话
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
好的程序 = 合适的数据结构 + 算法,学好数据结构对程序员来说是一件重要的事。
数据结构分为线性结构和非线性结构,线性表是线性结构中的一种,之前我们已经学习了C语言,那么接下来我们先用C语言实现我们学习的第一种数据结构:线性表中的顺序表。主要内容包括顺序表的创建、初始化、增、删、查、改等,因为本人也是C语言小白,比较理解小白的感受,我尽量详细的讲解!
线性表是n个具有相同特性的数据元素的有限序列。线性表在实际中使用广泛,包括顺序表、链表、栈、队列、字符串……
线性表在逻辑上是线性结构,但在物理上不一定。线性表在物理上的储存通常以数组和链式结构进行。
顺序表是用一段连续的内存空间依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。可分为:
11.3号晚,英语考的好烂,不开心,继续学编程……
几点简单的说明
//先创建,再重命名
struct Student
{
char name[20];
size_t age;
};
typedef Struct Student St;
//同时创建和重命名
typedef struct Student
{
char name[20];
size_t age;
} St;
创建一个静态顺序表
#define N 1000
typedef int SLDataType;
typedef struct SeqList
{
SLDataType arr[N]; // 定长数组
size_t size; // 有效数据的个数
}SL;
这种方法的弊端是十分明显的,如果我们只需要存储10个值,而我们开辟的空间过大,或者我们开辟的空间不足,都是非常麻烦的,因此我们基本不使用这种顺序表
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要来动态地分配空间大小,所以下面我们实现动态顺序表。
typedef struct SeqList
{
SLDataType* ps; // 指向动态开辟的数组
size_t size; // 有效数据个数,即当前存储数据的个数
size_t capicity; // 容量空间的大小
}SL;
#define MAX_SIZE 4
//我们用函数来实现动态顺序表的初始化
void SeqListInit(SL* ps)//把需要初始化的结构体s的指针传过来,因为我们知道,如果传过来的是形式参数,我们将无法对原结构体进行修改
{
ps->ps=NULL;//将指针置为空,后面扩容时再对指针赋值
ps->size=ps->capicuty=0;//把顺序表的当前大小和容量重置为0,这种情况下我们可以用连续赋值(我太懒了)
}
注意:我们后面说的头插,就是插入到第一个位置,我们所说的pos位置,就是pos下标对应的位置。
我们通过调用函数来实现动态顺序表的一些操作,这里我们先看一下我们都需要哪些函数,及这些函数实现的功能
// 检查空间,如果满了,进行增容,因为我们每次调用有关增加内容的函数时,都需要检查空间是否已满,因此先实现这个函数
void CheckCapacity(SeqList* ps);
// 动态顺序表头插,后面依次后移
void SeqListPushFront(SeqList* psl, SLDataType x);
// 动态顺序表头删,后面依次前移
void SeqListPopFront(SeqList* ps);
// 动态顺序表尾插
void SeqListPushBack(SeqList* ps, SLDataType x);
// 动态顺序表尾删
void SeqListPopBack(SeqList* ps);
// 动态顺序表查找
int SeqListFind(SeqList* ps, SLDataType x);
// 动态顺序表在pos位置插入x,原pos位及后面的都后移
void SeqListInsert(SeqList* ps, size_t pos, SLDataType x);
// 动态顺序表删除pos位置的值,pos位后面的前移
void SeqListErase(SeqList* ps, size_t pos);
// 动态顺序表打印
void SeqListPrint(SeqList* ps);
// 动态顺序表销毁
void SeqListDestory(SeqList* ps);
检查动态顺序表是否已满,已满则扩容为原来的二倍,为0则初始化为4:
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)//如果容量已满
{
int newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);//如果是第一次调用则容量设为4,否则扩充为原来的二倍
SLDataType* tmp = (SLDataType*)realloc(ps->ps, newcapacity * sizeof(SLDataType));//realloc指把第一个参数指向的位置扩容到第二个参数表示的大小的字节数,我们也需要把该函数返回值强转为SLDataType*型
if (tmp == NULL)//开辟空间失败
{
printf("realloc fail\n");
exit(-1);//退出程序,并且退出代码为-1,即括号内的参数
}
else
{
ps->ps = tmp;//把开辟的地址赋给结构体里的数组指针
ps->capacity = newcapacity;//扩容
}
}
}
动态顺序表在pos位置插入x:
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
assert(pos <= ps->size);//先确保pos指向的位置未越界,如果括号内表达式不成立,则终止程序执行
SeqListCheckCapacity(ps);//检查容量
int end = ps->size - 1;
while (end >= pos)//pos及后面的数据后移
{
ps->ps[end + 1] = ps->ps[end];
--end;
}
ps->ps[pos] = x;//pos位上赋值
ps->size++;//顺序表大小加一
}
动态顺序表头插:
其实这里我们只用调用在pos位置上插入x的函数就可以了(注释内容为另一种实现方法):
void SeqListPushFront(SL* ps, SLDataType x)
{
//SeqListCheckCapacity(ps);
1、初始条件
2、结束条件
3、迭代过程
//int end = ps->size - 1;
//while (end >= 0)
//{
// ps->ps[end + 1] = ps->ps[end];
// --end;
//}
//ps->ps[0] = x;
//ps->size++;
SeqListInsert(ps, 0, x);
}
动态顺序表尾插(注释内容为另一种实现方法):
void SeqListPushBack(SL* ps, SLDataType x)
{
/*SeqListCheckCapacity(ps);
ps->ps[ps->size] = x;
ps->size++;*/
SeqListInsert(ps, ps->size, x);
}
动态顺序表在pos位置删除:
void SeqListErase(SL* ps, int pos)
{
assert(pos < ps->size);
int start = pos + 1;
while (start < ps->size)//pos后面的数前移
{
ps->ps[start-1] = ps->ps[start];
++start;
}
ps->size--;
}
动态顺序表头删(注释内容为另一种实现方法):
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
/*
int start = 1;
while (start < ps->size)
{
ps->ps[start - 1] = ps->ps[start];
++start;
}
ps->size--;*/
SeqListErase(ps, 0);
}
动态顺序表尾删(注释内容为另一种实现方法):
void SeqListPopBack(SL* ps)
{
//assert(ps->size > 0);
ps->ps[ps->size - 1] = 0;
//ps->size--;
SeqListErase(ps, ps->size - 1);
}
动态顺序表遍历查找,找不到返回-1,找到返回下标:
int SeqListFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; ++i)
{
if (ps->ps[i] == x)
{
return i;
}
}
return -1;
}
动态顺序表的打印:
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; ++i)
{
printf("%d ", ps->ps[i]);
}
printf("\n");
}
动态顺序表把pos位置上的数修改为x:
void SeqListModity(SL* ps, int pos, SLDataType x)
{
assert(pos < ps->size);
ps->ps[pos] = x;
}
到这里,我们就搞定动态顺序表的基本功能了,后面我们会通过几道题进行练习巩固。