/// <summary>
C#雙向鏈表的冒泡排序
/// 雙向鏈表節點類
/// </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>
/// 冒泡排序,不改變節點的位置,只交換節點的值
/// 和數組的冒泡排序最接近
/// 對於雙向鏈表來說,如果只交換數據,而不交換
/// 節點,那麼和單向鏈表的冒泡排序是一致的
/// </summary>
public void BubbleSort1()
{
if (IsEmpty || size == 1)
{
return;
}
//外層循環節點
Node<T> i;
//內層循環節點
Node<T> j;
i = head;
//循環不改變每個節點的先後次序,僅僅交換節點的值
while (i.Next != null)
{
j = head;
while (j.Next != null)
{
//比較前後兩個節點的數據
if (j.Data.CompareTo(j.Next.Data) > 0)
{
//交換兩個節點的數據
T temp = j.Data;
j.Data = j.Next.Data;
j.Next.Data = temp;
}
j = j.Next;
}
i = i.Next;
}
}
/// <summary>
/// 用交換節點的方法冒泡
/// 這種方法需要額外的的新節點
/// 對於雙向鏈表來說,稍微麻煩一點的是要處理尾節點
/// 所以在添加了輔助頭節點,還需要添加一個輔助尾節點
/// 當然最後也要把輔助尾節點給去掉。
/// </summary>
public void BubbleSort2()
{
//如果鏈表是空的或者鏈表中只有一個節點,那不需要排序
if (IsEmpty || size == 1)
{
return;
}
//臨時加的新節點
Node<T> newHead;
//外層循環變量
Node<T> i;
//外層循環變量每次改變的值
Node<T> loopValue;
//交換兩個節點的臨時變量
Node<T> temp;
//在鏈表的頭部加一個新的輔助接點
newHead = new Node<T>();
newHead.Next = head;
head.Prev = newHead;
head = newHead;
//在鏈表尾部加一個新的輔助接點
Node<T> newTail = new Node<T>();
tail.Next = newTail;
newTail.Prev = tail;
tail = newTail;
//外層循環從鏈表尾部開始,尾部從最後一個節點開始
//到達鏈表的頭部爲止
for (i = tail.Next; i != head; i = loopValue)
{
//開始讓p等於鏈表頭
//在裏層循環中更改p的位置
loopValue = head;
//裏層循環從head開始,到要交換的兩個節點的後一個是尾部位置,每次向後前進一個節點
//因爲一次循環就將一個最大的元素沉到最末尾了,所以下次不用到達末尾
//而第一次遍歷的時候第二個元素的下一個就是null
//j.Next.Next.Next如果是null
//那麼j.Next.Next就是我們剛纔添加的輔助尾節點
//那麼j.Next就是原始鏈表中的尾節點
//這個時候,就沒有必要比較了。
for (Node<T> j = head; j.Next.Next != i && j.Next.Next.Next!=null ; j = j.Next)
{
//j其實就是要交換的兩個節點的前一個節點
//比較前後兩個元素誰大誰小
if (j.Next.Data.CompareTo(j.Next.Next.Data) > 0)
{
//將要交換的節點和前後的兩個節點編號
//要交換的兩個是2,3
//前面的節點是1
//後面的節點是4
//要交換的兩個節點中的第二個
//temp中存放的是3
temp = j.Next.Next;
//將4接到2的後面
//如果第二個節點有下一個
//要是3有下一個(3後面還有節點,不是尾節點)
if (temp.Next != null)
//將要交換的第二個的下一個的Prev設置爲要交換的第一個
temp.Next.Prev = j.Next;
//將要交換的第一個的Next設置爲原先第二個節點的下一個
j.Next.Next = temp.Next;
//將2接到的3後面
//這裏j.Next還是原來要交換的第一個
//將要交換的第一個的Prev設置爲要交換的第二個
j.Next.Prev = temp;
//將要交換的第二個的下一個設置爲要交換的第一個
temp.Next = j.Next;
//將3接到1的後面
//將要交換的第二個的Prev設置爲要交換的兩個節點之前的那個
temp.Prev = j;
//將最前面的那個節點的Next設置爲要交換的第二個節點
j.Next = temp;
//讓loopValue指向交換過以後的第二個節點那裏
//精髓就在這一句,通過在裏層循環改變外層循環的條件
loopValue = j.Next.Next;
}
}
}
//最後去掉那個添加上了的輔助頭節點
head = head.Next;
//最後去掉那個添加上了的輔助尾節點
tail = tail.Prev ;
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.