自己实现一个双向链表,性能跟C#自带的好一些
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace LKZ.DataStruct
{
/// <summary>
/// 双向链表
/// </summary>
[Serializable]
public class LinkedList2<T> : ICollection<T>, ICollection, IReadOnlyCollection<T>
, ISerializable, IDeserializationCallback, IEnumerable<T>, IEnumerable, IEnumerator, IEnumerator<T>
{
#region LinkedListNode 链表节点
/// <summary>
/// 链表节点
/// </summary>
public class LinkedListNode
{
/// <summary>
/// 上一个节点
/// </summary>
public LinkedListNode Pre { get; internal set; }
/// <summary>
/// 下一个
/// </summary>
public LinkedListNode Next { get; internal set; }
/// <summary>
/// 现在的值
/// </summary>
public T Value { get; internal set; }
internal LinkedListNode(T t)
{
Value = t;
}
internal LinkedListNode(T t, LinkedListNode pre, LinkedListNode next)
: this(t)
{
this.Pre = pre;
this.Next = next;
}
internal void Invalidate()
{
Value = default(T);
Next = null;
Pre = null;
}
}
#endregion
#region 构造
public LinkedList2() { }
/// <summary>
/// 构造
/// </summary>
/// <param name="ienumerable">添加到链表中的值</param>
public LinkedList2(IEnumerable<T> ienumerable)
{
foreach (T item in ienumerable)
{
this.AddLast(item);
}
}
/// <summary>
/// 反序列化调用这个
/// 会把两个参数传过来
/// </summary>
/// <param name="info">反序列化的参数在里面,从流中读取出阿里存储在这个类里</param>
/// <param name="context"></param>
protected LinkedList2(SerializationInfo info, StreamingContext context)
{
siInfo = info;
}
#endregion
/// <summary>
/// 有多少个
/// </summary>
public int Count { get; private set; }
public bool IsReadOnly => false;
/// <summary>
/// 这个笔者还不知道怎么用
/// </summary>
public object SyncRoot => throw new NotImplementedException();
public bool IsSynchronized => false;
/// <summary>
/// 头部
/// </summary>
public LinkedListNode Head { get; private set; }
/// <summary>
/// 尾部
/// </summary>
public LinkedListNode Last { get; private set; }
public T Current
{
get
{
T temp = positionNode.Value;
positionNode = positionNode.Next;
return temp;
}
}
object IEnumerator.Current => Current;
/// <summary>
/// 反序列化用的
/// 用来存储零时的这个反序列化的
/// </summary>
private SerializationInfo siInfo=null;
/// <summary>
/// 迭代器现在的节点位置
/// </summary>
private LinkedListNode positionNode = null;
/// <summary>
/// 在尾部添加一个元素
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public LinkedListNode AddLast(T t)
{
Count++;
if (Head == null)
{
return Last = Head = new LinkedListNode(t);
}
else
{
var newNode = new LinkedListNode(t, Last, null);
Last.Next = newNode;
return Last = newNode;
}
}
/// <summary>
/// 在头部添加一个元素
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public LinkedListNode AddFirst(T t)
{
Count++;
if (Head == null)
{
return Last = Head = new LinkedListNode(t);
}
else
{
var newNode = new LinkedListNode(t, null, Head);
Head.Pre = newNode;
return Head = newNode;
}
}
/// <summary>
/// 添加在这个节点之前
/// </summary>
/// <param name="linedListNode">在这个节点之前添加一个元素</param>
/// <param name="t">要添加的值</param>
public LinkedListNode AddBefore(LinkedListNode linedListNode, T t)
{
if (linedListNode == null)
ThrowError("linedListNode为空");
if (linedListNode == Head)
return this.AddFirst(t);
else
{
var newNode = new LinkedListNode(t, linedListNode.Pre, linedListNode);
linedListNode.Pre.Next = newNode;
linedListNode.Pre = newNode;
Count++;
return newNode;
}
/*
1,5,3,16,4
比如添加一个元素是,50
判断要添加在那个元素前面
linedListNode 添加在这个元素前面,判断这个元素是不是头部元素
不是头部元素就执行下面的 else代码
比如添加在16元素前面
创建一个新的元素,指定这个元素的前面是16的前面 3
后面是16的这个元素
然后设置3的后面的元素是50
设置16前面的元素是50就行了
*/
}
/// <summary>
/// 在这个元素之后添加一个元素
/// </summary>
/// <param name="linedListNode">在这个节点之前添加一个元素</param>
/// <param name="t">要添加的值</param>
/// <returns></returns>
public LinkedListNode AddAfter(LinkedListNode linedListNode, T t)
{
if (linedListNode == null)
ThrowError("linedListNode为空");
if (linedListNode == Last)
return this.AddLast(t);
else
{
var newNode = new LinkedListNode(t, linedListNode, linedListNode.Next);
linedListNode.Next.Pre = newNode;
linedListNode.Next = newNode;
Count++;
return newNode;
}
/*
1,5,3,16,4
比如添加一个元素是,50
判断要添加在那个元素后面
linedListNode 添加在这个元素前面,判断这个元素是不是尾部元素
不是头部元素就执行下面的 else代码
比如 添加在16元素后面
创建一个新的元素,指定这个元素的前面是16
后面是16的后面这个元素
然后设置16的后面的元素是50
设置4前面的元素是50就行了
*/
}
/// <summary>
/// 序列化调用了这个方法
/// </summary>
/// <param name="info">把序列化的数据储存这个这个类中</param>
/// <param name="context"></param>
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info为空");
}
info.AddValue(nameof(Count), Count);
if (Count != 0)
{
T[] array = new T[Count];
CopyTo(array, Count);
info.AddValue("data", array, array.GetType());
}
}
void ICollection<T>.Add(T item)
{
this.AddLast(item);
}
public void Clear()
{
LinkedListNode currentNode = Head;
while (currentNode!=null)
{
var temp= currentNode;
currentNode=currentNode.Next;
temp.Invalidate();
}
Count = 0;
Head = Last = null;
}
public bool Contains(T item)
{
return !object.ReferenceEquals(null, Find(item));
}
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
this.ThrowError("数组为空");
else if (arrayIndex < 0 || arrayIndex > array.Length)
this.ThrowError("arrayIndex不合法,超了范围");
else if (arrayIndex > Count)
this.ThrowError("arrayIndex不合法,超过了链表中的大小");
LinkedListNode currentNode = Head;
for (int i = 0; i < arrayIndex; i++)
{
array[i] = currentNode.Value;
currentNode = currentNode.Next;
}
}
public bool Remove(T item)
{
LinkedListNode linkedListNode = Find(item);
bool isNotNull = linkedListNode != null;
if (isNotNull)
{
linkedListNode.Next.Pre = linkedListNode.Pre;
linkedListNode.Pre.Next = linkedListNode.Next;
Count--;
}
return isNotNull;
}
public IEnumerator<T> GetEnumerator()
{
Reset();
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
Reset();
return this;
}
public void CopyTo(Array array, int index)
{
if (array == null)
this.ThrowError("数组为空");
else if (!(array is T[]))
this.ThrowError("数组和泛型类型不一致");
int length = array.Length - index;
if (length > Count)
length = Count;
CopyTo(array as T[], length);
}
void IDeserializationCallback.OnDeserialization(object sender)
{
if (siInfo.GetInt32(nameof(Count)) != 0)
{
T[] array = (T[])siInfo.GetValue("data", typeof(T[]));
for (int i = 0; i < array.Length; i++)
{
AddLast(array[i]);
}
}
siInfo = null;
}
public void Dispose()
{
Reset();
}
public bool MoveNext()
{
return positionNode != null;
}
public void Reset()
{
positionNode = Head;
}
/// <summary>
/// 抛出异常
/// </summary>
/// <param name="message"></param>
private void ThrowError(string message)
{
throw new ArgumentException(message);
}
private LinkedListNode Find(T item)
{
LinkedListNode currentLinkend = Head;
while (currentLinkend != null)
{
if (!object.Equals(currentLinkend.Value, item))
currentLinkend = currentLinkend.Next;
else
return currentLinkend;
}
return null;
}
}
}
- 有缺点
优点
这个解决了动态数组插入元素和删除元素,后面数据移位,性能消耗高的问题
这个链表插入元素和删除元素比动态数组性能好10倍
缺点
链表中都是无序的,如果在链表中找一个元素,这个性能是相当差的,因为要一个一个遍历查找
解决方法
使用二叉查找树
期待我下一章文章!
关注微信公众号【浪子独白】 获得更多精彩内容!