1. 程式人生 > >資料結構與演算法之十 提高二叉搜尋樹的效率

資料結構與演算法之十 提高二叉搜尋樹的效率

/*寫一個程式以實現插入、刪除且遍歷線索二叉搜尋樹,這裡樹中的每個節點包含一個字典程式。*/
using System;
using System.Text;

namespace Threads
{
    class Node
    {
        /*兩個線索域;lthread,rthread;1:表示子節點;0:表示線索.*/
        public int lthread; /*左線索標誌*/
        public Node lchild; /*左子節點/
        public string info; /*資料域*/
        public Node rchild; /*右子節點*/
        public int rthread; /*右線索標誌*/

        public Node(int lt, Node lc, string i, Node rc, int rt)
        {
            lthread = lt;
            lchild = lc;
            info = i;
            rchild = rc;
            rthread = rt;
        }
    }
    class Operations
    {
        Node head;
        public Operations()
        {
         /*在一個線索二叉樹種,我們增加一個節點,即頭節點.線索樹作為頭節點的左子樹,即頭點指向樹的根節點.當樹為空的時候,頭節點左子節點指向本身*/
         
            head = new Node(0, head, "頭節點", head, 0);
            head.lchild = head;
            head.rchild = head;
        }//建構函式初始化的時候,頭節點的左,右子節點指向本身.

        public void find(string element, ref Node parent, ref Node currentNode)
        {
            /*搜尋方法,查詢你要找的節點位置與之父節點的位置.*/
            if (head.lchild == head)
            {
                /*如果沒有找到節點為null,且父節點為頭節點*/
                currentNode = null;
                parent = head;
                return;
            }
            currentNode = head.lchild;
            parent = head;

            while (currentNode.info != element)
            {
                parent = currentNode;
                if (String.Compare(element,currentNode.info)<0)		//如果元素小於當前節點
                {
                    if (currentNode.lthread == 1)		//判斷當前節點的左線索標誌,如果為1,則指向當前節點的左子節點.
                        currentNode = currentNode.lchild;
                    else	//否則,如果左線索標誌為0,則設定當前節點為空.
                    {
                        currentNode = null;
                        return;
                    }
                }
                else
                {
                    if (currentNode.rthread == 1)		//如果當前節點的右線索標誌為1,則指向當前節點的右子節點.
                        currentNode = currentNode.rchild;
                    else	//否則,右線索標誌為0,則設定當前節點為空
                    {
                        currentNode = null;
                        return;
                    }
                }
            }
        }

        public void insert(string element) 			/*在二叉樹中插入一個節點.*/
        {
            Node tmp, parent = null, currentNode = null;	//
            find(element, ref parent, ref currentNode);	//呼叫查詢當前元素節點,當前元素父節點.
            if (currentNode != null)
            {
                /*在二叉搜尋樹中不允許,重複節點.*/
                Console.WriteLine("\n不允許重複單詞.");
                return;
            }
            tmp = new Node(0, null, element, null, 0);	//為tmp新節點分配記憶體.
            if (parent == head) 	/*如果父節點為頭節點,則插入節點為根節點.*/
            {
                head.lthread = 1; 		/*設定頭節點的左線索標誌為1*/
                head.lchild = tmp; 		/*設定頭節點的左子節點為要新節點.*/
                tmp.lchild = head; 		/*新節點的左線索為頭節點.*/
                tmp.rchild = head;		/*新節點的右線索為頭節點.*/
            }
            else
            {
                if (String.Compare(element,parent.info)<0)
                {
                    /*要插入的新節點比父節點小*/

                    tmp.lchild = parent.lchild;
                    tmp.rchild = parent;
                    parent.lthread = 1;
                    parent.lchild = tmp;
                }
                else
                {
                    /*要插入的新節點比父節點要大!*/

                    tmp.rchild = parent.rchild;
                    tmp.lchild = parent;
                    parent.rthread = 1;
                    parent.rchild = tmp;
                }
            }
        }

        public Node Inorder_successor(Node currentNode)		//中序編歷查詢後繼節點
        {
            /*中序:左子樹< 根<右子樹 */
            Node successor;
            if (currentNode.rthread == 0)
                successor = currentNode.rchild;
            else
            {
                currentNode = currentNode.rchild;
                while (currentNode.lthread == 1)
                {
                    currentNode = currentNode.lchild;
                }
                successor = currentNode;
            }
            return successor;
        }

        public Node Inorder_predecessor(Node currentNode) /*利用中序編歷查詢前驅節點.*/
        {            
            Node predecessor;
            if (currentNode.lthread == 0)
                predecessor = currentNode.lchild;
            else
            {
                currentNode = currentNode.lchild;
                while (currentNode.rthread == 1)
                {
                    currentNode = currentNode.rchild;
                }
                predecessor = currentNode;
            }
            return predecessor;
        }

