一、线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…等
线性表的存储结构
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
二、顺序表
1. 概念
顺序表是是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。顺序表本质是数组,但是可以动态增长并且完成数据的增删查改,其数据必须从第一个位置开始,从左往右连续存储,逻辑结构和物理结构是一致的。另外顺序表缺陷是:动态增容要性能消耗,并且头部插入数据需要挪动数据。
2. 顺序表分类
顺序表可以分为:
- 静态顺序表:使用定长数组存储元素。
- 动态顺序表:使用动态开辟的数组存储。
对比
静态顺序表只适用于确定知道需要存多少数据的场景,并且静态顺序表使用的定长数组个数多了,造成空间浪费,开少了又会导致空间不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小。
3. 顺序表的缺陷:
- 中间/头部的插入删除,时间复杂度为O(N)
- 增容需要申请新空间,拷贝数据,释放旧空间,有不小的消耗。
- 增容一般是呈2倍的增长,势必会有一定的空间浪费。
三、顺序表的实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType; //这样更改存放的数据类型时只需改这里的int即可
typedef struct SeqList
{
SLDataType* a; //动态数组
size_t size; //有效数据的个数
size_t capacity; //容量
}SeqList;
// 初始化顺序表
void SeqListInit(SeqList* pq);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* pq);
// 顺序表尾插
void SeqListPushBack(SeqList* pq, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* pq);
// 顺序表头插
void SeqListPushFront(SeqList* pq, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* pq);
// 顺序表查找
int SeqListFind(SeqList* pq, SLDataType x);
// 顺序表修改值
void SLModify(SL* ps, int pos, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* pq, int pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* pq, int pos);
// 顺序表销毁
void SeqListDestory(SeqList* pq);
// 顺序表打印
void SeqListPrint(SeqList* pq);
void SeqListInit(SeqList* pq)
{
assert(pq); //断言传过来的结构体指针不为NULL
pq->a = NULL; //将动态数组指针置空
pq->capacity = pq->size = 0; //将有效数据的个数和容量置为零
}
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* pq)
{
if (pq->capacity == pq->size)//如果有效数据的个数和容量相等,则扩容
{
int NewCapacity = (pq->capacity == 0) ? 4 : (pq->capacity) * 2;//扩容空间大小
SLDataType* NewA = (SLDataType*)realloc(pq->a, sizeof(SLDataType) * NewCapacity);//realloc扩容
if (NewA == NULL) //如果扩容失败,报错
{
printf("realloc fial\n");
exit(-1);//直接结束程序
}
pq->a = NewA; //改变指向动态数组的指针
pq->capacity = NewCapacity;//改变容量
}
}
// 顺序表尾插
void SeqListPushBack(SeqList* pq, SLDataType x)
{
assert(pq);
CheckCapacity(pq);
pq->a[pq->size] = x;
pq->size++;
}
// 顺序表尾删
void SeqListPopBack(SeqList* pq)
{
assert(pq);
assert(pq->size > 0);
pq->size--;
}
// 顺序表头插
void SeqListPushFront(SeqList* pq, SLDataType x)
{
assert(pq);
CheckCapacity(pq);
int end =(pq->size) - 1;//找到最后一个元素的下标
while (end>=0)
{
pq->a[end + 1] = pq->a[end];//挪动数据
end--;
}
pq->a[0] = x;
pq->size++;
}
// 顺序表头删
void SeqListPopFront(SeqList* pq)
{
assert(pq);
assert(pq->size > 0);
int begin = 1; //找到第二个元素的下标
while (begin < (pq->size))
{
pq->a[begin-1] = pq->a[begin]; //挪动数据
begin++;
}
pq->size--;
}
// 顺序表查找
int SeqListFind(SeqList* pq, SLDataType x)
{
assert(pq);
int i = 0;
for (i = 0; i < pq->size; i++)
{
if (x == pq->a[i]) //遍历顺序表中的元素
return i;
}
return -1;
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* pq, int pos, SLDataType x)
{
assert(pq);
assert(pos >= 0 && pos <= pq->size); //pos要>=0并且pos<=size
CheckCapacity(pq);
int end = pq->size - 1; //找到第一个元素的下标
while (end>=pos)
{
pq->a[end + 1] = pq->a[end];//挪动数据
end--;
}
pq->a[pos] = x;
pq->size++;
}
// 顺序表删除pos位置的值
void SeqListErase(SeqList* pq, int pos)
{
assert(pq);
assert(pos >= 0 && pos < pq->size);
int begin = pos;
while (begin <= pq->size - 1)
{
pq->a[begin] = pq->a[begin + 1];//挪动数据
begin++;
}
pq->size--;
}
// 修改顺序表的值
void SLModify(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
// 顺序表销毁
void SeqListDestory(SeqList* pq)
{
assert(pq);
free(pq->a);
pq->capacity = pq->size = 0;
}
// 顺序表打印
void SeqListPrint(SeqList* pq)
{
assert(pq);
int i = 0;
for (i = 0; i < pq->size; i++)
{
printf("%d ", pq->a[i]);
}
printf("\n");
}
int main()
{
SeqList s;
SeqListInit(&s);
SeqListPushBack(&s, 12);
SeqListPushBack(&s, 13);
SeqListPushBack(&s, 14);
SeqListPushFront(&s, 11);
SeqListPushFront(&s, 10);
SeqListPrint(&s);
SeqListInsert(&s, SeqListFind(&s, 13),18);
SeqListPrint(&s);
SeqListDestory(&s);
}