顺序表
前序,先简单认识一下顺序表的物理图
1.顺序表的定义
顺序表是在计算机内存中已数组形式保存的线性表
1.1 使用结构体来定义一个顺序表
typedef int SLDateType;
typedef struct SeqList
{
SLDateType * a;//a定义为指针类型是为了更好的动态扩容
int size;//目前有多少个元素
int capacity;//一共有多大的空间
}SeqList;
1.2 初始化顺序表
//初始化结构体顺序表
void SeqListInit(SeqList* ps)
{
ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4);//为结构体ps里的指针变量a申请动态空间内存
if (ps->a == NULL)
{
printf("INIT Seqlist Err");//如果申请失败指针变量a的值会为NULL
return;
}
ps->size = 0;//初始化目前元素为0个
ps->capacity = 4;//动态内存总空间为4,也就是说指针变量a的空间只能存放四个
}
如果这里不用动态申请,而使用静态的,当顺序表存满的时候,静态数组是无法二次扩容的,而把它定义为动态的是可以进行二次或更多次扩容的,唯一注意的是使用完动态内存后, 要记得释放,要不会出现内存泄漏的问题。
1.3 结构体顺序表尾部插入
//结构体尾部插入
void SeqListPushBack(SeqList* ps,SLDateType x)
{
assert(ps);
if (ps->size == ps->capacity)
{
SeqList* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity * 2);
if (tmp == NULL)
{
printf("realloc err");
return;
}
ps->a = tmp;
ps->capacity*=2;
}
ps->a[ps->size] = x;
ps->size++;
}
assert简称断言,这里引用他是为了防止你传进来的值为空。
if语句的作用是,判断你目前要插入的动态空间是否已经满了,如果条件符合,将会进行扩容,扩容到原来的二倍,如果扩容成功将会把扩容好的地址赋值给a,并将capacity的空间扩容为2倍,如果扩容失败返回NULL,将会提示报错并结束程序。
动态开辟空间malloc和realloc的区别,以及calloc
首先我们先看一下这三个函数的参数分别是什么
头文件均为 #include <stdlib.h>
,返回均值为void*,是为了更方便的转换成自己所需要的值。
malloc函数的作用是申请一块连续的空间,并且返回,如果申请成功,会返回这块申请好的连续空间,如果申请失败,会返回NULL
当申请的空间为0时,malloc不会返回空值,且申请好的0个空间也是不可用的。
当用完这段动态内存是,切记要free释放掉,且设置为空,要不会出现内存泄漏和野指针的问题。
这里简单了解一下free的作用,当使用完动态内存后,需要把所分配的地址内存还给系统,并且设置为空,如果你不释放且不设置为空,就会出现内存泄漏或者野指针的问题,就像你分手后,还不跟人家断了联系,那么下一个女朋友知道后会很麻烦,并且你脑子里的女朋友家地址也得忘记,如果不忘记将会很麻烦。
realloc函数的作用是将原目标的空间扩容到size大小,可大可小,并且申请的空间大小也是连续的,如果申请空间过大,它会跳过目前的内存,然后重找适合自己内存大小的位置,申请成功后返回这段空间,申请失败后返回NULL
同样使用完这段动态内存后也是需要free释放的。0
calloc函数的作用是申请num个size大小的字节空间(为连续空间),并且返回一个指向起始开辟空间的void* 类型的指针。
该函数会对申请的空间的数值做初始化,将每个字节的空间的数值赋值为0。
当空间申请失败时会返回一个空指
1.4顺序表尾部删除
//顺序表从尾部删除
void SeqListPoBack(SeqList* ps)
{
assert(ps->size>0);
ps->a[ps->size - 1] = 0;
ps->size--;
}
断言看顺序表里头是否有元素,如果没有直接暴力结束程序,如果有运行下面的代码。
把顺序表最后一个元素设为0,并且记录元素个数的size减一,来完成尾删。
1.5 顺序表打印
//输出结构体顺序表内容
void SeqListprint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
断言,如果a为空就结束程序
size为元素个数。
1.6 顺序表旋转及头部插入
//翻转顺序表
void reverse(SLDateType * a, int left, int size)
{
SLDateType tmp;
int right = size - 1;
while (left<right)
{
tmp = a[left];
a[left] = a[right];
a[right] = tmp;
left++;
right--;
}
}
//从顺序表头部插入
void SeqListPushFront(SeqList* ps,SLDateType x)
{
assert(ps);
if (ps->size == ps->capacity)
{
SeqList* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity * 2);
if (tmp == NULL)
{
printf("realloc err");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->size] = x;
ps->size++;
reverse(ps->a,0,ps->size);
reverse(ps->a,1,ps->size);
}
顺序表头插,插入时,要检查传过来的数组是否为空,如果为空结束程序,接的检查动态空间是否够用,如果不够,引用realloc函数,将他扩容到原空间大小的二倍。
这里我们进行的是尾插,然后通过调用翻转函数 reverse 进行翻转,使尾插的元素旋转到第一个。
1.7顺序表表头删除
//从顺序表头部删除
void SeqListPoFront(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size-1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
顺序表头删,依次将后面的数据挪动到自己前面的一个位置,完成后元素个数减减。
1.8 顺序表查找
//顺序表查找
void SeqListFind(SeqList* ps, SLDateType x)
{
assert(ps);
int left = 0;
int right = ps->size-1;
while (left<right)
{
int num = (left + right) / 2;
if (ps->a[num]>x)
{
right = num - 1;
}
else
{
if (ps->a[num] < x)
{
left = num + 1;
}
else
{
if (ps->a[num] == x)
{
printf("找到了,下标为:%d\n", num);
return;
}
}
}
}
printf("没有找到\n");
}
这里引用了折半查找法
1.9 顺序表pos位置插入
//在指定的pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
assert(ps);
SeqListPushBack(ps, x);
reverse(ps->a,pos, ps->size);
reverse(ps->a, pos+1, ps->size);
}
尾插后,从pos位置到尾部旋转,然后跳过pos位置选择pos位置之后的元素,来完成pos位置插入
1.10 顺序表pos位置删除
//顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("Erase Err");
return;
}
for (int i = pos; i < ps->size-1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
1.11 顺序表销毁
//销毁结构体顺序表动态空间
void SeqListDestroy(SeqList* ps)
{
free(ps);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
当使用完动态空间后,要进行free释放,并且置空。