顺序表
1.顺序表的特性
存储的数据类型相同;
存储单元的物理地址连续;
支持根据下标随机访问;
2.顺序表的类型
1)静态顺序表
存储的数据个数有上限,一般是用一个大小固定的数组实现,实用性不高;
2)动态顺序表
数据的存储空间可以随着元素个数的增加而调整,当数据满了就会增容;
3.顺序表模拟实现
1)静态顺序表
数据结构如下:
//顺序表大小
#define MAXSZ 8
typedef int DataType; //数据元素类型
typedef struct StaticList
{
DataType data[MAXSZ]; //数据域
int size; //有效元素个数
}StaticList;
重要的接口:
初始化顺序表
设置当前有效元素个数为0
void Init(StaticList* lst)
{
lst->size = 0;
}
插入
将待插入位置和其后面的元素都往后移动一位,再将数据放入插入位置,有效元素个数加1
bool Insert(StaticList* lst, int pos, DataType val)
{
//检查位置的有效性
if (pos < 0 || pos >= MAXSZ)
{
return false;
}
//检查空间是否足够
if (lst->size + 1 > MAXSZ)
return false;
//移动元素
for (int i = MAXSZ - 1; i > pos; --i)
{
lst->data[i] = lst->data[i - 1];
}
//插入数据,更新有效元素个数
lst->data[pos] = val;
lst->size++;
return true;
}
删除
将待删除位置后面的所有元素都往前移动一位,有效元素个数减1
bool Erase(StaticList* lst, int pos)
{
//检查位置的有效性
if (pos < 0 || pos >= MAXSZ)
return false;
//尾删不需要移动元素
//不是尾删需要移动元素
if (pos != MAXSZ - 1)
{
//移动元素
for (int i = pos; i < MAXSZ - 1; i++)
{
lst->data[i] = lst->data[i + 1];
}
}
//更新有效元素个数
lst->size--;
return true;
}
源码
StaticList.h:
#pragma once
//静态顺序表
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h> //INT_MIN
#include <stddef.h> //int
//顺序表大小
#define MAXSZ 8
typedef int DataType;
typedef struct StaticList
{
DataType data[MAXSZ]; //数据域
int size; //有效元素个数
}StaticList;
//初始化
void Init(StaticList* lst);
//插入
bool Insert(StaticList* lst, int pos, DataType val);
//删除
bool Erase(StaticList* lst, int pos);
//修改
bool SetValue(StaticList* lst, int pos, DataType val);
//获取元素
DataType getValue(StaticList* lst, int pos);
//查找
int Search(StaticList* lst, DataType val);
//头插
bool Push_front(StaticList* lst, DataType val);
//尾插
bool Push_back(StaticList* lst, DataType val);
//头删
bool Pop_front(StaticList* lst);
//尾删
bool Pop_back(StaticList* lst);
//有效元素个数
int getSize(StaticList* lst);
//顺序表是否为空
bool isEmpty(StaticList* lst);
//顺序表是否已满
bool isFull(StaticList* lst);
//打印顺序表内容
void PrintList(StaticList* lst);
StaticList.c:
#include "StaticList.h"
//初始化
void Init(StaticList* lst)
{
lst->size = 0;
}
//插入
bool Insert(StaticList* lst, int pos, DataType val)
{
//检查位置的有效性
if (pos < 0 || pos >= MAXSZ)
{
return false;
}
//检查空间是否足够
if (lst->size + 1 > MAXSZ)
return false;
//移动元素
for (int i = MAXSZ - 1; i > pos; --i)
{
lst->data[i] = lst->data[i - 1];
}
//插入数据,更新有效元素个数
lst->data[pos] = val;
lst->size++;
return true;
}
//删除
bool Erase(StaticList* lst, int pos)
{
//检查位置的有效性
if (pos < 0 || pos >= MAXSZ)
return false;
//尾删不需要移动元素
//不是尾删需要移动元素
if (pos != MAXSZ - 1)
{
//移动元素
for (int i = pos; i < MAXSZ - 1; i++)
{
lst->data[i] = lst->data[i + 1];
}
}
//更新有效元素个数
lst->size--;
return true;
}
//修改
bool SetValue(StaticList* lst, int pos, DataType val)
{
//检查位置的有效性
if (pos < 0 || pos >= MAXSZ)
return false;
lst->data[pos] = val;
return true;
}
//获取元素
DataType getValue(StaticList* lst, int pos)
{
if (pos < 0 || pos >= MAXSZ)
return INT_MIN;
return lst->data[pos];
}
//查找
int Search(StaticList* lst, DataType val)
{
int ret = -1;
for (int i = 0; i < MAXSZ; ++i)
{
if (lst->data[i] == val)
{
ret = i;
break;
}
}
return ret;
}
//头插
bool Push_front(StaticList* lst, DataType val)
{
return Insert(lst, 0, val);
}
//尾插
bool Push_back(StaticList* lst, DataType val)
{
return Insert(lst, lst->size, val);
}
//头删
bool Pop_front(StaticList* lst)
{
return Erase(lst, 0);
}
//尾删
bool Pop_back(StaticList* lst)
{
return Erase(lst, lst->size - 1);
}
//有效元素个数
int getSize(StaticList* lst)
{
return lst->size;
}
//顺序表是否为空
bool isEmpty(StaticList* lst)
{
return lst->size == 0;
}
//顺序表是否已满
bool isFull(StaticList* lst)
{
return lst->size == MAXSZ - 1;
}
//打印顺序表内容
void PrintList(StaticList* lst)
{
for (int i = 0; i < lst->size; ++i)
{
printf("%d ", lst->data[i]);
}
printf("\n");
}
2)动态顺序表
数据结构的定义
typedef int DataType_D; //数据元素类型
//动态顺序表
typedef struct DynamicList
{
DataType_D* data; //数据
int size; //有效元素个数
int capacity; //最大容纳元素个数
}DynamicList;
重要接口
初始化顺序表
开辟固定大小的空间,为元素个数和容量赋初值;
起始开辟一定大小的空间,可以通过宏来设置
//初始化
bool InitList(DynamicList* lst)
{
if (lst == NULL)
return false;
//startCapacity:起始容量
lst->data = (DataType_D*)malloc(sizeof(DataType_D)* startCapacity);
lst->size = 0;
lst->capacity = startCapacity;
return true;
}
销毁顺序表
释放堆空间,防止内存泄漏
bool DestroyList(DynamicList* lst)
{
if (lst == NULL || lst->data == NULL)
return true;
free(lst->data);
lst->data = NULL;
return true;
}
检查容量
当顺序表满了,就重新开辟空间,并将原空间的数据拷贝过去,更新容量
//检查容量
void checkCapacity(DynamicList* lst)
{
if (lst == NULL || lst->data == NULL || lst->size != lst->capacity)
return;
int newC = lst->capacity * 2;
//重新开辟空间并拷贝原空间的数据
lst->data = (DataType_D*)realloc(lst->data, newC * sizeof(DataType_D));
/*
//另一种重开空间并拷贝
DataType_D* newData = (DataType_D*)malloc(sizeof(DataType_D) * newC);
memcpy(newData, lst->data, sizeof(DataType_D) * lst->capacity());
free(lst->data);
lst->data = newData;
*/
//更新
lst->capacity = newC;
}
插入
第一步:检查容量;
第二步:将待插入位置和它后面的元素都往后移动一位;
第三步:插入数据;
第四步:更新元素个数
//插入
bool InsertList(DynamicList* lst, int pos, DataType_D val)
{
if (lst == NULL || lst->data == NULL || pos < 0 || pos > lst->capacity)
return false;
//检查容量
checkCapacity(lst);
if (pos > lst->size)
pos = lst->size;
//移动数据
int idx = lst->size - 1;
while (idx >= pos)
{
lst->data[idx + 1] = lst->data[idx];
idx--;
}
//插入数据
lst->data[pos] = val;
//更新
++lst->size;
return true;
}
删除
将待删除位置后面的元素都往前移动一位,并更新元素个数
bool EraseList(DynamicList* lst, int pos)
{
if (lst == NULL || lst->data == NULL || pos < 0 || pos >= lst->capacity)
return false;
//移动元素
int idx = pos;
while (idx < lst->size - 1)
{
lst->data[idx] = lst->data[idx + 1];
idx++;
}
//更新
--lst->size;
return true;
}
源码
DynamicList.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h> //INT_MAX
typedef int DataType_D; //数据元素类型
#define startCapacity 10 //起始容量
//动态顺序表
typedef struct DynamicList
{
DataType_D* data; //数据
int size; //有效元素个数
int capacity; //最大容纳元素个数
}DynamicList;
//初始化
bool InitList(DynamicList* lst);
//销毁顺序表,主要是释放堆上申请的空间
bool DestroyList(DynamicList* lst);
//检查容量
void checkCapacity(DynamicList* lst);
//插入
bool InsertList(DynamicList* lst, int pos, DataType_D val);
//删除
bool EraseList(DynamicList* lst, int pos);
//头插
bool pushFront(DynamicList* lst, DataType_D val);
//尾插
bool pushBack(DynamicList* lst, DataType_D val);
//头删
bool popFront(DynamicList* lst);
//尾删
bool popBack(DynamicList* lst);
//判空
bool IsEmpty(DynamicList* lst);
//打印顺序表
void printList(DynamicList* lst);
//根据值查找下标
int findIdx(DynamicList* lst, DataType_D val);
//根据下标查找值
DataType_D GetValue(DynamicList* lst, int pos);
//修改指定位置的值
bool setValue(DynamicList* lst, int pos, DataType_D val);
//获取有效元素个数
int getEleSize(DynamicList* lst);
//清空数据
void clearList(DynamicList* lst);
DynamicList.c:
#include "DynamicList.h"
//初始化
bool InitList(DynamicList* lst)
{
if (lst == NULL)
return false;
//startCapacity:起始容量
lst->data = (DataType_D*)malloc(sizeof(DataType_D)* startCapacity);
lst->size = 0;
lst->capacity = startCapacity;
return true;
}
//销毁顺序表,主要是释放堆上申请的空间
bool DestroyList(DynamicList* lst)
{
if (lst == NULL || lst->data == NULL)
return true;
free(lst->data);
lst->data = NULL;
return true;
}
//检查容量
void checkCapacity(DynamicList* lst)
{
if (lst == NULL || lst->data == NULL || lst->size != lst->capacity)
return;
int newC = lst->capacity * 2;
//重新开辟空间并拷贝原空间的数据
lst->data = (DataType_D*)realloc(lst->data, newC * sizeof(DataType_D));
/*
//另一种重开空间并拷贝
DataType_D* newData = (DataType_D*)malloc(sizeof(DataType_D) * newC);
memcpy(newData, lst->data, sizeof(DataType_D) * lst->capacity());
free(lst->data);
lst->data = newData;
*/
//更新
lst->capacity = newC;
}
//插入
bool InsertList(DynamicList* lst, int pos, DataType_D val)
{
if (lst == NULL || lst->data == NULL || pos < 0 || pos > lst->capacity)
return false;
//检查容量
checkCapacity(lst);
if (pos > lst->size)
pos = lst->size;
//移动数据
int idx = lst->size - 1;
while (idx >= pos)
{
lst->data[idx + 1] = lst->data[idx];
idx--;
}
//插入数据
lst->data[pos] = val;
//更新
++lst->size;
return true;
}
//删除
bool EraseList(DynamicList* lst, int pos)
{
if (lst == NULL || lst->data == NULL || pos < 0 || pos >= lst->capacity)
return false;
//移动元素
int idx = pos;
while (idx < lst->size - 1)
{
lst->data[idx] = lst->data[idx + 1];
idx++;
}
//更新
--lst->size;
return true;
}
//头插
bool pushFront(DynamicList* lst, DataType_D val)
{
return InsertList(lst, 0, val);
}
//尾插
bool pushBack(DynamicList* lst, DataType_D val)
{
return InsertList(lst, lst->size, val);
}
//头删
bool popFront(DynamicList* lst)
{
return EraseList(lst, 0);
}
//尾删
bool popBack(DynamicList* lst)
{
return EraseList(lst, lst->size);
}
//判空
bool IsEmpty(DynamicList* lst)
{
return lst == NULL || lst->data == NULL || lst->size == 0;
}
//打印顺序表
void printList(DynamicList* lst)
{
for (int i = 0; i < lst->size; ++i)
{
printf("%d ", lst->data[i]);
}
printf("\n");
}
//根据值查找下标:返回-1表示没找到
int findIdx(DynamicList* lst, DataType_D val)
{
if (lst == NULL || lst->data == NULL)
return -1;
for (int i = 0; i < lst->size; ++i)
{
if (lst->data[i] == val)
{
return i;
}
}
return -1;
}
//根据下标查找值
//约定没找到返回INT_MAX
DataType_D GetValue(DynamicList* lst, int pos)
{
if (lst == NULL || lst->data == NULL)
return INT_MAX;
if (pos < 0 || pos >= lst->size)
return INT_MAX;
return lst->data[pos];
}
//修改指定位置的值
bool setValue(DynamicList* lst, int pos, DataType_D val)
{
if (lst == NULL || lst->data == NULL || pos < 0 || pos >= lst->size)
return false;
lst->data[pos] = val;
return true;
}
//获取有效元素个数
int getEleSize(DynamicList* lst)
{
if (lst == NULL || lst->data == NULL)
return 0;
return lst->size;
}
//清空数据
void clearList(DynamicList* lst)
{
lst->size = 0;
}
4.顺序表的优缺点
优点:
尾插尾删效率高,时间复杂度为O(1);
支持按照下标随机访问;
空间连续,不会造成内存碎片;
增容代价高,需要开辟新空间和拷贝数据;
缺点:
头部和中间位置的插入和删除操作都需要移动元素,时间复杂度为O(N);
空间利用率不高,顺序表的空间可能会因为元素个数较小而被浪费