        public void Inorder_traversal() 		/*執行樹的中序編歷*/
        {
            Node currentNode = null;
            if (head.lchild == head)
            {
                Console.WriteLine("樹空!");
                return;
            }
            currentNode = head.lchild;
            while (currentNode.lthread == 1)
            {
                currentNode = currentNode.lchild;
            }
            Console.Write(currentNode.info + "   ");
            while (true)
            {
                currentNode = Inorder_successor(currentNode);
                if (currentNode == head)
                    break;
                Console.Write(currentNode.info + "   ");
            }
            Console.WriteLine();
        }

        public void remove() 			/*從樹種移除節點*/
        {
            if (head.lchild == head)
            {
                Console.WriteLine("樹空");
                return;
            }
            Node parent = null, currentNode = null;
            string element;
            Console.Write("請鍵入要刪除單詞:");
            element = Console.ReadLine();
            find(element, ref parent, ref currentNode);
            if (currentNode == null)
            {
                Console.WriteLine("\n在字典中沒有發現該單詞");
                return;
            }
            /*依據不同的狀態,來刪除不同的子節點.*/
            if (currentNode.lthread == 0 && currentNode.rthread == 0)
                case_1(ref parent, ref currentNode);
            if (currentNode.lthread == 1 && currentNode.rthread == 0)
                case_2(ref parent, ref currentNode);
            if (currentNode.lthread == 0 && currentNode.rthread == 1)
                case_2(ref parent, ref currentNode);
            if (currentNode.lthread == 1 && currentNode.rthread == 1)
                case_3(ref parent, ref currentNode);
        }

        public void case_1(ref Node parent, ref Node currentNode)
        {
            /* This function is invoked if the node to be removed is the leaf node */
            if (parent == head)
            {
                head.lthread = 0;
                head.lchild = head;
            }
            else
                if (currentNode == parent.lchild)
                {
                    parent.lthread = 0;
                    parent.lchild = currentNode.lchild;
                }
                else
                {
                    parent.rthread = 0;
                    parent.rchild = currentNode.rchild;
                }
        }

        public void case_2(ref Node parent, ref Node currentNode)
        {
            /* This function is invoked if the node to be removed has only one child (left or right) */
            Node child, successor, predecessor;
            if (currentNode.lthread == 1)
                child = currentNode.lchild;
            else
                child = currentNode.rchild;
            if (parent == head)
                head.lchild = child;
            else
                if (currentNode == parent.lchild)
                    parent.lchild = child;
                else
                    parent.rchild = child;
            successor = Inorder_successor(currentNode);
            predecessor = Inorder_predecessor(currentNode);

            if (currentNode.rthread == 1)
                successor.lchild = predecessor;
            else
            {
                if (currentNode.lthread == 1)
                    predecessor.rchild = successor;
            }
        }
        public void case_3(ref Node parent, ref Node currentNode)
        {
            /* This function is invoked if the node to be removed has two children */
            Node inorder_suc, inorder_parent;
            inorder_parent = currentNode;
            inorder_suc = currentNode.rchild;
            while (inorder_suc.lthread == 1)
            {
                inorder_parent = inorder_suc;
                inorder_suc = inorder_suc.lchild;
            }

            currentNode.info = inorder_suc.info;
            if (inorder_suc.lthread == 0 && inorder_suc.rthread == 0)
                case_1(ref inorder_parent, ref inorder_suc);
            else
                case_2(ref inorder_parent, ref inorder_suc);
        }


        static void Main(string[] args)
        {
            Operations t = new Operations();
            while (true)
            {
                try
                {
                    Console.WriteLine("\n選單");
                    Console.WriteLine("1. 插入操作");
                    Console.WriteLine("2.刪除操作");
                    Console.WriteLine("3.中序編歷操作");
                    Console.WriteLine("4. 退出");
                    Console.Write("\n請輸入您的選擇(1-4): ");
                    char ch = Convert.ToChar(Console.ReadLine());
                    Console.WriteLine();
                    switch (ch)
                    {
                        case '1':
                            {
                                Console.Write("請輸入單詞: ");
                                string word = Console.ReadLine();
                                t.insert(word);
                            }
                            break;
                        case '2':
                            {
                                t.remove();
                            }
                            break;
                        case '3':
                            {
                                t.Inorder_traversal();
                            }
                            break;
                        case '4':
                            return;
                        default:
                            {
                                Console.WriteLine("無效選擇");
                            }
                            break;
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("請檢查您的值.");
                }
            }
        }
    }
}