数据结构
顺序表
基本概念
- 顺序表:顺序存储的线性表。
- 链式表:链式存储的线性表,简称链表。
顺序存储就是将数据存储到一片连续的内存中,在C语言环境下,可以是具名的栈数组,或者是匿名的堆数组。
存储方式不仅仅只是提供数据的存储空间,而是必须要能体现数据之间的逻辑关系。当采用顺序存储的方式来存放数据时,唯一能用来表达数据间本身的逻辑关系的就是存储位置。比如队列中的两个人,小明和小花,如果小明在逻辑上排在相邻的小花的前面,那么在存储位置上也必须把小明存放在相邻的小花的前面。
基本操作
顺序表设计
一般而言,为了方便操作顺序表,需要一个专门管理顺序表的”管理结构体“,管理结构体中一般会
包含:
- 顺序表总容量
- 顺序表当前最末元素下标位置
- 顺序表指针
下面是管理结构体示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int capacity; // 顺序表容量
int last; // 最末元素下标
int * data; // 顺序表,以整型数据为例
}sequenceList;
- 初始化
所谓初始化就是建立一个不包含任何元素的顺序表,设置好管理结构体中的表的总容量、末元素下标,申请好顺序表内存空间等系列准备工作。
下面是初始化顺序表的示例代码:
seqList *initList(int cap)
{
// 申请内存
seqList *list = malloc(sizeof(seqList));
// 非空校验
if (list != NULL)
{
// 给数据区(整型)分配空间
list->data = malloc(sizeof(int) * cap);
if (list->data == NULL)
{
free(list);
return NULL;
}
list->capacity = cap;
list->last = -1;
}
}
测试
int main()
{
sequenceList *list = init_list(10);
if(list == NULL)
{
perror("初始化顺序表失败!");
exit(0);
}
else
{
printf("初始化顺序表成功!\n");
}
}
-
增删节点
在顺序表中增加一个数据,可以有多种方式,比如在原数组的末尾增加,或者在原数组的头部增加,或者在数组中间任意一个位置增加。根据实际需要来定。
下面以在顺序表头部增删数据为例,示例代码如下:
bool insert(seqList *list, int data)
{
// 判断顺序表是否满了
if (isFull(list))
{
return false;
}
// 将原有数据全部往后移动一位,腾出位置给新数据
for (int i = list->last; i >= 0; i--)
{
list->data[i + 1] = list->data[i];
}
// 将新数据置入表头
list->data[0] = data;
list->last++;
return true;
}
bool removeNode(seqList *list, int data)
{
// 判断是否为空
if (list->last == -1)
{
return false;
}
// 找到要删除的节点的位置
int i, pos = -1;
for (i = 0; i <= list->last; i++)
{
if (list->data[i] == data)
{
pos = i;
break;
}
}
// 找不到要删除的节点
if (i > list->last)
{
return false;
}
// 将截止到删除目标节点之后的所有元素向前移动一位
for (int i = pos; i < list->last; i++)
{
list->data[i] = list->data[i + 1];
}
list->last--;
return true;
}
-
销毁顺序表
一个顺序表最后不再需要,应当要释放其所占用的内存空间,这被称为顺序表的销毁。
下面是销毁操作的示例代码:
void destroy(sequenceList *s)
{
if(s == NULL)
return;
free(s->data);
free(s);
}
完整代码
-
seqlist.h
#ifndef __SEQLIST_H #define __SEQLIST_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> // 定义顺序表的结构体 typedef struct { int capacity;// 顺序表的容量(顺序表本质上是数组) int last; // 末元素的下标 int *data; // 数据,其实是一个存放多个int的容器 } seqList; // 创建顺序表(顺序表的初始化) seqList *initList(int cap); // 向顺序表的表头插入一个新数据 bool insert(seqList *list,int data); // 判断顺序表是否满了 bool isFull(seqList *list); // 遍历顺序表中的元素 void show(seqList *list); // 将顺序表中指定的某个元素删除 bool removeNode(seqList *list,int data); // 销毁顺序表 void destroy(seqList *s); #endif
-
seqlist.c
#include "seqlist.h"
// 初始化顺序表
seqList *initList(int cap)
{
// 申请内存
seqList *list = malloc(sizeof(seqList));
// 非空校验
if (list != NULL)
{
// 给数据区(整型)分配空间
list->data = malloc(sizeof(int) * cap);
if (list->data == NULL)
{
free(list);
return NULL;
}
list->capacity = cap;
list->last = -1;
}
}
// 判断顺序表是否满了
bool isFull(seqList *list)
{
return list->last == list->capacity - 1; // maxIndex = capacity -1
}
// 向顺序表的表头插入一个新数据
bool insert(seqList *list, int data)
{
// 判断顺序表是否满了
if (isFull(list))
{
return false;
}
// 将原有数据全部往后移动一位,腾出位置给新数据
for (int i = list->last; i >= 0; i--)
{
list->data[i + 1] = list->data[i];
}
// 将新数据置入表头
list->data[0] = data;
list->last++;
return true;
}
// 遍历顺序表中的元素
void show(seqList *list)
{
// 判断是否为空
if (list->last == -1)
{
return;
}
// 遍历
for (int i = 0; i <= list->last; i++)
{
printf("%d ", list->data[i]);
}
printf("\n");
}
// 将顺序表中指定的某个元素删除
bool removeNode(seqList *list, int data)
{
// 判断是否为空
if (list->last == -1)
{
return false;
}
// 找到要删除的节点的位置
int i, pos = -1;
for (i = 0; i <= list->last; i++)
{
if (list->data[i] == data)
{
pos = i;
break;
}
}
// 找不到要删除的节点
if (i > list->last)
{
return false;
}
// 将截止到删除目标节点之后的所有元素向前移动一位
for (int i = pos; i < list->last; i++)
{
list->data[i] = list->data[i + 1];
}
list->last--;
return true;
}
// 销毁顺序表
void destroy(seqList *s)
{
if(s == NULL)
return;
free(s->data);// 销毁数据区
free(s);// 销毁外部结构体对象
}
顺序表优缺点总结
顺序存储中,由于逻辑关系是用物理位置来表达的,因此从上述示例代码可以很清楚看到,增删数据都非常困难,需要成片地移动数据。顺序表对数据节点的增删操作是很不友好的。
总结其特点如下:
-
优点
-
不需要多余的信息来记录数据间的关系,存储密度高
-
所有数据顺序存储在一片连续的内存中,支持立即访问任意一个随机数据,比如上述顺序表中第个节点是 s->data[i]
-
-
缺点
-
插入、删除时需要保持数据的物理位置反映其逻辑关系,一般需要成片移动数据
-
当数据节点数量较多时,需要一整片较大的连续内存空间
-
当数据节点数量变化剧烈时,内存的释放和分配不灵活
-