目录
1.0:定义一个动态的顺序表
//动态顺序表
typedef int SLDataType;
typedef struct SeqList {
SLDataType* a; //指向首元素地址
int size; //存储有效数据个数
int capacity //空间大小
}SL;
a指针指向首元素地址,方便对其进行访问和修改
size为有效个数
capacity为容量
2.0:各个功能实现
2.1:初始化
//初始化
void SLInit(SL*ps) {
assert(ps);
ps->a = (SLDataType*)malloc(sizeof(SLDataType)*4);
if (ps->a == NULL) {
perror("malloc failed");
exit(-1);
}
ps->size = 0;
ps->capacity = 4;
}
首先开辟了4个字节的空间,用于存放数据,由于开辟空间有可能会开辟失败,开辟失败pa->a就是一个空指针,所以要对其检验。此时,容量为4 有效个数为0
2.2:销毁
//销毁
void SLDestroy(SL* ps) {
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->size = 0;
}
我们最终要对其进行销毁,防止数据泄露或数据丢失。
释放掉开辟好的空间后,将指针定义为空指针,并使其他参量为0
2.3:测试查看
//测试查看
void SLPrint(SL* ps) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
printf("%d ",ps->a[i]);
}
printf("\n");
}
对目前所存放的数据进行打印,ps->a[i]语句表示通过ps指针找到结构体中的a(首元素地址),a[i]
表示*(a+i)找到对应的值并且打印出来。
2.4:检查是否需要扩容
//检查是否需要扩容
void SLCheckCapacity(SL* ps) {
assert(ps);
//满了要扩容
if (ps->size == ps->capacity) {
SLDataType* tmp = realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));
if (tmp == NULL) {
perror("realloc failed");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
}
如果首先开辟的四个字节的空间满了,我们就要对其进行扩容,继续开辟一块容量为原来容量两倍的空间,并且检查是否开辟失败,然后将容量扩大2倍。
这里要强调的是,需要定义一个指针来接收这块空间的起始地址,由于考虑到开辟失败的因素,所以这里不能继续使用原来的指针a来接受。
使用malloc函数需要引用头文件stdlib.h
异地扩容:
原地扩容:
强调一下,不论是原地扩容还是异地扩容,都需要创建一个新的指针来接收,这样做的好处是,如果创建失败,原来的数据不会丢失,还可以通过a指针找回。如果创建成功,那么将原指针(a)指向新指针(tmp)即可
2.5:头部插入
//头插
void SLPushFront(SL* ps, SLDataType x) {
assert(ps);
void SLCheckCapacity(ps);
//挪动数据
int end = ps->size - 1;
while (end >= 0) {
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
头部插入的思想就是将新的元素放到a[0]的位置,我们先检查是否需要扩容,再将原数据依次向后挪动,将a[0]的位置空出来,再赋予它新的元素
假设我们现在有4个元素,要实现头插,后面是有空间的不需要扩容,体会一下挪动的思想
挪动有两种方式,从前往后或者从后往前,我们来一一实现以下
从前往后挪:
从后往前挪:
最终,我们发现从前往后挪会篡改原数据的值,所以要从后往前挪。
最后通过ps->a[0] = x和ps->size++使头部插入我们需要的元素。
2.6:尾部插入
//尾插
void SLPushBack(SL* ps, SLDataType x) {
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
尾插我们还是先检查是否需要扩容,然后直接将需要的元素插入到最后一个位置即可
2.7:头部删除
//头删
void SLPopFront(SL* ps) {
assert(ps);
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size) {
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}
头删我们直接将后面的数据依次向前挪动即可,从后面开始依次挪动会造成数据篡改,如下:
所以需要从前面开始挪
最后通过size--将最后一位减去即可
2.8:尾部删除
//尾删
void SLPopBack(SL* ps) {
assert(ps);
//普通检查
/*if (ps->size == 0) {
return;
}*/
//暴力检查
assert(ps->size > 0);
ps->size--;
}
尾删之前我们需要对有效个数进行检查,只有最少有1个元素的时候我们才可以对其尾删,尾删过程直接将有效个数-1即可。
对于检查,这里使用暴力检查,通过断言,如果断言为真,才会进行下面的操作,断言为假,直接报错退出。当然,也可以使用普通检查,两者都是要确保至少存有一个元素,不过暴力检查比较实用一些。
2.9:寻找元素
//通过值寻找元素,返回下标,没找到返回-1
int SLFind(SL* ps, SLDataType x) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
if (ps->a[i] == x) {
return i;
}
}
return(-1);
}
我们把要寻找的元素的值传入,通过循环遍历的方式去寻找这个值,如果找到了就返回这个元素的下标,没找到就返回-1即可。
2.10:在指定位置插入
//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x) {
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos) {
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
例如,我们要在3的位置插入我们需要的值,首先要检查是否需要扩容,接着将3后面的元素依次向后挪动一位,从前面开始挪动同样会造成数据篡改,如下图示:
所以,我们需要的是从后面开始挪动。
最后,插入我们所需的值即可。
2.11:在指定位置删除
//删除pos位置的值
void SLErase(SL* ps, int pos) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size) {
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
假设我们要删除3这个位置的元素,只需要将3后面的元素依次向前即可。
这里我们依旧需要从前开始挪动,从后开始会篡改数据,如下所示:
所以,需要从后面开始挪动。
最后,将size-1即可。
2.12:修改指定位置的值
//修改
void SLModify(SL* ps, int pos, SLDataType x) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
修改指定位置时,我们直接利用下标,将下标索引的元素进行覆盖即可。
3.0:优化
3.1:优化头插
头插就相当于在第一个位置(下标为0处)插入
//头插
void SLPushFront(SL* ps, SLDataType x) {
assert(ps);
SLInsert(ps, 0, x);
}
3.2:优化尾插
尾插就相当于在最后一个有效位置(size-1处)插入
//尾插
void SLPushBack(SL* ps, SLDataType x) {
assert(ps);
SLInsert(ps, ps->size,x);
}
3.3:优化头删
头删就相当于在第一个位置(下标为0处)删除
//头删
void SLPopFront(SL* ps) {
assert(ps);
SLErase(ps, 0);
}
3.4:优化尾删
尾删就相当于在最后一个有效位置(size-1处)删除
//尾删
void SLPopBack(SL* ps) {
assert(ps);
SLErase(ps, ps->size - 1);
}
4.0:代码整体
4.1:test.c
# define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"SeqList.h"
int main() {
//创建
SL s1;
//初始化
SLInit(&s1);
//进行指令操作.......
//销毁
SLDestroy(&s1);
return 0;
}
4.2:SeqList.h
#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
//动态顺序表
typedef int SLDataType;
typedef struct SeqList {
SLDataType* a;
int size; //存储有效数据个数
int capacity; //空间大小
}SL;
//初始化
void SLInit(SL*ps);
//销毁
void SLDestroy(SL*ps);
//测试
void SLPrint(SL*ps);
//检查容量够不够
void SLCheckCapacity(SL*ps);
//头插头删 尾插尾删
//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);
//返回下标,没找到返回-1
int SLFind(SL* ps, SLDataType x);
//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);
//删除pos位置的值
void SLErase(SL* ps, int pos);
//修改
void SLModify(SL* ps, int pos, SLDataType x);
4.3:SeqList.c
#include"SeqList.h"
//初始化
void SLInit(SL*ps) {
assert(ps);
ps->a = (SLDataType*)malloc(sizeof(SLDataType)*4);
if (ps->a == NULL) {
perror("malloc failed");
exit(-1);
}
ps->size = 0;
ps->capacity = 4;
}
//销毁
void SLDestroy(SL* ps) {
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->size = 0;
}
//测试查看
void SLPrint(SL* ps) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
printf("%d ",ps->a[i]);
}
printf("\n");
}
//检查是否需要扩容
void SLCheckCapacity(SL* ps) {
assert(ps);
//满了要扩容
if (ps->size == ps->capacity) {
SLDataType* tmp = realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));
if (tmp == NULL) {
perror("realloc failed");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
}
//尾插
void SLPushBack(SL* ps, SLDataType x) {
assert(ps);
/*SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;*/
SLInsert(ps, ps->size,x);
}
//尾删
void SLPopBack(SL* ps) {
assert(ps);
//普通检查
/*if (ps->size == 0) {
return;
}*/
//暴力检查
/*assert(ps->size > 0);
ps->size--;*/
SLErase(ps, ps->size - 1);
}
//头插
void SLPushFront(SL* ps, SLDataType x) {
assert(ps);
//void SLCheckCapacity(ps);
挪动数据
//int end = ps->size - 1;
//while (end >= 0) {
// ps->a[end + 1] = ps->a[end];
// --end;
//}
//ps->a[0] = x;
//ps->size++;
SLInsert(ps, 0, x);
}
//头删
void SLPopFront(SL* ps) {
assert(ps);
/*assert(ps->size > 0);
int begin = 1;
while (begin < ps->size) {
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;*/
SLErase(ps, 0);
}
//通过值寻找元素,返回下标,没找到返回-1
int SLFind(SL* ps, SLDataType x) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
if (ps->a[i] == x) {
return i;
}
}
return(-1);
}
//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x) {
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos) {
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
//删除pos位置的值
void SLErase(SL* ps, int pos) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size) {
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
//修改
void SLModify(SL* ps, int pos, SLDataType x) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
最后,如有不足,请大家指教!!!