資料結構之搜尋演算法二:二叉搜尋樹

來源:互聯網
上載者:User

  最近複習下資料結構,用C#實現了下二叉搜尋樹,後面再繼續實現平衡樹和紅/黑樹狀結構,以下是二叉搜尋樹(又稱二叉尋找樹)的定義和性質: 

二叉尋找樹(Binary Search Tree),或者是一棵空樹,或者是具有下列性質的二叉樹:

  若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

  若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

  它的左、右子樹也分別為二叉排序樹。

  二叉排序樹的尋找過程和次優二叉樹類似,通常採取二叉鏈表作為二叉排序樹的儲存結構。中序遍曆二叉排序樹可得到一個關鍵字的有序序列,一個無序序列可以通過構造一棵二叉排序樹變成一個有序序列,構造樹的過程即為對無序序列進行排序的過程。每次插入的新的結點都是二叉排序樹上新的葉子結點,在進行插入操作時,不必移動其它結點,只需改動某個結點的指標,由空變為非空即可。搜尋,插入,刪除的複雜度等於樹高,O(log(n)).

  

  二叉排序樹的尋找演算法:

  在二叉排序樹b中尋找x的過程為:

  若b是空樹,則搜尋失敗,否則:

  若x等於b的根結點的資料域之值,則尋找成功;否則:

  若x小於b的根結點的資料域之值,則搜尋左子樹;否則:尋找右子樹。

 

  向一個二叉排序樹b中插入一個結點s的演算法:

  過程為:

  若b是空樹,則將s所指結點作為根結點插入,否則:

  若s->data等於b的根結點的資料域之值,則返回,否則:

  若s->data小於b的根結點的資料域之值,則把s所指結點插入到左子樹中,否則:

  把s所指結點插入到右子樹中。

 

  在二叉排序樹刪除結點的演算法:

 

  在二叉排序樹刪去一個結點,分三種情況討論:

  若*x結點為葉子結點,即xL(左子樹)和xR(右子樹)均為空白樹。由於刪去葉子結點不破壞整棵樹的結構,則只需修改其雙親結點的指標即可。

  若*x結點只有左子樹xL或右子樹xR,此時只要令xL或xR直接成為其雙親結點*parent的左子樹或者右子樹即可,作此修改也不破壞二叉排序樹的特性。

  若*x結點的左子樹和右子樹均不空。在刪去*x之後,為保持其它元素之間的相對位置不變,可按中序遍曆保持有序進行調整,可以有兩種做法:其一是令*x的左子樹為*parent的左子樹,*xsucc為*f左子樹的最右下的結點,而*x的右子樹為*xsucc的右子樹;其二是令*x的直接前驅(或直接後繼)替代*x,然後再從二叉排序樹中刪去它的直接前驅(或直接後繼)。

  中序後繼結點替換要刪除的節點:從x的右兒子開始,一直靠左往下走,最後到達的節點就是所需的後繼結點,程式中用xsucc指向這個後繼結點,現在只需刪除xsucc指向的節點,可以根據情況1或者是情況2中的方法來刪除它。

 

代碼

 public class BinaryNode<T>
    {
        private T data;
        private BinaryNode<T> leftChild;
        private BinaryNode<T> rightChild;

        public T Data
        {
            get
            {
                return data;
            }
            set
            {
                data = value;
            }
        }

        public BinaryNode<T> LeftChild
        {
            get
            {
                return leftChild;
            }
            set
            {
                leftChild = value;
            }
        }

        public BinaryNode<T> RightChild
        {
            get
            {
                return rightChild;
            }
            set
            {
                rightChild = value;
            }
        }
    }

