/// 双向链表节点类
/// </summary>
/// <typeparam name="T">节点中的存放的数据类型</typeparam>
public class Node<T> where T:IComparable<T>
{
/// <summary>
/// 当前节点的数据
/// </summary>
T data;
/// <summary>
/// 节点中存放的数据
/// </summary>
public T Data
{
get { return this.data; }
set { this.data = value; }
}
/// <summary>
/// 当前节点的下一个节点
/// </summary>
Node<T> next;
/// <summary>
/// 下一个节点
/// </summary>
public Node<T> Next
{
get { return this.next; }
set { this.next = value; }
}
/// <summary>
/// 当前节点的上一个节点
/// </summary>
Node<T> prev;
/// <summary>
/// 上一个节点
/// </summary>
public Node<T> Prev
{
get { return prev; }
set { prev = value; }
}
/// <summary>
/// 无参构造:数据为默认值,下一个节点为null,上一个节点也为null
/// </summary>
public Node()
{
this.data = default(T);
this.next = null;
this.prev = null;
}
/// <summary>
/// 构造方法:数据为传过来的t,下一个节点为null,上一个节点也为null
/// </summary>
/// <param name="t">传入的元素值</param>
public Node(T t)
{
this.data = t;
this.next = null;
this.prev = null;
}
/// <summary>
/// 构造方法:数据为t,下一个节点为node
/// </summary>
/// <param name="t">传入的元素值</param>
/// <param name="next">上一个节点</param>
/// <param name="prev">下一个节点</param>
public Node(T t, Node<T> next, Node<T> prev)
{
this.data = t;
this.next = next;
this.prev = prev;
}
/// <summary>
/// 此方法在调试过程中使用,可以删掉
/// </summary>
/// <returns></returns>
public override string ToString()
{
T p = this.prev == null ? default(T) : this.prev.data;
T n = this.next == null ? default(T) : this.next.data;
string s = string.Format("Data:{0},Prev:{1},Next:{2}", data, p, n);
return s;
}
}
/// <summary>
/// 双向链表接口
/// </summary>
/// <typeparam name="T">链表中元素的类型</typeparam>
public interface ILinkList<T> where T:IComparable<T>
{
void AddFirst(T t);
void AddLast(T t);
void Clear();
int Count { get; }
Node<T> Head { get; set; }
Node<T> Tail { get;set;}
void Insert(int index, T t);
bool IsEmpty { get; }
void RemoveAt(int index);
void RemoveFirst();
void RemoveLast();
Node<T> this[int index] { get; }
}
/// <summary>
/// 双向链表操作类
/// </summary>
/// <typeparam name="T">链表中元素的类型</typeparam>
public class LinkList<T> : ILinkList<T> where T:IComparable<T>
{
/// <summary>
/// 链表头节点
/// </summary>
Node<T> head;
/// <summary>
/// 链表头节点
/// </summary>
public Node<T> Head
{
get { return head; }
set { head = value; }
}
/// <summary>
/// 链表尾节点
/// </summary>
Node<T> tail;
/// <summary>
/// 链表尾节点
/// </summary>
public Node<T> Tail
{
get { return tail; }
set { tail = value; }
}
/// <summary>
/// 链表大小
/// </summary>
int size = 0;
/// <summary>
/// 添加节点到链表的开头
/// </summary>
/// <param name="t">要添加的数据</param>
public void AddFirst(T t)
{
Node<T> node = new Node<T>(t);
//如果头为null
if (head == null)
{
//把头节点设置为node
head = node;
//因为是空链表,所以头尾一致
tail = node;
//大小加一
size++;
return;
}
//原来头节点的上一个为新节点
head.Prev = node;
//新节点的下一个为原来的头节点
node.Next = head;
//新头节点为新节点
head = node;
//大小加一
size++;
}
/// <summary>
/// 添加节点到链表的末尾
/// </summary>
/// <param name="t">要添加的数据</param>
public void AddLast(T t)
{
Node<T> node = new Node<T>(t);
//如果头为null
if (head == null)
{
//把头节点设置为node
head = node;
//因为是空链表,所以头尾一致
tail = node;
//大小加一
size++;
return;
}
//将原尾节点的下一个设置为新节点
tail.Next = node;
//将新节点的上一个设置为原尾节点
node.Prev = tail;
//将尾节点重新设置为新节点
tail = node;
//大小加一
size++;
}
/// <summary>
/// 在给定的索引处插入数据
/// </summary>
/// <param name="index">索引</param>
/// <param name="t">要插入的数据</param>
public void Insert(int index, T t)
{
Node<T> node = new Node<T>(t);
//索引过小
if (index < 0)
{
throw new IndexOutOfRangeException();
}
//索引过大
if (index >= Count)
{
throw new IndexOutOfRangeException();
}
//如果链表是空的,而且索引大于0
if (IsEmpty && index > 0)
{
throw new IndexOutOfRangeException();
}
//如果索引为0,意味着向链表头部添加节点。
if (index == 0)
{
AddFirst(t);
return;
}
//要插入位置的节点
Node<T> current = head;
int i = 0;
while (true)
{
if (i == index)
{
break;
}
i++;
current = current.Next;
}
//此处非常重要,特别要注意先后次序
//当前节点的上一个的下一个设置为新节点
current.Prev.Next = node;
//新节点的上一个设置为当前节点的上一个
node.Prev = current.Prev;
//新节点的下一个设置为当前节点
node.Next = current;
//当前节点的上一个设置为新节点
current.Prev = node;
//大小加一
size++;
}
/// <summary>
/// 移除链表中的节点
/// </summary>
/// <param name="index">要移除的节点的索引</param>
public void RemoveAt(int index)
{
//链表头节点是空的
if (IsEmpty)
{
throw new Exception("链表是空的。");
}
//索引过小
if (index < 0)
{
throw new IndexOutOfRangeException();
}
//索引过大
if (index >= Count)
{
throw new IndexOutOfRangeException();
}
//如果要移除的是头节点
if (index == 0)
{
RemoveFirst();
return;
}
if (index == size - 1)
{
RemoveLast();
return;
}
//要移除的节点
Node<T> current = head;
int i = 0;
while (true)
{
if (i == index)
{
break;
}
i++;
current = current.Next;
}
//当前节点的上一个的Next设置为当前节点的Next
current.Prev.Next = current.Next;
//当前节点的下一个的Prev设置为当前节点的Prev
current.Next.Prev = current.Prev;
//大小减一
size--;
}
/// <summary>
/// 移除头节点
/// </summary>
public void RemoveFirst()
{
//链表头节点是空的
if (IsEmpty)
{
throw new Exception("链表是空的。");
}
//如果size为1,那就是清空链表。
if (size == 1)
{
Clear();
return;
}
//将头节点设为原头结点的下一个节点,就是下一个节点上移
head = head.Next;
//处理上一步遗留问题,原来的第二个节点的上一个是头结点,现在第二个要变成头节点,那要把它的Prev设为null才能成为头节点
head.Prev = null;
//大小减一
size--;
}
/// <summary>
/// 移除尾节点
/// </summary>
public void RemoveLast()
{
//链表头节点是空的
if (IsEmpty)
{
throw new Exception("链表是空的。");
}
//如果size为1,那就是清空链表。
if (size == 1)
{
Clear();
return;
}
//尾节点设置为倒数第二个节点
tail = tail.Prev;
//将新尾节点的Next设为null,表示它是新的尾节点
tail.Next = null;
//大小减一
size--;
}
/// <summary>
/// 判断链表是否是空的
/// </summary>
public bool IsEmpty
{
get
{
return head == null;
}
}
/// <summary>
/// 链表中元素的个数
/// </summary>
public int Count
{
get
{
////也可以采用遍历的方法获得长度,遍历可以从前向后,也可以从后向前
//int count = 0;
////取得链表头部节点
//Node<T> current = new Node<T>();
//current = head;
////遍历整个链表,直到最后一个Next为null的节点为止
//while (current!=null)
//{
// count++;
// current = current.Next;
//}
//return count;
return size;
}
}
/// <summary>
/// 清除链表中的数据
/// </summary>
public void Clear()
{
head = null;
tail = null;
size = 0;
}
/// <summary>
/// 根据索引获取链表中的节点
/// </summary>
/// <param name="index">整型索引</param>
/// <returns>节点</returns>
public Node<T> this[int index]
{
get
{
//链表头节点是空的
if (head == null)
{
throw new Exception("链表是空的。");
}
//索引过小
if (index < 0)
{
throw new IndexOutOfRangeException();
}
//索引过大
if (index >= Count)
{
throw new IndexOutOfRangeException();
}
//取得头节点
Node<T> current = new Node<T>();
//current = head;
//int i = 0;
////遍历链表
//while (true)
//{
// //找到第index个节点
// if (i == index)
// {
// break;
// }
// current = current.Next;
// i++;
//}
//return current;
//如果索引在前一半,那么从前向后找
if (index < size / 2)
{
current = head;
int i = 0;
//遍历链表
while (true)
{
//找到第index个节点
if (i == index)
{
break;
}
current = current.Next;
i++;
}
return current;
}
else//如果索引在后一半,那么从后向前找
{
current = tail;
int i = size;
//遍历链表
while (true)
{
//找到第index个节点
if (i == index)
{
break;
}
current = current.Prev;
i--;
}
return current.Next;
}
}
}
/// <summary>
/// 归并排序,非递归版本
/// 步长为1的一轮过后,相邻的两个链表节点已经有序了
/// 步长为2的一轮过后,前两个和紧挨着的后两个就有序了。
/// 步长为4的一轮过后,前四个和紧挨着的后四个就有序了。
/// 跟数组的差别在于:对于数组来说,长度易得,将数组分成两部分容易,将已经分成两部分的再次拆分也容易。
/// 对于链表来说,长度难得,拆分更难得,因而,将链表划成两部分更难得。
/// 对于链表的处理用以下方式:
/// 首先将步长设为1,那么将链表两两分组,这两个节点中的前面一个作为第一个链表,后面的一个作为第二个链表。
/// 将这两个链表进行合并,合并完成的结果必然是个有序的链表,当步长为1的合并进行完成之后,链表中相邻的两个
/// 就排好序了,可是整体上可能还是无序的,那么将步长增加一倍,步长为2,继续进行合并,这个时候,第一个链表中
/// 就存放了两个已经排好序的节点,而第二个链表中也是两个已经排好序的节点,继续对这两个链表进行合并,合并的
/// 结果是4个一组的链表排序好了,这样一直进行下去,总该有个头吧,那是自然,头在何处呢?头就在合并次数这里;
/// 如果链表中有10个节点,那么第一次进行合并的时候,分成了5组,也就是进行了5次合并;第二次进行合并的时候,
/// 步长为2,分成了3组,合并进行了3次,第三次合并的时候,步长为4,合并进行2次,第四次合并的时候,步长为8
/// 合并只需进行1次就可以结束了。
///
/// 从单向链表的归并,到双向链表的归并非常简单,补上前向节点和尾节点就够了。
/// </summary>
public void MergeSort()
{
//如果是空链表或只有一个节点的链表
//那么是不需要排序的
if (IsEmpty || size == 1)
{
return;
}
int nstep = 1;//步长
//使劲循环
while (true)
{
Node<T> first;//第一个链表
Node<T> second;//第二个链表
Node<T> tail;//链表尾部
Node<T> temp;//临时节点
int firstSize, secondSize;//第一个链表和第二个链表的大小
//第一个链表从头开始
first = head;
//设置链表尾为null
tail = null;
int mergeCount = 0;//合并次数
//第一个链表不空
while (first != null)
{
//需要合并的数量加一
mergeCount++;
//把第一个链表给第二个链表
second = first;
//第一个链表的大小设置为0
firstSize = 0;
//根据步长得到第一个链表的长度
//下面这个循环还有个目的是确定第二个链表的起始位置
//firstSize是第一个链表的实际长度,而secondSize长度可能大于实际的长度
for (int i = 0; i < nstep; i++)
{
firstSize++;
second = second.Next;
if (second == null)
{
break;
}
}
//让第二个链表的长度为步长
secondSize = nstep;
//如果第一个链表的长度大于零,或者第二个链表的长度大于零并且第二个链表不为空
//这个循环是用来合并两个有序链表的
while (firstSize > 0 || (secondSize > 0 && second != null))
{
//下面这一大段if的意思是,从first或second中掐出来一个较小的节点
//放入temp这个临时节点中
//如果第一个链表的长度为0
if (firstSize == 0)
{
//将第二个链表中的第一个节点放入临时节点中
temp = second;
//将第二个链表下移一位,让第二个链表的第一个节点独立出来
second = second.Next;
//第二个链表的长度减一
secondSize--;
}//如果第二个链表为空或大小是零
else if (second == null || secondSize == 0)
{
//那么新链表就是第一个链表
temp = first;
//将第一个链表下移一位,让第一个链表的第一个节点独立出来
first = first.Next;
//第一个链表的长度减一
firstSize--;
}//到这里的时候,第一第二链表都不空
//如果第一个链表的第一个节点的数值小于第二个链表的第一个节点的数值
else if (first.Data.CompareTo(second.Data) <= 0)
{
//让新链表是第一个链表的第一个节点
temp = first;
//第一个链表下移一位,让第一个链表的第一个节点独立出来
first = first.Next;
//第一个链表的长度减一
firstSize--;
}
else//到这里的时候,第一第二链表都不空,而且第二个链表的第一个节点的数值小于第一个链表的第一个节点的数值
{
//让新链表是第二个链表
temp = second;
//第二个链表下移一位,让第二个链表的第一个节点独立出来
second = second.Next;
//第二个链表的长度减一
secondSize--;
}
//将得到的较小的哪个临时节点先放入tail这个临时链表中
//如果临时链表不是空的,意味着这个节点已经不是两个节点中最小的那个节点了
if (tail != null)
{
//那么将得到的临时节点附加在临时链表的最后
tail.Next = temp;
temp.Prev = tail;
//最后将临时节点作为临时链表
//这一句每轮可能会进行很多次
tail = temp;
}
else
{
//如果临时链表是空的
//每一轮会为head赋一次值
//每轮的第一次会将两个链表中最小的那个节点赋给head
//最后一轮的时候head一定是最小的哪个节点
head = temp;
head.Prev = null;
//最后将临时节点作为临时链表
tail = temp;
}
}
//将第一个链表设置为第二个链表
//到这里的时候second已经前进了一个步长了
//新的first链表就从second开始
first = second;
}
//走到这里的时候,以某个步长为步进的合并已经完成了
//下面的工作,要么结束,要么将步长设为原来的两倍
//临时链表的下一个设置为null,表示这是个尾节点
//每一轮结束的时候,tail中放置的是两个链表中最后一个节点
//因为这是最后一个节点,所以就没有下一个了,因而将Next设置为null
//如果不设置为null的话,那么链表有可能构成循环链表。
tail.Next = null;
this.tail = tail;
//如果要合并的节点数量小于等于1,就直接返回,表明排序完成了
if (mergeCount <= 1)
{
return;
}
else
{
//否则步长加倍,继续排序
nstep = nstep * 2;
}
}
}
}