博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 算法之链表、双向链表以及正向反向遍历实现
阅读量:5890 次
发布时间:2019-06-19

本文共 9360 字,大约阅读时间需要 31 分钟。

1、简介

链表是一种非常基础的数据结构之一,我们在日常开发种都会接触到或者是接触到相同类型的链表数据结构.所以本文会使用C#算法来实现一个简单的链表数据结构,并实现其中几个简单的api以供使用.

 

2、概述

链表是一种递归的数据结构,他或者为null,或者是指向像一个节点的(node)的引用,该节点含有一个泛型的元素(当然可以是非泛型的,但是为了充分利用C#的优势,切让链表更具有灵活性,这里使用泛型)和指向另一个链表的引用.

 

3、实战 单向链表

如下图,因为下一个节点对象没有保持上个节点的引用,所以这种链表称之为单向链表

实现代码如下,这边我使用了迭代器模式,方便节点的单向遍历,因为没有使用MS提供的标准的迭代器接口,所以无法使用foreach遍历.

///     /// C#链表实现    ///     public class LinkedList    {        static void Main(string[] args)        {            //生成对应的Node节点            var nodeFirst = new Node
(); var nodeSecond = new Node
(); var nodeThird = new Node
(); //构造节点内容 nodeFirst.Item = "one"; nodeSecond.Item = "two"; nodeThird.Item = "three"; //链接节点 nodeFirst.NodeItem = nodeSecond; nodeSecond.NodeItem = nodeThird; //注:这里nodeThird的NodeItem指向null var nodeEnumerator = nodeFirst.GetEnumerator(); while (nodeEnumerator.MoveNext()) { Console.Write($"当前节点元素内容:{nodeEnumerator.CurrentItem}"); //这里如果当前节点的下一个节点不为空,则让当前节点变为下一个节点 if (nodeEnumerator.SetNext()) Console.WriteLine($"下一个节点内容:{nodeEnumerator.CurrentNode.Item}"); else Console.WriteLine($"链表遍历结束,下一个节点内容为null"); } Console.ReadKey(); } ///
/// 节点对象,使用迭代器模式,实现链表的遍历 /// ///
public class Node
: ILinkedListEnumerable
{ public T Item { get; set; } public Node
NodeItem { get; set; } public ILinedListEnumerator
GetEnumerator() { return new NodeEnumerator
(this); } } private class NodeEnumerator
: ILinedListEnumerator
{ public Node
CurrentNode { get; set; } public NodeEnumerator(Node
node) { CurrentNode = node; } public T CurrentItem => CurrentNode.Item; public bool MoveNext() { if (CurrentNode!=null) return true; return false; } public bool SetNext() { if (CurrentNode.NodeItem != null) { CurrentNode = CurrentNode.NodeItem; return true; } else { CurrentNode = null; return false; } } ///
/// 当迭代器内部存在非托管资源时,用于释放资源 /// public void Dispose() { throw new NotImplementedException(); } } ///
/// 链表迭代器接口约束 /// ///
public interface ILinkedListEnumerable
{ ILinedListEnumerator
GetEnumerator(); } ///
/// 链表单个迭代器 /// ///
public interface ILinedListEnumerator
: IDisposable { ///
/// 当前迭代器元素 /// Node
CurrentNode { get; } ///
/// 当前迭代器元素内容 /// T CurrentItem { get; } ///
/// 是否可以进行下一步遍历操作 /// ///
bool MoveNext(); ///
/// 遍历完当前链表元素,使其指向下一个元素 /// bool SetNext(); } }

 

4、实战 双向链表

双向链表的应用场景很多,比如Redis的就是使用双向链表实现的.这种形式的链表更加的灵活.

修改代码如下:

///     /// C#链表实现    ///     public class LinkedList    {        static void Main(string[] args)        {            //生成对应的Node节点            var nodeFirst = new Node
(); var nodeSecond = new Node
(); var nodeThird = new Node
(); //构造节点内容 nodeFirst.Item = "one"; nodeSecond.Item = "two"; nodeThird.Item = "three"; //链接节点 nodeFirst.NextNode = nodeSecond; nodeSecond.NextNode = nodeThird; //注:这里nodeThird的NextNode指向null var nodeEnumerator = nodeFirst.GetEnumerator(); while (nodeEnumerator.MoveNext()) { //输出当前节点的内容 Console.Write($"当前节点元素内容:{nodeEnumerator.CurrentItem} "); //输出上一个节点的内容 Console.Write($"上一个节点元素内容:{nodeEnumerator.PreviousNode?.Item??"没有上一个节点"} "); //这里如果当前节点的下一个节点不为空,则让当前节点变为下一个节点 if (nodeEnumerator.SetNext()) Console.WriteLine($"下一个节点内容:{nodeEnumerator.CurrentNode.Item}"); else Console.WriteLine($"链表遍历结束,下一个节点内容为null"); } Console.ReadKey(); } ///
/// 节点对象,使用迭代器模式,实现链表的遍历 /// ///
public class Node
: ILinkedListEnumerable
{ public T Item { get; set; } public Node
NextNode { get; set; } public ILinedListEnumerator
GetEnumerator() { return new NodeEnumerator
(this); } } private class NodeEnumerator
: ILinedListEnumerator
{ public Node
PreviousNode { get; set; } public Node
CurrentNode { get; set; } public NodeEnumerator(Node
node) { CurrentNode = node; } public T CurrentItem => CurrentNode.Item; public bool MoveNext() { if (CurrentNode!=null) return true; return false; } public bool SetNext() { if (CurrentNode.NextNode != null) { PreviousNode = CurrentNode; CurrentNode = CurrentNode.NextNode; return true; } else { CurrentNode = null; return false; } } ///
/// 当迭代器内部存在非托管资源时,用于释放资源 /// public void Dispose() { throw new NotImplementedException(); } } ///
/// 链表迭代器接口约束 /// ///
public interface ILinkedListEnumerable
{ ILinedListEnumerator
GetEnumerator(); } ///
/// 链表单个迭代器 /// ///
public interface ILinedListEnumerator
: IDisposable { ///
/// 上一个迭代器元素 /// Node
PreviousNode { get; } ///
/// 当前迭代器元素 /// Node
CurrentNode { get; } ///
/// 当前迭代器元素内容 /// T CurrentItem { get; } ///
/// 是否可以进行下一步遍历操作 /// ///
bool MoveNext(); ///
/// 遍历完当前链表元素,使其指向下一个元素 /// bool SetNext(); } }

 

5、通过双向链表实现反向遍历

如果没有实现链表的双向功能,实现反向遍历的功能是不可能,实际上Redis的List是实现了这个功能的,所以这里我也实现下,tip:目前为止,所以的遍历都是先进先出的,类似于队列,所以如果实现了反向遍历,从而该双向链表同时也支持了先进后出的功能,类似于栈,为了分离正向和反向这个遍历过程,所以我实现了两个迭代器,分别为正向迭代器和反向迭代器.

代码如下:

///     /// C#链表实现    ///     public class LinkedList    {        static void Main(string[] args)        {            //生成对应的Node节点            var nodeFirst = new Node
(); var nodeSecond = new Node
(); var nodeThird = new Node
(); //构造节点内容 nodeFirst.Item = "one"; nodeSecond.Item = "two"; nodeThird.Item = "three"; //链接节点 nodeFirst.NextNode = nodeSecond; nodeSecond.NextNode = nodeThird; nodeSecond.PreviousNode = nodeFirst; nodeThird.PreviousNode = nodeSecond; //注:这里nodeThird的NextNode指向null var nodeEnumerator = nodeThird.GetNodeReverseEnumerator(); while (nodeEnumerator.MoveNext()) { //输出当前节点的内容 Console.Write($"当前节点元素内容:{nodeEnumerator.CurrentItem} "); //输出上一个节点的内容 Console.Write($"上一个节点元素内容:{nodeEnumerator.PreviousNode?.Item ?? "没有上一个节点"} "); //这里如果当前节点的下一个节点不为空,则让当前节点变为下一个节点 if (nodeEnumerator.SetNext()) Console.WriteLine($"下一个节点内容:{nodeEnumerator?.CurrentNode.Item}"); else Console.WriteLine($"链表遍历结束,下一个节点内容为null"); } Console.ReadKey(); } ///
/// 节点对象,使用迭代器模式,实现链表的遍历 /// ///
public class Node
: ILinkedListEnumerable
{ public T Item { get; set; } public Node
PreviousNode { get; set; } public Node
NextNode { get; set; } ///
/// 获取正向迭代器 /// ///
public ILinedListEnumerator
GetEnumerator() { return new NodeEnumerator
(this); } ///
/// 获取反向迭代器 /// ///
public ILinedListEnumerator
GetNodeReverseEnumerator() { return new NodeReverseEnumerator
(this); } } ///
/// 正向迭代器 /// ///
private class NodeEnumerator
: ILinedListEnumerator
{ public Node
PreviousNode { get; set; } public Node
CurrentNode { get; set; } public NodeEnumerator(Node
node) { CurrentNode = node; } public T CurrentItem => CurrentNode.Item; public bool MoveNext() { if (PreviousNode != null) return true; return false; } public bool SetNext() { if (CurrentNode.PreviousNode != null) { PreviousNode = CurrentNode; CurrentNode = CurrentNode.PreviousNode; return true; } else { CurrentNode = null; return false; } } ///
/// 当迭代器内部存在非托管资源时,用于释放资源 /// public void Dispose() { throw new NotImplementedException(); } } ///
/// 反向迭代器 /// private class NodeReverseEnumerator
: ILinedListEnumerator
{ public Node
PreviousNode { get; set; } public Node
CurrentNode { get; set; } public NodeReverseEnumerator(Node
node) { CurrentNode = node; } public T CurrentItem => CurrentNode.Item; public bool MoveNext() { if (CurrentNode != null) return true; return false; } public bool SetNext() { if (CurrentNode.PreviousNode != null) { PreviousNode = CurrentNode; CurrentNode = CurrentNode.PreviousNode; return true; } else { CurrentNode = null; return false; } } ///
/// 当迭代器内部存在非托管资源时,用于释放资源 /// public void Dispose() { throw new NotImplementedException(); } } ///
/// 链表迭代器接口约束 /// ///
public interface ILinkedListEnumerable
{ ///
/// 正向迭代器 /// ///
ILinedListEnumerator
GetEnumerator(); ///
/// 反向迭代器 /// ///
ILinedListEnumerator
GetNodeReverseEnumerator(); } ///
/// 链表单个迭代器 /// ///
public interface ILinedListEnumerator
: IDisposable { ///
/// 上一个迭代器元素 /// Node
PreviousNode { get; } ///
/// 当前迭代器元素 /// Node
CurrentNode { get; } ///
/// 当前迭代器元素内容 /// T CurrentItem { get; } ///
/// 是否可以进行下一步遍历操作 /// ///
bool MoveNext(); ///
/// 遍历完当前链表元素,使其指向下一个元素 /// bool SetNext(); } }

 

转载于:https://www.cnblogs.com/GreenLeaves/p/10261808.html

你可能感兴趣的文章
白话讲反射技术 --- 适合初学者入门引导
查看>>
css变形 transform
查看>>
win7家庭版添加组策略编辑器
查看>>
lnmp环境搭建
查看>>
自定义session扫描器精确控制session销毁时间--学习笔记
查看>>
【转】EDK简单使用流程(3)
查看>>
Ubuntu中无法update的解决办法
查看>>
仿射变换
查看>>
decltype类型指示符
查看>>
虹软ArcFace人脸识别 与 Dlib 人脸识别对比
查看>>
laravel 验证码使用示例
查看>>
IE开发人员工具无法使用
查看>>
分页器(自定制)
查看>>
HDU1877 又一版 A+B
查看>>
往sde中导入要素类报错000732
查看>>
springboot之启动方式
查看>>
初次安装git配置用户名和邮箱及密钥
查看>>
6.1(续)索引、索引组织表--Oracle模式对象
查看>>
动画 球
查看>>
C++中的堆,栈,静态内存区,常量区
查看>>