线性表——顺序表和链表
Posted 聪明的骑士
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表——顺序表和链表相关的知识,希望对你有一定的参考价值。
目录
C语言代码实现:(1条消息) 无头单向非循环链表_聪明的骑士的博客-CSDN博客
一、线性表
线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构。
常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
二、顺序表
1.顺序表的概念
简单地说,顺序表就是数组。
2.静态与动态顺序表
//顺序表的静态存储
#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;
静态的顺序表一次性开辟空间,不可扩容;动态顺序表可以根据数据的储存在堆区扩容。
3.动态顺序表的代码实现
#define TYPE int
struct SeqList
TYPE* SL;
int volume;
int size;
;
typedef struct SeqList SList;
//初始化
void InitSeqlist(SList* p);
//扩容
void enlarge(SList* p);
//打印
void print(SList* p);
//销毁
void destory(SList* p);
//对顺序表实现增删查改的函数
//增加元素
void Seqlist_front_push(SList* p, TYPE a);//前增
void Seqlist_back_push(SList* p, TYPE a); //后增
//减少元素
void Seqlist_front_pop(SList* p); //前减
void Seqlist_back_pop(SList* p);//后减
//查找对应下标的元素,没有找到就返回-1
int Seqlist_search_element(SList* p,TYPE a);
//修改对应下标的元素
void Seqlist_modify_element(SList* p, size_t pos, TYPE b);
// 顺序表在pos位置插入a
void SLInsert(SList* p, size_t pos, TYPE a);
// 顺序表删除pos位置的值
void SLErase(SList* p, size_t pos);
包括了顺序表的增删查改的操作,下面是代码实现:
(4条消息) 动态顺序表的代码实现_聪明的骑士的博客-CSDN博客
三、链表
1.概念及结构概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
就像寻宝游戏中的线索一样,你有了一条线索,然后沿着这条线索寻找,你会找到下一条线索,沿着找到的条线索寻找,你又会找到下一条线索,这样层层递进最后就能找到宝物,所有的线索也都被串联起来了。
你如果在上一层的数据中存放下一层的信息,那么这一层层的信息就可以不断向下寻找,我们的数据就也被串联起来了,这就是一种链表的简单概括。
链表的思想,就是结构体的自引用的应用。
2.链表的种类
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
(1)单向、双向
单向链表只存在一个next指针,只支持向后访问。
双向链表有prev和next指针,可以向后或向前访问。
(2)带头、不带头
带头指带哨兵卫的头节点,此时我们的头节点不再存储有效数据,只存储地址下一个位置作为头节点。通过哨兵卫就可以避免二级指针并简化代码。
(3)循环、非循环
循环的链表尾节点指向头节点,反之亦然,这样就做到了头部和尾部的快速访问。
这三个二选一的选择题经过组合就是一种类型。
4.特殊链表的实现
我们实际中最常用还是两种结构:无头单向非循环链表和带头双向循环链表
(1)无头单向非循环链表
结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
#define TYPE int
typedef struct SListNode
TYPE data;
struct SListNode* next;
SLNode;
//动态申请一个节点
SLNode* BuySListNode(TYPE x);
// 单链表打印
void SListPrint(SLNode* plist);
// 单链表尾插
void SListPushBack(SLNode** pplist,TYPE x);
// 单链表的头插
void SListPushFront(SLNode** pplist, TYPE x);
// 单链表的尾删
void SListPopBack(SLNode** pplist);
// 单链表头删
void SListPopFront(SLNode** pplist);
// 单链表查找
SLNode* SListFind(SLNode* plist, TYPE x);
C语言代码实现:(1条消息) 无头单向非循环链表_聪明的骑士的博客-CSDN博客
(2)带头双向循环链表
结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势。
#define TYPE int
typedef struct DoubleList
TYPE* data;
struct DoubleList* prev;
struct DoubleList* next;
DList;
//初始化双向链表
DList* InitDList(DList* p);
//初始化节点
DList* Buylistnode(TYPE x);
//头插
void DListfrontpush(DList* p, TYPE x);
//尾插
void DListbackpush(DList* p, TYPE x);
//头删
void DListfrontpop(DList* p);
//尾删
void DListbackpop(DList* p);
//判读链表是否为空
bool ListEmpty(DList* p);
//计算大小
size_t ListSize(DList* p);
//寻找元素
DList* ListFind(DList* p, TYPE x);
// 在pos之前插入
void ListInsert(DList* pos, TYPE x);
// 删除pos位置
void ListErase(DList* pos);
//考虑用一级指针,让调用ListDestory的人置空(保持接口一致性)
void ListDestory(DList* p);
//打印链表
void print(DList* p);
C语言代码实现:带头双向循环链表_聪明的骑士的博客-CSDN博客
四、顺序表和链表的区别和联系
1.顺序表
优势:空间连续、支持随机访问
劣势:
(1)中间或前面部分的插入删除时间复杂度O(N)
(2)增容的代价比较大。
2.链表
劣势:以节点为单位存储,不支持随机访问
优势:
(1)任意位置插入删除时间复杂度为O(1)
(2)没有增容问题,插入一个开辟一个空间。
顺序表和链表介绍
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
本篇梗概
线性表
线性表是N个同类型的数据元素的有限序列。
常见的线性表有:顺序表,队列,链表等
线性表在逻辑上是连续的,也就是连续的一条直线结构。但在物理结构不一定连续,即地址不一定连续。线性表通常以数组和链式结构的形式存储。
1.1顺序表
顺序表是一种线性结构,他的元素地址连续。通常采用数组存储。
顺序表分类:
1.静态数据表:使用定长数组存储
2.动态数据表:使用动态开辟的数组存储
//顺序表的静态存储
#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;
静态顺序表的空间开大了浪费,开小了不够用,只适用于确定知道需要存多少数据的场景。所以现实中基本都是使用动态顺序表。
这是顺序表的存储结构,所有的数据都是存储在连续空间当中。当插入或删除元素时需要移动大量数据,插入/删除位置后面的每个元素都要移动,而查找元素只需要根据内存中的地址查找就可以了。
所以时间复杂度时O(N)。而查找元素由于每个数据在内存中的地址都是连续的,所以复杂度是O(1)。
1.2链表
链表是一种非连续非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现。
常规的申请内存的方式,每个内存空间只能存放数据,不带指针,为了定义这种带指针的空间,需要借助结构体。
//节点的定义
typedef struct SlistNode
{
DataType data;
struct SlistNode *next;//定义了一个结构体指针,这样每一个结构体就包含了一个想存储的数据,和一个指向下一个同类型结构体的指针
}Node;
//此时Node就变成了结构体类型(相当于int,float等),是我们人为定义的一个数据类型
链表的种类:
1.单项,双项
2.带头,不带头
3.循环,非循环
无头单项非循环链表
带头双向循环链表
无头单向链表和带头双向链表是最常用的。
顺序表和链表对比:
链表插入元素的时间复杂度为O(1),查找元素复杂度为O(N)。
顺序表插入时间复杂度为O(N),查找为O(1)。
总结
以上是介绍了顺序表和链表的基本概念。希望大家有所收获。至于链表的增删查改等操作,会在下篇文章介绍。
以上是关于线性表——顺序表和链表的主要内容,如果未能解决你的问题,请参考以下文章