目录
1.线性表
定义:
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。
常见的线性表:
顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
定义:
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
一般分为
1.静态顺序表:使用定长数组存储。
2.动态顺序表:使用动态开辟的数组存储。
下面给出动态顺序表的接口
typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{SLDataType* array; // 指向动态开辟的数组
size_t size; // 有效数据个数
size_t capicity; // 容量空间的大小
}
// 基本增删查改接口
// 顺序表初始化void SeqListInit(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);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);
缺点:
1.在非尾部插入时,为了维持顺序表的三个顺序,需要对插入位置以后的元素进行后移。
2.扩容的时候,需要申请空间、拷贝数据、释放旧的空间,会有不少的浪费。
3.增容一般是2倍,会有一定的空间浪费。
为了解决以上问题,提出了链表这种线性结构。
3.链表
定义:
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表的类型:(共可以组成8种链表)
1.单向或者双向
2.带头或者不带头
3.循环或者非循环
我们一般使用两种极端的链表:
1.不带头单向非循环链表。(结构简单,运用广泛)
2.带头双向循环链表。 (结构复杂,容易实现)
链表的接口(以不带头单向非循环为例)
// 1、无头+单向+非循环链表增删查改实现
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
4.顺序表和链表的区别
| 不同点 | 顺序表 | 链表 |
|---|---|---|
| 存储空间 | 物理上一定连续 | 逻辑上连续,物理上不一定 |
| 访问 | 随机访问 | 顺序访问 |
| 插入/删除 | 可能需要移动元素 (保持顺序的三个顺序) | 只需要改变相关指针的指向 |
| 扩容 | 动态顺序表需要扩容 | 没有容量概念 |
| 应用场景 | 元素高效存储,访问频繁 | 任意位置的插入和删除 |
| 缓存利用率 | 高 | 低 |
5.动态顺序表和无头单链表的接口实现(代码)
a.动态顺序表
#pragma warning(disable:4996)
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size; //目前数据个数
int capacity; //容量大小
}SeqList;
//================辅助函数================
void checkAndChange(SeqList* ps)//检查 满则增加
{
SLDateType* newa = NULL;
if (ps->size >= ps->capacity)//表满
{
ps->capacity *= 2;
newa = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity);
if (newa == NULL)
{
perror("空间增加失败:");
exit(1);
}
ps->a = newa;
}
}
// 对数据的管理:增删查改
void SeqListInit(SeqList* ps)
{
SLDateType* newa = NULL;
ps->capacity = 100;//初始化100个
ps->size = 0;//初始数据为0个
newa = (SLDateType*)malloc(sizeof(SLDateType) * ps->capacity);
if (newa == NULL)
{
perror("初始化失败:");
exit(1);
}
ps->a = newa;
}
void SeqListDestroy(SeqList* ps)
{
ps->size = 0;
free(ps->a);
ps->a = NULL;
}
//打印
void SeqListPrint(SeqList* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("第%d个: %d\n", i + 1, ps->a[i]);
}
}
//尾插
void SeqListPushBack(SeqList* ps, SLDateType x)
{
checkAndChange(ps);
ps->a[ps->size] = x;
ps->size++;
}
//头插
void SeqListPushFront(SeqList* ps, SLDateType x)
{
int i = 0;
checkAndChange(ps);
for (i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[i] = x;
ps->size++;
}
//头删
void SeqListPopFront(SeqList* ps)
{
int i = 0;
assert(ps->size);
for (i = 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
//尾删
void SeqListPopBack(SeqList* ps)
{
assert(ps->size);
ps->size--;
}
// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x)//找到返回下标,找不到返回0
{
int cur = 0;
while (cur < ps->size)
{
if (ps->a[cur++] == x)
{
return cur;
}
}
return 0;
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
if (pos <= 0)
{
pos = 0;
}
if (pos > ps->size)
{
pos = ps->size;
}
int i = 0;
checkAndChange(ps);
for (i = ps->size; i > pos; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[i] = x;
ps->size++;
}
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
assert(ps->size);
if (pos <= 0)
{
pos = 0;
}
if (pos > ps->size)
{
pos = ps->size;
}
int i = 0;
for (i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
b.无头非循环单链表
#include<stdio.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode,*SList;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = NULL;
newnode = (SListNode*)malloc(sizeof(SListNode));
if (!newnode)
{
perror("空间分配失败");
}
newnode->next = NULL;
newnode->data = x;
return newnode;
}
// 单链表打印
void SListPrint(SListNode* plist)
{
while (plist)
{
printf("%d->", plist->data);
plist = plist->next;
}
printf("NULL\n");
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
SListNode* plist = NULL;
if (*pplist == NULL)
{
*pplist = newnode;
return;
}
plist = *pplist;
while (plist->next)
{
plist = plist->next;
}
plist->next = newnode;
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
SListNode* plist = *pplist;
if (plist->next == NULL)
{
*pplist = NULL;
free(plist);
return;
}
while (plist->next)
{
if (plist->next->next == NULL)
{
free(plist->next);
plist->next = NULL;
return;
}
plist = plist->next;
}
}
// 单链表头删
void SListPopFront(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
SListNode* plist = *pplist;
if (plist->next == NULL)
{
free(plist);
*pplist = NULL;
return;
}
plist = plist->next;
free(*pplist);
*pplist = plist;
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
while (plist)
{
if (plist->data == x)
{
return plist;
}
plist = plist->next;
}
return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos)
{
assert(pos);
SListNode* oldnode = pos->next;
if (oldnode)
{
pos->next = oldnode->next;
free(oldnode);
}
}
// 单链表的销毁
void SListDestroy(SList* pplist)
{
SListNode* plist = *pplist;
SListNode* oldnode = plist;
while (plist)
{
plist = plist->next;
free(oldnode);
oldnode = plist;
}
*pplist = NULL;
}
6.结语
大学生自用,仅供参考,如有错误欢迎指出。
文章介绍了线性表的概念,包括顺序表和链表两种常见形式,详细阐述了它们的定义、特点以及操作接口。顺序表以数组形式存储,提供了动态扩容和基本的增删查改操作,但存在插入和扩容的效率问题。链表则通过指针链接元素,支持更灵活的插入和删除操作。文章还提供了动态顺序表和无头单链表的C语言接口实现,并对比了两者的优缺点。
1072





