C#雙向鏈表的冒泡排序

/// <summary>

    /// 雙向鏈表節點類
    /// </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 ;
        }
    }

發佈了23 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章