.NET源码中的链表

来源:互联网 发布:java html标签转pdf 编辑:程序博客网 时间:2024/06/05 18:23

.NET中自带的链表是LinkedList类,并且已经直接实现成了双向循环链表。

其节点类LinkedListNode的数据结构如下,数据项包括指示到某个链表的引用,以及左,右节点和值。

  public sealed class LinkedListNode<T>  {    internal LinkedList<T> list;    internal LinkedListNode<T> next;    internal LinkedListNode<T> prev;    internal T item;  }

另外,获取前一个节点和后一个节点的实现如下:注意:里面的if-else结构的意义是当前一个(后一个)节点不为空且不是头节点时才不返回null,这样做的意义是当链表内只有1个节点时,其prev和next是指向自身的。

 [__DynamicallyInvokable]    public LinkedListNode<T> Next    {      [__DynamicallyInvokable] get      {        if (this.next != null && this.next != this.list.head)          return this.next;        else          return (LinkedListNode<T>) null;      }    }    [__DynamicallyInvokable]    public LinkedListNode<T> Previous    {      [__DynamicallyInvokable] get      {        if (this.prev != null && this != this.list.head)          return this.prev;        else          return (LinkedListNode<T>) null;      }    }

过有一个把链表置为无效的方法定义如下:

    internal void Invalidate()    {      this.list = (LinkedList<T>) null;      this.next = (LinkedListNode<T>) null;      this.prev = (LinkedListNode<T>) null;    }

而LinkedList的定义如下:主要的两个数据是头节点head以及长度count。

public class LinkedList<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable, ISerializable, IDeserializationCallback  {    internal LinkedListNode<T> head;    internal int count;    internal int version;    private object _syncRoot;    private SerializationInfo siInfo;    private const string VersionName = "Version";    private const string CountName = "Count";    private const string ValuesName = "Data";

而对此链表的主要操作,包括:

  • 插入节点到最后,Add(),也是AddLast()。
  • 在某个节点后插入,AddAfter(Node, T)。
  • 在某个节点前插入,AddBefore(Node, T)。
  • 插入到头节点之前,AddFirst(T)。
  • 清除所有节点,Clear()。
  • 是否包含某个值,Contains(T),也就是Find()。
  • 查找某个节点的引用,Find()和FindLast()。
  • 复制到数组,CopyTo(Array)
  • 删除某个节点,Remove(T)。
另外有几个内部方法,用来支撑复用插入和删除操作:
  • 内部插入节点,InternalInsertNodeBefore()
  • 内部插入节点到空链表,InternalInsertNodeToEmptyList()
  • 内部删除节点,InternalRemoveNode()
  • 验证新节点是否有效,ValidateNewNode()
  • 验证节点是否有效,ValidateNode()

插入新节点到链表最后的代码如下:
    public void AddLast(LinkedListNode<T> node)    {      this.ValidateNewNode(node);      if (this.head == null)        this.InternalInsertNodeToEmptyList(node);      else        this.InternalInsertNodeBefore(this.head, node);      node.list = this;    }
插入操作的第一步是验证节点是否有效,即节点不为null,且节点不属于其他链表。
 internal void ValidateNewNode(LinkedListNode<T> node)    {      if (node == null)        throw new ArgumentNullException("node");      if (node.list != null)        throw new InvalidOperationException(SR.GetString("LinkedListNodeIsAttached"));    }

如果头节点为空,则执行插入到空链表的操作:将节点的next和prev都指向为自己,并作为头节点。
 private void InternalInsertNodeToEmptyList(LinkedListNode<T> newNode)    {      newNode.next = newNode;      newNode.prev = newNode;      this.head = newNode;      ++this.version;      ++this.count;    }
如果头节点不为空,则执行插入到头节点之前(注:因为是双向链表,所以插到头节点之前就相当于插到链表的最后了),具体的指针指向操作如下:
    private void InternalInsertNodeBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)    {      newNode.next = node;      newNode.prev = node.prev;      node.prev.next = newNode;      node.prev = newNode;      ++this.version;      ++this.count;    }

