在开始之前,我们先来了解一些基本概念:
线性结构
- 线性结构是一种广泛使用的数据结构,其特点是数据元素之间存在一对一的对应关系。
- 常见的线性结构有数组、线性表、栈、队列、串…
顺序表
- 顺序表是一种线性表,可见其视为数组,就是用一段物理地址连续的存储单元依次存储数据元素的线性结构。一般可分为静态顺序表和动态顺序表。
-
静态顺序表:使用定长数组存储。这就导致数组长度N定大了,空间开多了造成浪费;N定小了,空间开小了又不够用。因此它只适用于确定知道需要存多少数据的场景。
-
动态顺序表:使用动态开辟的数组存储。这样我们就可以根据需要动态的分配空间大小,因此在现实中基本都是使用动态顺序表。
-
代码实现
接下来,我们就要实现动态顺序表的相关操作,主要包括:
- 初始化顺序表
- 顺序表尾插、尾删
- 顺序表头插、头删
- 顺序表任意位置插入、任意位置删除
- 顺序表元素查找
- 删除顺序表中第一个值为data的元素
- 删除顺序表中所有值为data的元素
- 修改顺序表中某一元素的数值
- 顺序表元素个数、容量查询
- 顺序表扩容、判空、清空、销毁及打印
SeqList.h头文件:
在这里完成动态顺序表的结构体定义以及上述相关操作的函数声明。
#pragma once
typedef int DataType;
typedef struct SeqList{
DataType* _array;
int _capacity; //顺序表的总大小
int _size; //顺序表中有效元素的个数
}SeqList, *PSeq;
//顺序表的初始化
void SeqListInit(PSeq ps, int capacity);
//在顺序表的尾部插入值为data的元素
void SeqListPushBack(PSeq ps, DataType data);
//删除顺序表最后一个元素
void SeqListPopBack(PSeq ps);
//在顺序表的头部插入值为data的元素
void SeqListPushFront(PSeq ps, DataType data);
//删除顺序表头部的元素
void SeqListPopFront(PSeq ps);
//在顺序表pos位置插入值为data的元素
void SeqListInsert(PSeq ps, int pos, DataType data);
//删除顺序表中pos位置上的元素
void SeqListErase(PSeq ps, int pos);
// 在顺序表中查找值为data的元素,找到返回该元素在顺序表中的下标,否则返回-1
int SeqListFind(PSeq ps, DataType data);
// 删除顺序表中第一个值为data的元素
void SeqListRemove(PSeq ps, DataType data);
//删除顺序表中所有值为data的元素
void SeqListRemoveAll(PSeq ps, DataType data);
//修改顺序表pos位置元素的数值
void SeqListModify(PSeq ps, int pos, DataType data);
// 返回顺序表中有效元素的个数
int SeqListSize(PSeq ps);
// 返回顺序表的容量大小
int SeqListCapacity(PSeq ps);
// 顺序表的扩容
void CheckCapacity(PSeq ps);
// 检测顺序表是否为空,如果为空返回非0值,非空返回0
int SeqListEmpty(PSeq ps);
// 将顺序表中的元素清空
void SeqListClear(PSeq ps);
// 销毁顺序表
void SeqListDestroy(PSeq ps);
//打印顺序表
void SeqListPrint(PSeq ps);
void TestSeqList();
SeqList.c文件:
此为动态顺序表各操作函数的具体代码。
#include"SeqList.h"
#include<malloc.h>
#include<assert.h>
#include<stdio.h>
//顺序表的初始化
void SeqListInit(PSeq ps, int capacity){
ps->_array = (DataType*)malloc(sizeof(DataType)*capacity);
//申请空间失败
if (ps->_array == NULL){
assert(0);
return;
}
ps->_capacity = capacity;
ps->_size = 0;
}
//在顺序表的尾部插入值为data的元素
void SeqListPushBack(PSeq ps, DataType data){
assert(ps);
//检测是否需要扩容
CheckCapacity(ps);
ps->_array[ps->_size] = data;
ps->_size++;
}
//删除顺序表最后一个元素
void SeqListPopBack(PSeq ps){
assert(ps);
//保证顺序表中有元素
if (SeqListEmpty(ps)){
return;
}
ps->_size--;
}
//在顺序表的头部插入值为data的元素
void SeqListPushFront(PSeq ps, DataType data){
assert(ps);
//检测是否需要扩容
CheckCapacity(ps);
//将顺序表中所有元素统一向后搬移一个位置(从后往前)
for (int i = ps->_size - 1; i >= 0; i--){
ps->_array[i + 1] = ps->_array[i];
}
ps->_array[0] = data;
ps->_size++;
}
//删除顺序表头部的元素
void SeqListPopFront(PSeq ps){
assert(ps);
//保证顺序表中有元素
if (SeqListEmpty(ps)){
return;
}
//将顺序表中的元素统一向前搬移一个位置(从前往后)
for (int i = 1; i < ps->_size; i++){
ps->_array[i - 1] = ps->_array[i];
}
ps->_size--;
}
//在顺序表pos位置插入值为data的元素
void SeqListInsert(PSeq ps, int pos, DataType data){
assert(ps);
//检测pos是否有效(此处ps->_size可以取到,类似于尾插)
if (pos<0 || pos>ps->_size){
return;
}
//检测是否需要扩容
CheckCapacity(ps);
for (int i = ps->_size - 1; i >= pos; i--){
ps->_array[i + 1] = ps->_array[i];
}
ps->_array[pos] = data;
ps->_size++;
}
//删除顺序表中pos位置上的元素
void SeqListErase(PSeq ps, int pos){
assert(ps);
//检测pos是否有效
if (pos<0 || pos>=ps->_size){
return;
}
//保证顺序表中有元素
if (SeqListEmpty(ps)){
return;
}
for (int i = pos + 1; i < ps->_size; i++){
ps->_array[i - 1] = ps->_array[i];
}
ps->_size--;
}
// 在顺序表中查找值为data的元素,找到返回该元素在顺序表中的下标,否则返回-1
int SeqListFind(PSeq ps, DataType data){
assert(ps);
for (int i = 0; i < ps->_size; i++){
if (ps->_array[i] == data){
return i;
}
}
return -1;
}
// 删除顺序表中第一个值为data的元素
void SeqListRemove(PSeq ps, DataType data){
assert(ps);
//利用已有函数来实现:SeqListFind函数找到data元素的位置,SeqListErase函数删除该位置的元素
SeqListErase(ps, SeqListFind(ps, data));
}
//删除顺序表中所有值为data的元素
void SeqListRemoveAll(PSeq ps, DataType data){
assert(ps);
int pos = -1;
//重复调用SeqListFind、SeqListErase函数,找到值为data的元素就进行删除
while ((pos = SeqListFind(ps, data)) != -1){
SeqListErase(ps, pos);
}
}
//修改顺序表pos位置元素的数值
void SeqListModify(PSeq ps, int pos, DataType data){
assert(ps);
//检测pos是否有效
if (pos<0 || pos>ps->_size){
return;
}
//保证顺序表中有元素
if (SeqListEmpty(ps)){
return;
}
ps->_array[pos] = data;
}
// 返回顺序表中有效元素的个数
int SeqListSize(PSeq ps){
assert(ps);
return ps->_size;
}
// 返回顺序表的容量大小
int SeqListCapacity(PSeq ps){
assert(ps);
return ps->_capacity;
}
// 顺序表的扩容
void CheckCapacity(PSeq ps){
assert(ps);
//顺序表中已没有空间
if (ps->_size == ps->_capacity){
int newCapacity = ps->_capacity * 2;
//申请新空间
int* pTemp = (DataType*)malloc(sizeof(DataType)*newCapacity);
//申请新空间失败
if (pTemp == NULL){
assert(0);
return;
}
//拷贝元素至新空间
for (int i = 0; i < ps->_size; i++){
pTemp[i] = ps->_array[i];
}
//释放旧空间
free(ps->_array);
//更新
ps->_size = pTemp;
ps->_capacity = newCapacity;
}
}
// 检测顺序表是否为空,如果为空返回非0值,非空返回0
int SeqListEmpty(PSeq ps){
assert(ps);
if (ps->_size == 0){
return 1;
}
return 0;
}
// 将顺序表中的元素清空
void SeqListClear(PSeq ps){
assert(ps);
ps->_size = 0;
}
// 销毁顺序表
void SeqListDestroy(PSeq ps){
if (ps->_array){
free(ps->_array);
ps->_array = NULL;
ps->_capacity = 0;
ps->_size = 0;
}
}
//打印顺序表
void SeqListPrint(PSeq ps){
assert(ps);
for (int i = 0; i < ps->_size; i++){
printf("%d ", ps->_array[i]);
}
printf("\n");
}
void TestSeqList(){
SeqList s;
SeqListInit(&s, 10);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPrint(&s);
SeqListPopBack(&s, 5);
SeqListPrint(&s);
SeqListPushFront(&s, 0);
SeqListPrint(&s);
SeqListPopFront(&s, 0);
SeqListPrint(&s);
SeqListInsert(&s, 2, 9);
SeqListPrint(&s);
int pos = SeqListFind(&s, 9);
printf("pos = %d\n", pos);
SeqListModify(&s, pos, 4);
SeqListPrint(&s);
//SeqListErase(&s, pos);
//SeqListPrint(&s);
//SeqListRemove(&s, 4);
//SeqListPrint(&s);
SeqListRemoveAll(&s, 4);
SeqListPrint(&s);
printf("顺序表中有效元素的个数为%d\n", SeqListSize(&s));
printf("顺序表的容量大小为%d\n", SeqListCapacity(&s));
SeqListDestroy(&s);
}
test.c文件:
即主函数文件,完成测试。
#include"SeqList.h"
#include<stdlib.h>
int main(){
TestSeqList();
system("pause");
return 0;
}
在TestSeqList()函数中测试了增删改查等基本操作,代码运行无误,结果如下: