顺序表
【本节目标】
1.线性表
2.顺序表
3.链表
4.顺序表和链表的区别和联系
**1.线性表**
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结
构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物
理上存储时,通常以数组和链式结构的形式存储。**
**2.顺序表**
2.1概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组
上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储。
- 动态顺序表:使用动态开辟的数组存储。
// 顺序表的静态存储
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; // 定长数组
size_t size; // 有效数据的个数
}SeqList;
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
2.2 接口实现:
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多
了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下
面我们实现动态顺序表。
```c
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
// 基本增删查改接口
void SeqListInit(SeqList* psl, size_t capacity);
void SeqListDestory(SeqList* psl);
void CheckCapacity(SeqList* psl);
void SeqListPushBack(SeqList* psl, SLDataType x);
void SeqListPopBack(SeqList* psl);
void SeqListPushFront(SeqList* psl, SLDataType x);
void SeqListPopFront(SeqList* psl);
int SeqListFind(SeqList* psl, SLDataType x);
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
void SeqListErase(SeqList* psl, size_t pos);
void SeqListRemove(SeqList* psl, SLDataType x);
void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
void SeqListPrint(SeqList* psl);
int SeqListBinaryFind(SeqList* psl, SLDataType x);
void SeqListRemoveAll(SeqList* psl, SLDataType x);
接口实现
#include "Seqlist.h"
void SeqlistInit(Seqlist* ps)
{
ps->_a = NULL;
ps->size = 0;
ps->capicity = 0;
}
void SeqListDestory(Seqlist* ps)
{
}
void SeqListCheck(Seqlist* ps)
{
if (ps->capicity == ps->size)
{
size_t num = ps->capicity == 0 ? 4 : ps->capicity * 2;
ps->_a = realloc(ps->_a, num * sizeof(Datetype));
ps->capicity *= 2;
}
}
void SeqListPushBack(Seqlist* ps, Datetype x)
{
SeqListCheck(ps);
ps->_a[ps->size++] = x;
}
void SeqListprint(Seqlist* ps)
{
for (size_t i = 0; i < ps->size; i++)
{
printf("%d ", ps->_a[i]);
}
printf("\n");
}
void SeqListPopBack(Seqlist* ps)
{
ps->_a[ps->size--] = 0;
}
void Seqlistchange(Seqlist* ps, Datetype x , Datetype y)
{
int ret = ps->size - 1;
while (ret)
{
if (ps->_a[ret] == x)
{
ps->_a[ret] = y;
}
else
{
ret--;
}
}
return 0;
}
void SeqListPopRront(Seqlist* ps)
{
for (size_t i = 0; i < ps->size-1; i++)
ps->_a[i] = ps->_a[i + 1];
ps->size--;
}
void SeqListFind(Seqlist* ps, Datetype x)
{
//for (size_t i = 0; i < ps->size - 1; i++)
//{
// if (x == ps->_a[i])
// {
// printf("第%d是这个数\n",ps->_a[i]-1);
// }
//}
//
//
// printf("没有这个数\n");
// }
int tmp = ps->size - 1;
while (tmp)
{
if (x == ps->_a[tmp])
{
printf("第%d是这个数\n",tmp-1);
return tmp ;
}
tmp--;
}
printf("没有这个数\n");
return 0;
}
void SeqListPushFront(Seqlist* ps, Datetype x)
{
assert(ps);
SeqListCheck(ps);
Datetype end = ps->size - 1;
for (; end >= 0; end--){
ps->_a[end + 1] = ps->_a[end];
}
ps->_a[0] = x;
ps->size++;
}
- 中间/头部的插入删除,时间复杂度为O(N)
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
- 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,
我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。