而插入新节点到指定节点之后的操作如下:同样还是调用的内部函数,把新节点插入到指定节点的下一个节点的之前。有点绕,但确实让这个内部函数起到多个作用了。
    public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)    {      this.ValidateNode(node);      this.ValidateNewNode(newNode);      this.InternalInsertNodeBefore(node.next, newNode);      newNode.list = this;    }

而插入新节点到指定节点之前的操作如下:直接调用插入新节点的内部函数,另外还要判断指定的节点是否是头节点,如果是的话,要把头节点变成新的节点。
public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)    {      this.ValidateNode(node);      this.ValidateNewNode(newNode);      this.InternalInsertNodeBefore(node, newNode);      newNode.list = this;      if (node != this.head)        return;      this.head = newNode;    }

把新链表插入到第一个节点(也就是变成头节点)的操作如下:如果链表为空就直接变成头节点,否则就插入到头节点之前,取代头节点。
public void AddFirst(LinkedListNode<T> node)    {      this.ValidateNewNode(node);      if (this.head == null)      {        this.InternalInsertNodeToEmptyList(node);      }      else      {        this.InternalInsertNodeBefore(this.head, node);        this.head = node;      }      node.list = this;    }

查找链表中某个值的操作如下:注意直接返回null的条件是头节点为空。然后就是遍历了,因为是双向链表,所以要避免死循环(遍历到头节点时跳出)。
public LinkedListNode<T> Find(T value)    {      LinkedListNode<T> linkedListNode = this.head;      EqualityComparer<T> @default = EqualityComparer<T>.Default;      if (linkedListNode != null)      {        if ((object) value != null)        {          while (!@default.Equals(linkedListNode.item, value))          {            linkedListNode = linkedListNode.next;            if (linkedListNode == this.head)              goto label_8;          }          return linkedListNode;        }        else        {          while ((object) linkedListNode.item != null)          {            linkedListNode = linkedListNode.next;            if (linkedListNode == this.head)              goto label_8;          }          return linkedListNode;        }      }label_8:      return (LinkedListNode<T>) null;    }

删除某个节点的操作如下:
    public void Remove(LinkedListNode<T> node)    {      this.ValidateNode(node);      this.InternalRemoveNode(node);    }

同样,内部删除节点的实现如下:如果节点指向自己,说明是头节点,所以直接把头节点置null。然后就是指针的指向操作了。
internal void InternalRemoveNode(LinkedListNode<T> node)    {      if (node.next == node)      {        this.head = (LinkedListNode<T>) null;      }      else      {        node.next.prev = node.prev;        node.prev.next = node.next;        if (this.head == node)          this.head = node.next;      }      node.Invalidate();      --this.count;      ++this.version;    }

而清空链表的操作如下:遍历链表,逐个设置为无效,最后将内部的头节点也置为null。
    public void Clear()    {      LinkedListNode<T> linkedListNode1 = this.head;      while (linkedListNode1 != null)      {        LinkedListNode<T> linkedListNode2 = linkedListNode1;        linkedListNode1 = linkedListNode1.Next;        linkedListNode2.Invalidate();      }      this.head = (LinkedListNode<T>) null;      this.count = 0;      ++this.version;    }


链表转数组的实现如下:首先判断入参的有效性,然后从头节点开始遍历,依次复制到数组中,直到头结点(尽头)。
 public void CopyTo(T[] array, int index)    {      if (array == null)        throw new ArgumentNullException("array");      if (index < 0 || index > array.Length)      {        throw new ArgumentOutOfRangeException("index", SR.GetString("IndexOutOfRange", new object[1]        {          (object) index        }));      }      else      {        if (array.Length - index < this.Count)          throw new ArgumentException(SR.GetString("Arg_InsufficientSpace"));        LinkedListNode<T> linkedListNode = this.head;        if (linkedListNode == null)          return;        do        {          array[index++] = linkedListNode.item;          linkedListNode = linkedListNode.next;        }        while (linkedListNode != this.head);      }    }

以上。


0 0
原创粉丝点击