顺序表:将元素顺序地放在一块被开辟出来的连续的内存空间里,其逻辑相邻,物理也相邻
不定长顺序表:即是在插入某个数据时,超过了我们目前内存空间的大小,那么我们就需要对其进行扩容操作,其内存空间大小是由我们申请开辟的,不是固定的。
因为其内存空间大小是不确定的,那么我们在设计时,除了其存储空间基址外,还需要有两个变量,一个是有效数据的长度,另一个则是内存空间大小。
当有效数据长度和内存空间大小相等,而我们要进行数据插入时,就要对其进行扩容
elem(空间基址) | 1 | 2 | 3 | 4 | 5 | 6 | ... | 数据 |
0 | 1 | 2 | 3 | 4 | 5 | ... | 下标 | |
length(有效长度) | ||||||||
list_size(目前空间大小) |
时间复杂度:
插入/删除数据时间复杂度O(n),需要挪动数据
尾部插入和删除时间复杂度O(1),不需要挪动数据
下边是代码实现
结构体设计
定义其初始空间为MAX_SIZE 值为100
#define MAX_SIZE 100
typedef int ElemType;
typedef struct Sqlist {
ElemType* elem; //存储空间基址
int length; //有效长度
int list_size; //当前容量大小
}Sqlist,*PSqlist;
不定长顺序表 初始化
对每个指针都要进行断言,可以使用assert (头文件 #include <assert.h>) 也可以加 if 判断,assert 只在 debug 版本有用,在release 版本就被忽视掉了,所以我这儿两个都写着。
先通过(malloc)申请空间,其空间大小初值为MAX_SIZE 100,数据初始有效长度为0,目前空间大小也更改为申请的空间内存大小(MAX_SIZE (100))
//初始化
void Init_sqlist(struct Sqlist* plist) {
assert(plist != NULL); //Debug
if (plist == NULL) return; //Release
plist->elem = (ElemType*)malloc(MAX_SIZE * sizeof(ElemType));
assert(plist->elem != NULL);
if (plist->elem == NULL) return;
plist->length = 0; //初始长度为0
plist->list_size = MAX_SIZE; //目前空间大小为MAX_SIZE 自己宏定义的100
}
先根据思路来写,进行代码插入、删除操作,那就要进行判满和判空(插入判满,删除判空,满了需要进行扩容,因此还要有扩容操作)
判满
简写 就是 length == list_size 可以理解为存储数据的有效长度(存储数据所占用的有效空间大小) == 目前所申请的内存空间大小
如果满了则返回为真(true)
//判满
bool IsFull(PSqlist plist) {
assert(plist != NULL);
if (plist == NULL) return false;
return plist->length == plist->list_size;
}
ps:最后一行 return plist -> length == plist -> list_size 不能写成 plist->length == MAX_SIZE; 因为如果扩容了的话那么当前容量大小就不是MAX_SIZE了
扩容
先进行判满操作,返回是真,就说明满了,则调用扩容函数
void Inc(PSqlist plist) {
assert(plist != NULL);
if(plist == NULL) return;
plist->elem = (ElemType*)realloc(plist->elem, (plist->list_size * sizeof(ElemType)) * 2); //扩容2倍
plist->list_size *= 2; //目前内存空间大小
}
ps:进行扩容操作,不要忘了后面 list_size 也要进行扩容修改
判空
链表的有效长度是否是 0(plist -> length == 0)
//判空
bool IsEmpty(PSqlist plist) {
assert(plist != NULL);
if (plist == NULL) return false;
return plist->length == 0;
}
插入 —— 按位置插入
插入位置后面的数据都向后移动
//插入
bool Insert(PSqlist plist, int pos, int val) {
assert(plist != NULL);
if (plist == NULL) return false;
assert(pos >= 0 && pos <= plist->length);
if (pos < 0 || pos > plist->length) return false;
if (IsFull(plist)) { //判满,为真,需要扩容
Inc(plist);
}
for (int i = plist->length - 1; i >= pos; i--) { // 数据后移,空出需要插入的下标
plist->elem[i + 1] = plist->elem[i];
}
plist->elem[pos] = val;
plist->length++;
return true;
}
ps:插入后,要记得修改长度length呀~ 后面删除操作也不要忘记哦~
删除——根据下标删除 根据值删除
删除操作先是要对顺序表进行判空操作,根据值删除其实也就是通过查找要删w下标,再调用根据下标删除的函数就好了
//删除——根据下标
bool Del_pos(PSqlist plist, int pos) {
assert(plist != NULL);
if (plist == NULL) return false;
assert(pos >= 0 && pos <= plist->length);
if (pos < 0 || pos > plist->length) return false;
if (IsEmpty(plist)) {
return false;
}
for (int i = pos + 1; i < plist->length; i++) {
plist->elem[i - 1] = plist->elem[i];
}
plist->length--;
return true;
}
//删除——根据值
bool Del_val(PSqlist plist, int val) {
assert(plist != NULL);
if (plist == NULL) return false;
if (IsEmpty(plist)) {
return false;
}
int pos = Search_val(plist, val);
if (pos == -1) return false;
return Del_pos(plist, pos); //调用按下标删除的函数,直接返回就行
}
查找——根据值
int Search_val(PSqlist plist, int val) {
assert(plist != NULL);
if (plist == NULL) return -1;
for (int i = 0; i < plist->length; i++) { //遍历查找
if (plist->elem[i] == val) {
return i;
}
}
return -1;
}
清空 —— 也就是让有效数据长度(length)为0
//清空
void Clear(PSqlist plist) {
assert(plist != NULL);
if (plist == NULL) return;
plist->length = 0;
}
销毁
这里不仅让有效数据长度为0,也要对申请出来的内存空间进行释放,释放之后,一定要让其指针重新指向空,是为了防止野指针的出现(也就是为了避免二次释放导致的异常或者崩溃)
//销毁
void Destroy(PSqlist plist) {
plist->length = 0;
free(plist->elem);
plist->elem = NULL;
plist->list_size = 0;
}
结果打印
//打印
void Show(PSqlist plist) {
for (int i = 0; i < plist->length; i++) {
printf("%d ", plist->elem[i]);
}
printf("\n");
}
测试用例:
int main() {
struct Sqlist sq;
Init_sqlist(&sq);
for (int i = 0; i < 100; i++) {
Insert(&sq, i, i + 1);
}
Show(&sq);
printf("length = %d\n", sq.length);
printf("list_size = %d\n", sq.list_size);
Insert(&sq, 1, 1000);
Show(&sq);
printf("length = %d\n", sq.length);
printf("list_size = %d\n", sq.list_size);
Del_pos(&sq, 74);//74
Del_val(&sq, 44);//44
Show(&sq);
printf("------------------------------------>\n");
Clear(&sq);
Show(&sq);
printf("length = %d\n", sq.length);
printf("list_size = %d\n", sq.list_size);
printf("------------------------------------>\n");
Destroy(&sq);
Show(&sq);
printf("length = %d\n", sq.length);
printf("list_size = %d\n", sq.list_size);
return 0;
}
结果打印: