动态顺序表的代码实现
1.动态顺序表的功能API框架
对于顺序表代码实现的功能接口(API)框架:
//以下为动态顺序表的API框架 //基本增删查改接口API // 顺序表初始化 void SeqListInit(SeqList* psl, size_t capacity); // 顺序表销毁 void SeqListDestory(SeqList* psl); // 顺序表打印 void SeqListPrint(SeqList* psl); // 检查空间,如果满了,进行增容 void CheckCapacity(SeqList* psl); // 顺序表头插 void SeqListPushFront(SeqList* psl, SLDataType x); // 顺序表头删 void SeqListPopFront(SeqList* psl); // 顺序表尾插 void SeqListPushBack(SeqList* psl, SLDataType x); // 顺序表尾删 void SeqListPopBack(SeqList* psl); // 顺序表查找 int SeqListFind(SeqList* psl, SLDataType x); // 顺序表指定位置插入数据 void SeqListInsert(SeqList* psl, size_t pos, SLDataType x); // 顺序表指定位置数据内容 void SeqListErase(SeqList* psl, size_t pos);
2.动态顺序表的功能API代码实现
0.顺序表的结构定义:
typedef int SLDataType; // 顺序表的动态存储 typedef struct SeqList { SLDataType* a; // 指向动态开辟的数组---静态存储就把它改成SLDataType a[N];就行 size_t size ; // 有效数据个数 size_t capicity ; // 容量空间的大小 }SeqList;
1.顺序表的初始化:
void SeqListInit(SeqList* psl) { //记得一定要加上断言,防止传进来的指针为空 assert(psl != NULL); //断言,psl不能为NULL psl->a = NULL; //初始顺序表为空 psl->size = 0; //初始数据个数为0 psl->capacity = 0;//初始空间容量为0 }
2.顺序表的销毁:
void SeqListDestory(SeqList* psl) { assert(psl != NULL); //断言,psl不能为空 free(psl->a); //释放动态开辟的空间 psl->a = NULL; //置空 psl->size = 0; //数据个数置0 psl->capacity = 0; //空间容量大小置0 }
3.检查是否扩容:
void CheckCapacity(SeqList* psl) { assert(psl != NULL); //断言 if (psl->size == psl->capacity) //检查容量,满了则增容 { size_t newcapacity; //新容量 if (psl->capacity == 0) newcapacity = psl->capacity = 4; //原来容量为0,扩容为4 else newcapacity = 2 * psl->capacity; //原来容量不为0,扩容为原来的2倍 SLDataType* p = (SLDataType*)realloc(psl->a, newcapacity*sizeof(SLDataType)); //扩容 if (p == NULL) { perror("realloc"); exit(-1); } psl->a = p; // p 不为空,开辟成功 psl->capacity = newcapacity; //更新容量 } }
4.顺序表的头插:
//因为顺序表是连续存储的,所以头插时要依次挪动数据 void SeqListPushFront(SeqList* psl, SLDataType x) { assert(psl); //断言 CheckCapacity(psl); //检查顺序表容量是否已满 for (int i = psl->size - 1; i >= 0; i--) //顺序表中[0,size-1]的元素依次向后挪动一位 { psl->a[i + 1] = psl->a[i]; } psl->a[0] = x; //头插数据 psl->size++; //有效数据个数+1 }
5.顺序表的头删:
void SeqListPopFront(SeqList* psl) { assert(psl); //断言 assert(psl->size > 0); //顺序表不能为空 int i = 0; for (i = 1; i < psl->size; i++) //顺序表中[1,size-1]的元素依次向前挪动一位 { psl->a[i - 1] = psl->a[i]; } psl->size--; //有效数据个数-1 }
6.顺序表的尾插:
void SeqListPushBack(SeqList* psl, SLDataType x) { assert(psl != NULL); //断言 CheckCapacity(psl); //检查顺序表容量是否已满 psl->a[psl->size] = x; //尾插数据 psl->size++; //有效数据个数+1 }
7.顺序表的尾删:
//顺序表最后一个数据只需将有效数据个数size-1即可达到尾删的目的 void SeqListPopBack(SeqList* psl) { assert(psl != NULL); //断言 assert(psl->size > 0); //顺序表不能为空 //不知道SLDataType是什么类型的数据,不能冒然的赋值为0 //比如:psl->a[psl->size - 1] = 0;---错误方式 psl->size--; //有效数据个数-1 }
8.顺序表的打印:
void SeqListPrint(const SeqList* psl) { assert(psl != NULL); //断言 if (psl->size == 0) //判断顺序表是否为空 { printf("顺序表为空\n"); return; } int i = 0; for (i = 0; i < psl->size; i++) //打印顺序表 { printf("%d ", psl->a[i]); } printf("\n"); }
9.顺序表的查找:
int SeqListFind(const SeqList* psl, SLDataType x) { assert(psl); //断言 int i = 0; for (i = 0; i < psl->size; i++) { if (psl->a[i] == x) { return i; //查找到,返回该值在数组中的下标 } } return -1; //没有查找到 }
10.顺序表指定下标位置插入数据:
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x) { assert(psl); //断言 assert(pos >= 0 && pos <= psl->size); //检查pos下标的合法性 CheckCapacity(psl); //检查顺序表容量是否已满 for (size_t i = psl->size; i > pos; i--) //将pos位置后面的数据依次向后挪动一位 { psl->a[i] = psl->a[i - 1]; } psl->a[pos] = x; //插入数据 psl->size++; //有效数据个数+1 } //为什么使用size_t pos? //比如: for (int i = psl->size - 1; i >= pos; i--) psl->a[i + 1] = psl->a[i]; /* 这种写法,当顺序表为空 size = 0 时,会导致 i = -1,执行 i >= pos 时,i 被算术转换成无符号数,而无符号数的 -1 是一个值很大的正数, 远大于 pos,满足条件进入循环,会造成越界访问 */
11.顺序表指定下标位置删除数据:
void SeqListErase(SeqList* psl, size_t pos) { assert(psl); //断言 assert(psl->size > 0); //顺序表不能为空 assert(pos >= 0 && pos < psl->size); //检查pos下标的合法性 size_t i = 0; for (i = pos + 1; i < psl->size; i++) //将pos位置后面的数据依次向前挪动一位 { psl->a[i - 1] = psl->a[i]; } psl->size--; //有效数据个数-1 }
补充两个扩展功能:
1.查看顺序表中有效数据个数:
//访问或修改数据结构中的数据,不要直接去访问,要去调用它的函数来访问和修改,这样更加规范安全,也更方便检查是否出现了越界等一些错误情况 //越界不一定报错,系统对越界的检查是一种抽查 size_t SeqListSize(const SeqList* psl) { assert(psl); //断言 return psl->size; }
2.修改指定下标位置的数据:
void SeqListAt(SeqList* psl, size_t pos, SLDataType x) { assert(psl); //断言 assert(psl->size > 0); //顺序表不能为空 assert(pos >= 0 && pos < psl->size); //检查pos下标的合法性 psl->a[pos] = x; //修改pos下标处对应的数据 }
3.动态顺序表的功能API效果动态展示
4.动态顺序表的功能API源代码
SeqList.h 头文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 要求:存储的数据从0开始,依次连续存储
// 静态的顺序表
// 问题:开小了,不够用。开大了,存在浪费。
//#define N 10000
//struct SeqList
//{
// int a[N];
// int size; // 记录存储了多少个数据
//};
typedef int SLDataType;
// 动态的顺序表
typedef struct SeqList
{
SLDataType* a;
int size; // 存储数据个数
int capacity; // 存储空间大小
}SL, SeqList;
void SeqListPrint(SeqList* psl);
//void SLInit(SeqList* psl);
void SeqListInit(SeqList* psl);
void SeqListDestroy(SeqList* psl);
void SeqListCheckCapacity(SeqList* psl);
// 时间复杂度是O(1)
void SeqListPushBack(SeqList* psl, SLDataType x);
void SeqListPopBack(SeqList* psl);
// 时间复杂度是O(N)
void SeqListPushFront(SeqList* psl, SLDataType x);
void SeqListPopFront(SeqList* psl);
// 在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 删除pos位置的数据
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
SeqList.c 源文件
#include "SeqList.h"
void SeqListPrint(SeqList* psl)
{
assert(psl);
for (int i = 0; i < psl->size; ++i)
{
printf("%d ", psl->a[i]);
}
printf("\n");
}
void SeqListInit(SeqList* psl)
{
assert(psl);
psl->a = NULL;
psl->size = 0;
psl->capacity = 0;
}
void SeqListDestroy(SeqList* psl)
{
assert(psl);
free(psl->a);
psl->a = NULL;
psl->capacity = psl->size = 0;
}
void SeqListCheckCapacity(SeqList* psl)
{
assert(psl);
// 如果满了,我们要扩容
if (psl->size == psl->capacity)
{
size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
SLDataType* tmp = realloc(psl->a, sizeof(SLDataType) * newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
psl->a = tmp;
psl->capacity = newCapacity;
}
}
}
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
/*SeqListCheckCapacity(psl);
psl->a[psl->size] = x;
psl->size++;*/
SeqListInsert(psl, psl->size, x);
}
void SeqListPopBack(SeqList* psl)
{
assert(psl);
//psl->a[psl->size - 1] = 0;
/*if (psl->size > 0)
{
psl->size--;
}*/
SeqListErase(psl, psl->size - 1);
}
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
//SeqListCheckCapacity(psl);
挪动数据,腾出头部空间
//int end = psl->size - 1;
//while (end >= 0)
//{
// psl->a[end + 1] = psl->a[end];
// --end;
//}
//psl->a[0] = x;
//psl->size++;
SeqListInsert(psl, 0, x);
}
void SeqListPopFront(SeqList* psl)
{
assert(psl);
// 挪动数据覆盖删除
/*if (psl->size > 0)
{
int begin = 1;
while (begin < psl->size)
{
psl->a[begin - 1] = psl->a[begin];
++begin;
}
--psl->size;
}*/
SeqListErase(psl, 0);
}
// 在pos位置插入x
//void SeqListInsert(SeqList* psl, int pos, SLDataType x)
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
// 暴力检查
assert(psl);
// 温和检查
if (pos > psl->size)
{
printf("pos 越界:%d\n", pos);
return;
//exit(-1);
}
// 暴力检查
//assert(pos <= psl->size);
SeqListCheckCapacity(psl);
//int end = psl->size - 1;
//while (end >= (int)pos)
//{
// psl->a[end + 1] = psl->a[end];
// --end;
//}
size_t end = psl->size;
while (end > pos)
{
psl->a[end] = psl->a[end - 1];
--end;
}
psl->a[pos] = x;
psl->size++;
}
// 删除pos位置的数据
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
assert(pos < psl->size);
size_t begin = pos + 1;
while (begin < psl->size)
{
psl->a[begin - 1] = psl->a[begin];
++begin;
}
psl->size--;
}
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; ++i)
{
if (psl->a[i] == x)
{
return i;
}
}
return -1;
}
main.c 源文件主函数
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
#include<stdlib.h>
void menu()
{
printf("**************************************************\n");
printf("------------动态顺序表功能选择--------------------\n");
printf("**************************************************\n");
printf("1.头插数据4321 2.头删数据432\n");
printf("3.尾插数据234 4.尾删数据234\n");
printf("5.指定位置1后插入数据2 6.删除指定位置1数据内容2\n");
printf("0.退出\n");
printf("**************************************************\n");
}
int main()
{
SeqList s;
SeqListInit(&s);
int option = -1;
do
{
menu();
printf("请选择你的操作:>");
scanf("%d", &option);
switch (option)
{
case 1:SeqListPushFront(&s, 4); SeqListPushFront(&s, 3); SeqListPushFront(&s, 2); SeqListPushFront(&s, 1); SeqListPrint(&s); break;//头插4321,数据内容:1234
case 2:SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPrint(&s); break;//头删432,数据内容:1
case 3:SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); break;//尾插234,数据内容:1234
case 4:SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPrint(&s); break;//尾删432,数据内容:1
case 5:SeqListInsert(&s, 1, 2); SeqListPrint(&s); break;//下标位置1插入2,数据内容:12
case 6:SeqListErase(&s, 1); SeqListPrint(&s); break;//位置1删除1,数据内容:2
default:printf("选择功能错误,请输入数字0~6!\n"); break;
}
} while (option != 0);
printf("程序已退出,感谢使用动态顺序表功能!\n");
system("pause");//让系统停下里,等待输入任何键退出---头文件stdlib.h
return 0;
}