public class BinaryTree<T>
    {
        private BinaryNode<T> root;       
       
        public BinaryNode<T> Root
        {
            get
            {
                return root;
            }
            set
            {
                root = value;
            }
        }

        /// <summary>
        /// 先序遍曆
        /// </summary>
        /// <param name="root"></param>
        public void FirstVisit(BinaryNode<T> root)
        {
            if (root==null)
            {
                return;
            }

            //訪問當前節點資料
            Console.WriteLine("此節點資料為:{0}", root.Data);

            //遍曆左子樹
            FirstVisit(root.LeftChild);

            //遍曆右子樹
            FirstVisit(root.RightChild);
        }

        /// <summary>
        /// 中序遍曆
        /// </summary>
        /// <param name="root"></param>
        public void MidVisit(BinaryNode<T> root)
        {
            if (root == null)
            {
                return;
            }

            //遍曆左子樹
            MidVisit(root.LeftChild);

            //訪問當前節點資料
            Console.WriteLine("此節點資料為:{0}", root.Data);
           
            //遍曆右子樹
            MidVisit(root.RightChild);
        }

        /// <summary>
        /// 後序遍曆
        /// </summary>
        /// <param name="root"></param>
        public void AfterVisit(BinaryNode<T> root)
        {
            if (root == null)
            {
                return;
            }

            //遍曆右子樹
            AfterVisit(root.RightChild);          

            //遍曆左子樹
            AfterVisit(root.LeftChild);

            //訪問當前節點資料
            Console.WriteLine("此節點資料為:{0}", root.Data);
        }

        /// <summary>
        /// 逐層遍曆
        /// </summary>
        /// <param name="root"></param>

        public void LevelVisit(BinaryNode<T> root)
        {
            if (root == null)
            {
                return;
            }
            //用一個隊列來儲存節點
            Queue<BinaryNode<T>> queue = new Queue<BinaryNode<T>>();

            //根節點入隊
            queue.Enqueue(root);

            while (queue.Count>0)
            {
                BinaryNode<T> node = queue.Dequeue();

                //訪問當前節點資料
                Console.WriteLine("此節點資料為:{0}", root.Data);

                if (node.LeftChild!=null)
                {
                    queue.Enqueue(node);
                }

                if (node.RightChild != null)
                {
                    queue.Enqueue(node);
                }
            }
        }
    }

 public class BinarySearchTree 
    {
        public bool SearCh(BinaryTree<int> bt, int key)
        {
            BinaryNode<int> p;
            if (bt.Root == null)
            {
                //Console.WriteLine("the binaryTree is empty!");
                return false;
            }
            p = bt.Root;
            while (p != null)
            {
                if (p.Data == key)
                {
                    //Console.WriteLine("search succeed!");
                    return true;
                }
                else if (key < p.Data)
                {
                    p = p.LeftChild;
                }
                else
                {
                    p = p.RightChild;
                }
            }

            return false;
        }

        /************************************************************************/
        /* 向一個二叉排序樹b中插入一個結點s的演算法,過程為:
            1.若b是空樹,則將s所指結點作為根結點插入,否則:
            2.若s->data等於b的根結點的資料域之值,則返回,否則:
            3.若s->data小於b的根結點的資料域之值,則把s所指結點插入到左子樹中,否則:
            4.把s所指結點插入到右子樹中。 
         * 注意:對二叉尋找樹插入一個節點時總是插入到分葉節點,即此時新增的節點一定是分葉節點
        /************************************************************************/
        public bool Insert(BinaryTree<int> bt, int key)
        {
            BinaryNode<int> p;
            if (bt.Root == null)
            {
                bt.Root = new BinaryNode<int>();
                bt.Root.Data = key;
                return true;
            }
            p = bt.Root;
            BinaryNode<int> parent = new BinaryNode<int>(); //用於保留插入時找到的父節點
            while (p != null)
            {
                if (p.Data == key)
                {
                    return false;
                }
                else if (key < p.Data)
                {
                    parent = p;
                    p = p.LeftChild;
                }
                else
                {
                    parent = p;
                    p = p.RightChild;
                }
            }
            p = new BinaryNode<int>();
            p.Data = key;
            if (key < parent.Data)
            {
                parent.LeftChild = p;
            }
            else
            {
                parent.RightChild = p;
            }
            return true;
        }

        /************************************************************************/
        /* 在二叉排序樹刪去一個結點,分三種情況討論:

            1.若*p結點為葉子結點,即PL(左子樹)和PR(右子樹)均為空白樹。
         * 由於刪去葉子結點不破壞整棵樹的結構,則只需修改其雙親結點的指標即可。
            2.若*p結點只有左子樹PL或右子樹PR,
         * 此時只要令PL或PR直接成為其雙親結點*f的左子樹即可,
         * 作此修改也不破壞二叉排序樹的特性。
            3.若*p結點的左子樹和右子樹均不空。在刪去*p之後,
         * 為保持其它元素之間的相對位置不變,可按中序遍曆保持有序進行調整,
         * 可以有兩種做法:其一是令*p的左子樹為*f的左子樹,
         * *s為*f左子樹的最右下的結點,而*p的右子樹為*s的右子樹;
         * 其二是令*p的直接前驅(或直接後繼)替代*p,
         * 然後再從二叉排序樹中刪去它的直接前驅(或直接後繼)。                                                                     */
        /************************************************************************/

        //採取右子樹填充法
        public bool Delete(BinaryTree<int> bt, int key)
        {
            BinaryNode<int> p = bt.Root;

            BinaryNode<int> parent = new BinaryNode<int>(); //用於保留刪除時找到的父節點
            while (p != null)
            {
                if (p.Data == key)
                {
                    break;
                }
                else if (key < p.Data)
                {
                    parent = p;
                    p = p.LeftChild;
                }
                else
                {
                    parent = p;
                    p = p.RightChild;
                }
            }
            //未找到關鍵碼
            if (p == null)
            {
                return false;
            }

            //既沒有左子節點又沒有右子節點
            if ((p.LeftChild == null) && (p.RightChild == null))
            {
                if (p.Data < parent.Data)
                {
                    parent.LeftChild = null;
                }
                else
                {
                    parent.RightChild = null;
                }
            }
            //沒有右子節點
            else if ((p.LeftChild != null) && (p.RightChild == null))
            {
                if (p.Data < parent.Data)//判斷刪除節點為父節點的左孩子還是右孩子
                {
                    parent.LeftChild = p.LeftChild;
                }
                else
                {
                    parent.RightChild = p.LeftChild;
                }
            }
            //沒有左子節點
            else if ((p.LeftChild == null) && (p.RightChild != null))
            {
                if (p.Data < parent.Data)
                {
                    parent.LeftChild = p.RightChild;
                }
                else
                {
                    parent.RightChild = p.RightChild;
                }
            }
            //左右均有
            else
            {
                BinaryNode<int> fillNode = p.RightChild;
                BinaryNode<int> fillParentNode = p;
                //迴圈找到p的右子樹中最小的節點,即最靠左的節點
                while (fillNode.LeftChild != null)
                {
                    fillParentNode = fillNode;
                    fillNode = fillNode.LeftChild;
                }
                //判斷填充節點為它的父節點的左孩子還是右孩子
                if (fillNode.Data < fillParentNode.Data)//左孩子
                {
                    if (fillNode.RightChild != null)//判斷填充節點是否有右孩子
                    {
                        fillParentNode.LeftChild = fillNode.RightChild;
                    }
                    else
                    {
                        fillParentNode.LeftChild = null;
                    }
                }
                else//右孩子
                {
                    if (fillNode.RightChild != null)
                    {
                        fillParentNode.RightChild = fillNode.RightChild;
                    }
                    else
                    {
                        fillParentNode.RightChild = null;
                    }
                }
                fillNode.LeftChild = p.LeftChild;
                fillNode.RightChild = p.RightChild;
                if (p.Data != bt.Root.Data)
                {
                    if (p.Data < parent.Data)//判斷刪除節點為父節點的左孩子還是右孩子
                    {
                        parent.LeftChild = fillNode;
                    }
                    else
                    {
                        parent.RightChild = fillNode;
                    }
                }
                else
                {
                    bt.Root = fillNode;
                }
            }           
            return true;
        }
    }

 class Program
    {
        static void Main(string[] args)
        {
            BinarySearchTree bst = new BinarySearchTree();
            BinaryTree<int> bt=new BinaryTree<int>();
            bst.Insert(bt, 10);
            bst.Insert(bt, 6);
            bst.Insert(bt, 16);
            bst.Insert(bt, 25);
            bst.Insert(bt, 8);
            bst.Insert(bt, 22);
            bst.Insert(bt, 12);
            bst.Insert(bt, 5);
            bst.Insert(bt, 7);
            bst.Delete(bt, 10);
            bool isExist = bst.SearCh(bt, 10);
            string msg=isExist?"存在資料":"不存在資料";
            Console.WriteLine(msg);
            Console.ReadLine();
        }

        static void WriteOut(int[] seq)
        {
            foreach (int i in seq)
            {
                Console.Write("{0} ,", i);
            }
            Console.WriteLine();
        }
    }

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.