转载——用C#学数据结构(6)
链表(续)
用C#实现循环链表
可以将循环链表看作是双链表的一种特例,即链表尾部节点的Next属性不是null,而是指向了头结点,可以利用现有的LinkedListNode类作为循环链表的节点。
下面将提供一个完整的循环链表类CircleLinkedList,该类和LinkedList类似,都是构建于LilnkedListNode之上,而且内部使用一个LinkedList维护循环链表,该循环链表具有泛型特性,可用于多种场合。
//本类由Castor原创,转载注明出处
public class CircleLinkedList<T>//需要System和System.Collections.Generic命名空间的支持
{
//内部实现链表
private LinkedList<T> ll;
//当前节点
private LinkedListNode<T> current;
//头结点,注意头结点不参与数据存储
private LinkedListNode<T> head;
/// <summary>
/// 循环链表构造函数
/// </summary>
public CircleLinkedList()
{
ll = new LinkedList<T>();
T value = default(T);
head = new LinkedListNode<T>(value);
ll.AddFirst(head);
current = head;
}
/// <summary>
/// 遍历器
/// </summary>
/// <returns>返回除头结点外的所有节点,提供foreach支持</returns>
public IEnumerator<LinkedListNode<T>> GetEnumerator()
{
LinkedListNode<T> p = ll.First;
p = p.Next;
while (p != null)
{
yield return p;
p = p.Next;
}
}
#region 属性
/// <summary>
/// 只读属性,返回循环链表中的节点数目,不包括Head结点
/// </summary>
public int Count
{
//不包括Head节点
get { return ll.Count - 1; }
}
/// <summary>
/// 只读属性,当前节点
/// </summary>
public LinkedListNode<T> Current
{
get { return current; }
}
/// <summary>
/// 只读属性,头结点
/// </summary>
public LinkedListNode<T> Head
{
get { return head; }
}
/// <summary>
/// 只读属性,返回循环链表第一个节点
/// </summary>
public LinkedListNode<T> First
{
get
{
if (ll.First.Next == null)
throw new IndexOutOfRangeException("循环链表为空,无法获取第一个节点");
else
return ll.First.Next;
}
}
/// <summary>
/// 只读属性,返回循环链表最后一个节点
/// </summary>
public LinkedListNode<T> Last
{
get
{
if (ll.First.Next == null)
throw new IndexOutOfRangeException("循环链表为空,无法获取最后一个节点");
else
return ll.Last;
}
}
#endregion
#region 添加节点方法
/// <summary>
/// 将一个节点或值插入到循环链表的最前面
/// 注意仍在Head节点后面
/// </summary>
/// <param name="node">待插入节点</param>
public void AddFirst(LinkedListNode<T> node)
{
ll.AddAfter(head, node.Value);
}
/// <summary>
/// 将一个节点或值插入到循环链表的最前面
/// 注意仍在Head节点后面
/// </summary>
/// <param name="value">待插入值</param>
public void AddFirst(T value)
{
ll.AddAfter(head, value);
}
/// <summary>
/// 将一个节点或值插到循环链表最后面
/// </summary>
/// <param name="node">待插入节点</param>
public void AddLast(LinkedListNode<T> node)
{
ll.AddLast(node);
}
/// <summary>
/// 将一个节点或值插到循环链表最后面
/// </summary>
/// <param name="value">待插入值</param>
public void AddLast(T value)
{
ll.AddLast(value);
}
/// <summary>
/// 将一个值或者节点插入到指定节点后面
/// </summary>
/// <param name="node">节点</param>
/// <param name="newNode">待插入节点</param>
public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
ll.AddAfter(node, newNode);
}
/// <summary>
/// 将一个值或者节点插入到指定节点后面
/// </summary>
/// <param name="node">节点</param>
/// <param name="value">待插入值</param>
public void AddAfter(LinkedListNode<T> node, T value)
{
ll.AddAfter(node, value);
}
/// <summary>
/// 将一个节点或者值插入到指定节点前面
/// 注意该节点不能是头结点Head,但可以是First
/// </summary>
/// <param name="node">节点</param>
/// <param name="newNode">新节点</param>
public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
if (node == head)
throw (new IndexOutOfRangeException("前插参数错误,你不可能把一个节点或值插入到Head节点前面"));
ll.AddBefore(node, newNode);
}
/// <summary>
/// 将一个节点或者值插入到指定节点前面
/// 注意该节点不能是头结点Head,但可以是First
/// </summary>
/// <param name="node">节点</param>
/// <param name="value">值</param>
public void AddBefore(LinkedListNode<T> node, T value)
{
if (node == head)
throw (new IndexOutOfRangeException("前插参数错误,你不可能把一个节点或值插入到Head节点前面"));
ll.AddBefore(node, value);
}
#endregion
#region 移动节点方法
/// <summary>
/// 将当前节点向后循环移动一位,跳过head节点
/// </summary>
public void MoveNext()
{
if (ll.First.Next == null)
throw new IndexOutOfRangeException("循环链表为空,无法移动当前节点");
if (current == ll.Last)
current = ll.First.Next;
else
current = current.Next;
}
/// <summary>
/// 将当前节点向前移动一位,跳过head节点
/// </summary>
public void MovePrevious()
{
if (ll.First.Next == null)
throw new IndexOutOfRangeException("循环链表为空,无法移动当前节点");
if (current.Previous == ll.First)//要跳过head节点
current = ll.Last;
else
current = current.Previous;
}
/// <summary>
/// 将当前节点移动到最后一个节点,如果为空,则直接为head节点
/// </summary>
public void MoveLast()
{
current = ll.Last;
}
/// <summary>
/// 将当前节点移动到第一节点
/// </summary>
public void MoveFirst()
{
if (ll.First.Next == null)
throw new IndexOutOfRangeException("循环链表为空,无法移动到第一个节点");
current = ll.First.Next;
}
/// <summary>
/// 将当前节点移动到头结点
/// </summary>
public void MoveHead()
{
current = ll.First;
}
#endregion
#region 移除节点方法
/// <summary>
/// 移出第一节点,Current变为下一个节点,但是如果第一节点也是最后一个节点,Current将变为head节点
/// </summary>
public void RemoveFirst()
{
if (ll.First.Next == null)//head后已经没有节点了
throw new IndexOutOfRangeException("循环链表为空,无法移出第一个节点");
else//head后还有节点
{
if (ll.Count == 2)//第一节点也是最后一个节点
{
ll.RemoveLast();
current = head;
}
else
{
LinkedListNode<T> temp = current;
current = current.Next;
ll.Remove(temp);
}
}
}
/// <summary>
/// 移出末尾节点,如果当前节点恰好为末尾节点,当前节点将变为第一节点
/// </summary>
public void RemoveLast()
{
if (ll.First.Next == null)//head后已经没有节点了
throw new IndexOutOfRangeException("循环链表为空,无法移出最后一个节点");
else
{
current = ll.First.Next;
ll.RemoveLast();
}
}
/// <summary>
/// 移出当前节点,循环链表中当前节点顺移到下一位
/// 如果Current已经是head节点,将不做任何操作
/// 如果当前节点恰好为尾节点,当前节点将变为第一节点
/// 如果当前节点是循环链表中唯一可用节点(既是第一节点又是尾节点),则移出后Current变为head节点
/// </summary>
public void RemoveCurrent()
{
if (current == head) return;//为首节点不移出
if (ll.First.Next == null)
throw new IndexOutOfRangeException("循环链表为空,无法移出当前节点");
LinkedListNode<T> temp = current;
//移动当前节点
if (current.Next == null)//为尾节点
{
if (current == ll.First.Next)//又恰好为第一节点
current = head;
else//一般尾节点
current = ll.First.Next;
}
else
current = current.Next;
ll.Remove(temp);
}
#endregion
}
用该类解决约瑟夫环问题将会更简单,因为其本身就有维护循环链表的功能:
public static LinkedList<int> Josephus(int n, int m, int k)
{
LinkedList<int> J = new LinkedList<int>();
CircleLinkedList<int> ring = new CircleLinkedList<int>();
for (int i = 1; i <= n; i++)
ring.AddLast(i);
ring.MoveFirst();
for (int i = 1; i < k; i++)
ring.MoveNext();
while (ring.Count > 0)
{
for (int i = 1; i < m; i++)
ring.MoveNext();
J.AddLast(ring.Current.Value);
ring.RemoveCurrent();
}
return J;
}
该方法将会返回一个链表,链表中的值就是出列顺序
浙公网安备 33010602011771号