⭐️ 顺序表介绍
顺序表是线性表的一种。
🌠什么是线性表呢?
线性表是数据结构的一种,一个线性表是 n n n个具有相同特性的数据元素的有限序列。常见的线性表:顺序表、链表、栈、队列、字符串…
🌠什么是顺序表呢?
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。顺序表与数组的不同点是:数组可以任意的放入元素,不管元素与元素之间是否连续。而顺序表不仅物理地址连续,元素与元素之间也要连续存放。
图1:
而顺序表又分为:静态顺序表和动态顺序表。
- 静态顺序表:使用长度固定的数组存储元素。
- 缺陷: (不推荐)长度不自由,当长度给的太大而数据较少时,浪费空间。长度太小,而数据较多,数据又放不下。
- 类型定义:
// 顺序表默认大小
#define SequenceListSize 100
// 顺序表数据的类型
typedef int SequenceListType;
// 顺序表结构
typedef struct SequenceList {
SequenceListType data[SequenceListSize]; // 顺序表数据的类型的数组
int size; // 数据表当前的大小
}SequenceList;
- 动态顺序表:(推荐)第一次初始化后,可根据数据多少动态的分配空间大小。
- 类型定义:
// 顺序表数据的类型
typedef int SequenceListType;
// 顺序表结构
typedef struct SequenceList {
SequenceListType* data; // 顺序表数据的类型的指针
int size; // 数据表当前的大小
int capacity; // 数据表的容量
}SequenceList;
⭐️ 手撕顺序表(动态版)
🌠 顺序表的类型声明:
// 使用到的头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 顺序表数据的类型
typedef int SequenceListType;
// 顺序表结构
typedef struct SequenceList {
SequenceListType* data; // 顺序表数据的类型的指针
int size; // 数据表当前的大小
int capacity; // 数据表的容量
}SequenceList;
typedef int SequenceListType;
顺序表的类型不能写死,因为管理的数据可以是任意类型 char
、int
、double
、...
,所以这里使用 typedef
声明出一个顺序表类型,方便后期修改。
🌠 顺序表的接口实现
⭕️ 初始化顺序表:
声明:
void SequenceListInit(SequenceList * ps);
代码实现:
void SequenceListInit(SequenceList* ps) {
assert(ps != NULL);
ps->data = NULL;
ps->size = ps->capacity = 0;
}
⭕️ 顺序表检查是否增容:
声明:
void SequenceListCheckCapacity(SequenceList* ps);
代码实现:
void SequenceListCheckCapacity(SequenceList* ps) {
assert(ps != NULL);
if (ps->size == ps->capacity) {
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SequenceListType* newData = (SequenceListType*)realloc(ps->data , newCapacity * sizeof(SequenceListType));
if (newData == NULL) {
perror("SequenceListCheckCapacity_Function_realloc:");
exit(-1);
}
ps->data = newData;
ps->capacity = newCapacity;
}
}
✨往期文章:C语言动态开辟内存函数的使用。
⭕️ 顺序表尾插:
声明:
void SequenceListPushBack(SequenceList* ps , SequenceListType node);
代码实现:
void SequenceListPushBack(SequenceList* ps, SequenceListType node) {
assert(ps != NULL);
// 检查容量
SequenceListCheckCapacity(ps);
ps->data[ps->size] = node;
ps->size++;
}
⭕️ 顺序表尾删:
声明:
void SequenceListPopBack(SequenceList* ps);
代码实现:
void SequenceListPopBack(SequenceList* ps) {
assert(ps != NULL);
// 检查顺序表不为空
assert(ps->size > 0);
ps->size--;
}
⭕️ 顺序表头插:
声明:
void SequenceListPushFront(SequenceList* ps , SequenceListType node);
代码实现:
void SequenceListPushFront(SequenceList* ps, SequenceListType node) {
assert(ps != NULL);
// 检查容量
SequenceListCheckCapacity(ps);
// 从尾部向后挪动数据 也可以使用memmove()
for (int end = ps->size - 1; end >= 0; end--) {
ps->data[end + 1] = ps->data[end];
}
ps->data[0] = node;
ps->size++;
}
⭕️ 顺序表头删:
声明:
void SequenceListPopFront(SequenceList* ps);
代码实现:
void SequenceListPopFront(SequenceList* ps) {
assert(ps != NULL);
// 检查顺序表不为空
assert(ps->size > 0);
for (int start = 1; start < ps->size; start++) {
ps->data[start - 1] = ps->data[start];
}
ps->size--;
}
⭕️ 顺序表在某一位置插入:
声明:
void SequenceListInsert(SequenceList* ps , int pos , SequenceListType node);
代码实现:
void SequenceListInsert(SequenceList* ps, int pos, SequenceListType node) {
assert(ps != NULL);
// 检查插入顺序表元素边界
assert(pos >= 0 && pos <= ps->size);
// 检查增容
SequenceListCheckCapacity(ps);
for (int end = ps->size - 1; end >= pos; end--) {
ps->data[end + 1] = ps->data[end];
}
ps->data[pos] = node;
ps->size++;
}
注: 当有了 Insert
函数,顺序表的 PushBack
、PushFront
就可以复用 Insert
接口。
void SequenceListPushBack(SequenceList* ps, SequenceListType node) {
// 复用
SequenceListInsert(ps , ps->size , node);
}
void SequenceListPushFront(SequenceList* ps, SequenceListType node) {
// 复用
SequenceListInsert(ps , 0 , node);
}
⭕️ 顺序表在某一位置删除:
声明:
void SequenceListErase(SequenceList* ps , int pos);
代码实现:
void SequenceListErase(SequenceList* ps, int pos) {
assert(ps);
// 检查删除顺序表元素边界
assert(pos >= 0 && pos < ps->size);
for (int start = pos; start < ps->size - 1; start++) {
ps->data[start] = ps->data[start + 1];
}
ps->size--;
}
注: 当有了 Erase
函数,顺序表的 PopBack
、PopFront
就可以复用 Erase
接口。
void SequenceListPopBack(SequenceList* ps) {
// 复用
SequenceListErase(ps , ps->size - 1);
}
void SequenceListPopFront(SequenceList* ps) {
// 复用
SequenceListErase(ps , 0);
}
结论: 如果想要在短时间内实现一个顺序表,只需要实现 Insert
、Erase
函数,PushBack
、PushFront
、PopBack
、PopFront
只需要复用这两个接口即可。
⭕️ 顺序表查找元素:
声明:
int SequenceListFind(SequenceList* ps , SequenceListType node);
代码实现:
int SequenceListFind(SequenceList* ps, SequenceListType node) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
if (ps->data[i] == node) {
return i;
}
}
return -1;
}
⭕️ 顺序表修改元素:
声明:
void SequenceListModify(SequenceList* ps , int pos , SequenceListType node);
代码实现:
void SequenceListModify(SequenceList* ps, int pos, SequenceListType node) {
assert(ps != NULL);
// 检查pos范围
assert(pos >= 0 && pos < ps->size);
ps->data[pos] = node;
}
⭕️ 顺序表打印元素:
声明:
void SequenceListPrint(SequenceList* ps);
代码实现:
void SequenceListPrint(SequenceList* ps) {
for (int i = 0; i < ps->size; i++) {
printf("%d " , ps->data[i]);
}
printf("\n");
}
⭕️ 顺序表销毁:
声明:
void SequenceListDestroy(SequenceList* ps);
代码实现:
void SequenceListDestroy(SequenceList* ps) {
assert(ps);
if (ps->data != NULL) {
free(ps->data);
ps->data = NULL;
ps->capacity = ps->size = 0;
}
}
数据结构和算法的概念以及时间复杂度空间复杂度详解。
C语言动态开辟内存函数的使用。
leetcode189.轮转数组。