数据结构——链表(超详细解读)
创作时间:
作者:
@小白创作中心
数据结构——链表(超详细解读)
引用
CSDN
1.
https://blog.csdn.net/2303_81146519/article/details/142530281
链表是一种常见的数据结构,它通过指针将数据元素链接在一起,形成一个线性序列。与顺序表不同,链表的元素在内存中可以是不连续的,这使得链表在插入和删除操作上具有更高的效率。本文将详细介绍链表的基本概念、分类以及单链表的各种操作实现。
一、链表的概念和结构
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表的基本组成单元)组成,结点可以在运行时动态生成。
图中的phead指针中存放的是第一个结点的地址,根据这个地址可以找到这个结构体,又因为这个结构体中存放了下一个结构体的地址,所以又可以找到第二个结构体,循环往复就可以找到所有的结点,直到存放空地址的结构体。
注:图中的箭头实际上是不存在的,这里只是为了方便理解。
注意:
- 从图中可以看出,链式结构在逻辑上是连续的,但在物理上不一定连续。
- 现实中的结点一般都是从堆上申请出来的。
- 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。
二、链表的分类
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
虽然链表结构如此之多,但是我们常用的就只有两种:
- 单链表:无头+单向+非循环
- 双链表:无头+双向+非循环
三、单链表的实现
所谓单链表就是无头+单向+非循环 链表。
动态申请节点
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
assert(plist);
SListNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newnode = BuySListNode(x);
if (*(pplist) == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
newnode->next = *pplist;
*pplist = newnode;
}
}
单链表的尾删
void SListPopBack(SListNode** pplist)
{
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* tail = *pplist;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
单链表的头删
void SListPopFront(SListNode** pplist)
{
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* next = (*pplist)->next;
free(*pplist);
*pplist = next;
}
}
单链表在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
SListNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
单链表在pos位置之前插入
void SListInsertFront(SListNode** pplist, SListNode* pos, SLTDateType x)
{
assert(pos);
assert(*pplist);
if (pos == *pplist)
{
SListPushFront(pplist, x);
}
else
{
SListNode* prev = *pplist;
while (prev->next != pos)
{
prev = prev->next;
}
SListNode* newnode = BuySListNode(x);
newnode->next = pos;
prev->next = newnode;
}
}
删除pos位置的值
void SListErase(SListNode** pplist, SListNode* pos)
{
assert(pos);
assert(*pplist);
if (pos == *pplist)
{
SListPopFront(pplist);
}
else
{
SListNode* prev = *pplist;
while (prev->next != pos)
{
prev = prev->next;
}
SListNode* next = pos->next;
free(pos);
prev->next = next;
}
}
单链表的销毁
void SListDestroy(SListNode** pplist)
{
assert(*pplist);
while (*pplist)
{
SListNode* prev = *pplist;
*pplist = (*pplist)->next;
free(prev);
}
}
四、完整代码
SList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
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位置之前插入x
void SListInsertFront(SListNode** pplist, SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 删除pos位置
void SListErase(SListNode** pplist, SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** pplist);
SList.c
#include "SList.h"
void SListPrint(SListNode* plist)
{
assert(plist);
while (plist)
{
printf("%d ", plist->data);
plist = plist->next;
}
printf("NULL\n");
}
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
assert(newNode);
newNode->data = x;
newNode->next = NULL;
return newNode;
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newNode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newNode;
}
else
{
SListNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newNode;
}
}
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newNode = BuySListNode(x);
newNode->next = *pplist;
*pplist = newNode;
}
void SListPopBack(SListNode** pplist)
{
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* tail = *pplist;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListPopFront(SListNode** pplist)
{
assert(*pplist);
SListNode* next = (*pplist)->next;
free(*pplist);
*pplist = next;
}
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
assert(plist);
while (plist)
{
if (plist->data == x)
{
return plist;
}
plist = plist->next;
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
SListNode* next = pos->next;
pos->next = newnode;
newnode->next = next;
}
void SListInsertFront(SListNode** pplist, SListNode* pos, SLTDateType x)
{
assert(pos);
assert(*pplist);
if (pos == *pplist)
{
SListPushFront(pplist, x);
}
else
{
SListNode* prev = *pplist;
while (prev->next != pos)
{
prev = prev->next;
}
SListNode* newnode = BuySListNode(x);
newnode->next = pos;
prev->next = newnode;
}
}
void SListErase(SListNode** pplist, SListNode* pos)
{
assert(pos);
assert(*pplist);
if (pos == *pplist)
{
SListPopFront(pplist);
}
else
{
SListNode* prev = *pplist;
while (prev->next != pos)
{
prev = prev->next;
}
SListNode* next = pos->next;
free(pos);
prev->next = next;
}
}
void SListDestroy(SListNode** pplist)
{
assert(*pplist);
while (*pplist)
{
SListNode* prev = *pplist;
*pplist = (*pplist)->next;
free(prev);
}
}
链表和顺序表是两种常见的线性数据结构,它们各有优劣。链表在插入和删除操作上具有更高的效率,而顺序表在随机访问上具有优势。理解这两种数据结构的原理和使用场景,对于编写高效的数据处理程序至关重要。
热门推荐
印度女性鼻环:从宗教符号到文化象征的演变
UI设计动图:让你的作品活起来!
零基础学做肉丸:轻松掌握从调味到成型的技巧
福建美食大搜罗:富昌茶树菇、金唐紫菜、方家铺子海蛎干
探秘振成楼:福建土楼的建筑传奇与文化瑰宝
新春探访清新福建:独特文旅体验
谢洪涛当选东莞书协主席,将推动书法艺术发展
1954年,郑州成为新的河南省省会,在这场较量中,开封是怎么输的
2024年人工智能国防应用的10个实例
秋冬季节自驾游安全指南
世界镇痛日:这些顽痛“三叉神经痛、舌咽神经痛、偏头疼”别再硬抗
成都必吃的五大美食:从街头小吃到经典名菜,每一道都是地道美味
家庭聚会上的健康祝酒词
《别叫我酒神》教你职场完美祝酒词
掌握商务饭局祝酒词,轻松成社交达人!
云聚餐时如何说好祝酒词?这些新玩法让你秒变"酒桌达人"
龙利鱼:少刺美味,轻松上手!
职场必备:鱼刺卡喉的科学应对指南
春节自驾游必打卡:川西稻城香格里拉
太原出发,5天4晚自驾游桐庐和太湖林屋梅海,感受不一样的春节风情!
环太湖自驾游:春节必打卡的沿线美食
春节自驾游:滇东南之春vs庐山西海水上公路,哪条路线更适合你?
澳门必打卡:妈阁庙+大三巴+官也街
《内陆之行》:一场关于自由与成长的心灵之旅
跟随汉德克的脚步:探索法国内陆的秘密
《内陆之行》:汉德克笔下的一场心灵探索之旅
九芝堂足光散:脚部湿疹防护的中药良方
雨季来袭,如何避免脚癣困扰?
喝牛奶真的能缓解脚部湿疹吗?
特应性皮炎患者日常护理全攻略:从《全程管理共识》到生活实践