从零开始实现链表数据结构
创作时间:
2025-01-22 00:29:14
作者:
@小白创作中心
从零开始实现链表数据结构
链表是计算机科学中一种重要的数据结构,广泛应用于各种编程场景。本文将从链表的基本概念出发,详细讲解链表的定义、分类,并通过实际代码演示链表的实现过程,包括初始化、插入、查找、更新、删除等操作。
一、链表的定义
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点组成,每个节点包含两部分:数据域和指针域。数据域用于存储数据元素,指针域用于存储下一个节点的地址。
- 节点:链表的基本组成单位,包含数据域和指针域。
- 头指针:指向链表第一个节点的指针。
- 头节点:链表的第一个节点,数据域可以为空,主要用于简化链表操作。
- 首元节点:链表中第一个数据域不为空的节点。
二、链表的分类
链表主要可以分为以下几类:
- 静态链表和动态链表:根据内存分配方式区分。
- 单向链表、双向链表和循环链表:根据节点连接方式区分。
单链表:每个节点包含一个指向下一个节点的指针。
双向链表:每个节点包含两个指针,分别指向前后两个节点。
循环链表:链表首尾相连,形成闭环。可以是单向循环链表或双向循环链表。
三、链表的实现
下面将通过代码实现一个简单的单链表。我们将使用C#语言,通过手动管理内存来实现链表的基本操作。
1. ADT定义
首先定义链表的抽象数据类型(ADT):
ADT LinkedList{
数据对象:D 是一个非空的元素集合,D = {a1, a2, ..., an},其中 ai 表示一个元素即节点,一个节点存储着数据和指向下一个节点的指针。
数据关系:D中的节点通过指针进行连接,每一个节点都包含一个指向下一个节点的指针。
基本操作:[
Init(n) :初始化一个空链表,即声明一个头指针,如有必要也可以声明一个头节点。
Length:返回链表长度。
HeadNode:返回头节点。
Find(v):返回数据域v对应的节点。
Update(n,v):更新n节点的数据域。
InsertAfter(n,v):在n节点后面添加数据域为v的新节点。
Remove(n):移除n节点。
Destroy():销毁链表。
]
}
2. 定义类
定义节点结构体:
public struct MyselfLinkedListNode
{
//数据域
public string Data { get; set; }
//指针域
public IntPtr Next { get; set; }
}
定义链表实现类:
public sealed class MyselfLinkedList : IDisposable
{
//申请内存起始位置指针
private IntPtr _head;
//链表长度
private int _length;
}
3. 初始化链表
public MyselfLinkedListNode Init()
{
var size = Marshal.SizeOf(typeof(MyselfLinkedListNode));
_head = Marshal.AllocHGlobal(size);
var node = new MyselfLinkedListNode
{
Data = null,
Next = IntPtr.Zero
};
Marshal.StructureToPtr(node, _head, false);
_length++;
return node;
}
4. 获取链表长度
public int Length
{
get
{
return _length;
}
}
5. 获取头节点
public MyselfLinkedListNode? HeadNode
{
get
{
if (_head == IntPtr.Zero)
{
return null;
}
return GetNode(_head);
}
}
private MyselfLinkedListNode GetNode(IntPtr pointer)
{
return Marshal.PtrToStructure<MyselfLinkedListNode>(pointer);
}
6. 插入节点
public MyselfLinkedListNode InsertAfter(MyselfLinkedListNode node, string value)
{
var pointer = GetPointer(node);
if (pointer != IntPtr.Zero)
{
var (newPointer, newNode) = CreateNode(value);
newNode.Next = node.Next;
node.Next = newPointer;
Marshal.StructureToPtr(newNode, newPointer, false);
Marshal.StructureToPtr(node, pointer, false);
_length++;
return newNode;
}
return default;
}
private IntPtr GetPointer(MyselfLinkedListNode node)
{
var currentPointer = _head;
while (currentPointer != IntPtr.Zero)
{
var currentNode = GetNode(currentPointer);
if (currentNode.Data == node.Data && currentNode.Next == node.Next)
{
return currentPointer;
}
currentPointer = currentNode.Next;
}
return IntPtr.Zero;
}
private (IntPtr Pointer, MyselfLinkedListNode Node) CreateNode(string value)
{
var size = Marshal.SizeOf(typeof(MyselfLinkedListNode));
var pointer = Marshal.AllocHGlobal(size);
var node = new MyselfLinkedListNode
{
Data = value,
Next = IntPtr.Zero
};
Marshal.StructureToPtr(node, pointer, false);
return (pointer, node);
}
7. 查找节点
public MyselfLinkedListNode Find(string value)
{
var pointer = _head;
while (pointer != IntPtr.Zero)
{
var node = GetNode(pointer);
if (node.Data == value)
{
return node;
}
pointer = node.Next;
}
return default;
}
8. 更新节点
public void Update(MyselfLinkedListNode node, string value)
{
var pointer = GetPointer(node);
if (pointer != IntPtr.Zero)
{
node.Data = value;
Marshal.StructureToPtr(node, pointer, false);
}
}
9. 删除节点
public void Remove(MyselfLinkedListNode node)
{
var currentPointer = _head;
var currentNode = GetNode(_head);
var pointer = GetPointer(node);
while (true)
{
if (currentNode.Next == IntPtr.Zero)
{
return;
}
else if (currentNode.Next == pointer)
{
currentNode.Next = node.Next;
Marshal.FreeHGlobal(pointer);
Marshal.StructureToPtr(currentNode, currentPointer, false);
_length--;
break;
}
else
{
currentPointer = currentNode.Next;
currentNode = GetNode(currentPointer);
}
}
}
10. 销毁链表
public void Destroy()
{
var pointer = _head;
while (pointer != IntPtr.Zero)
{
var value = GetNode(pointer);
Marshal.FreeHGlobal(pointer);
_length--;
pointer = value.Next;
}
_head = IntPtr.Zero;
}
11. 释放内存
由于实现了IDisposable
接口,需要在Dispose
方法中调用上面的Destroy
方法。
注:完整的测试代码和示例源码可以参考以下仓库:https://gitee.com/hugogoos/Planner
热门推荐
上海迪士尼必玩攻略:翱翔·飞越地平线,13分钟飞越13个世界奇观
周末亲子游:上海迪士尼攻略大揭秘!
上海迪士尼奇幻城堡摄影攻略:从入门到出片
胰岛自身抗体阳性,如何确定是否为1型糖尿病?分 3 步
糖尿病防治:您需要了解的基本知识与生活管理措施
糖尿病如何鉴别
晕车恶心想吐?这些方法助你快速缓解不适
“须知少时凌云志”:从清代诗歌到当代荧屏的共鸣
李白笔下的少年壮志:你敢不敢像他一样?
清朝试婚格格的去向:揭秘古代皇家的独特习俗
清朝格格的丈夫称谓解析
年夜饭必吃红烧肉,你家怎么做的?
春节家庭旅游攻略,让你的春节假期更精彩
叛逆期孩子与父母的关系
编程使用什么CPU较好,AMD还是Intel
养宠新手必看:如何科学照顾小奶狗?
双十一囤货:小奶狗健康必备清单
怎么查婚姻登记信息
东京到京都的省钱秘籍:高速巴士完全攻略
旅途出现晕车、晕船等不适怎么办?教你几招应对!
新手做饭小白必看:教你科学去除果蔬农残
六味地黄丸:千年养生名方的现代应用与注意事项
知柏地黄丸与六味地黄丸的历史沿革与现代应用
张仲景教你辨识六味地黄丸与知柏地黄丸
养宠物真的会影响宝宝健康吗?
幼犬与新生儿和谐共处指南
广元市中医院事件:3000元"专家费"引发的医患信任危机
尿素偏高的原因及危害
深度探索:颠覆硅谷的中国人工智能初创公司
不同麦克风设计的区别和工作原理是什么?