1. 程式人生 > >資料結構基礎溫故-4.樹與二叉樹(下)

資料結構基礎溫故-4.樹與二叉樹(下)

上面兩篇我們瞭解了樹的基本概念以及二叉樹的遍歷演算法,還對二叉查詢樹進行了模擬實現。數學表示式求值是程式設計語言編譯中的一個基本問題,表示式求值是棧應用的一個典型案例,表示式分為字首、中綴和字尾三種形式。這裡,我們通過一個四則運算的應用場景,藉助二叉樹來幫助求解表示式的值。首先,將表示式轉換為二叉樹,然後通過先序遍歷二叉樹的方式求出表示式的值。

一、二叉樹如何表示四則運算

1.1 表示式轉換為二叉樹

  上圖是表示式“3+2*9-16/4”轉換成的二叉樹,觀察表示式,可以看出:

  (1)運算元都是葉子節點

  (2)運算子都是內部節點

  (3)優先運算的操作符都在樹下方,而相對優先順序較低的減法(根節點)運算則最後運算。

  從上往下看,這棵二叉樹可以理解如下:

  (1)要理解根節點"-"號的結果必須先計算出左子樹"+"和右子樹"/"號的結果。可以看,要想得到"+"號的結果,又必須先計算其右子樹"*"號的結果;

  (2)"*"號左右孩子是數字,可以直接計算,2*9=18。接下來計算"+"號,3+18=21,即根節點的左子樹結果為21;

  (3)"/"號左右孩子是數字,可以直接計算,16/4=4。於是,根節點的右子樹結果為4。

  (4)最後計算根節點的"-"號,21-4=17,於是得出了該表示式的值為17。

1.2 二叉表示式樹的構造過程解析

   從上面的解析過程可以看出,這是一個遞迴的過程,正好可以用二叉樹先序遍歷的方法進行計算。下面我們來一步一步地通過圖示來演示一下表達式"3+2*9-16/4"解析生成二叉樹的過程。

  (1)首先獲取表示式的第一個字元“3”,由於表示式樹目前還是一棵空樹,所以3成為根節點;

  (2)獲取第二個字元“+”,此時表示式樹根節點為數字,需要將新節點作為根節點,原根節點作為新根節點的左孩子。這裡需要注意的是:只有第二個節點會出現這樣的可能,因為之後的根節點必定為操作符;

  (3)獲取第三個字元“2”,數字將沿著根節點右鏈插入到最右端;

  (4)獲取第四個字元“*”,如果判斷到是操作符,則將與根節點比較優先順序,如果新節點的優先順序高則插入成為根節點的右孩子,而原根節點的右孩子則成為新節點的左子樹;

  (5)獲取第五個字元“9”,數字將沿著根節點右鏈插入到最右端;

  (6)獲取第六個字元“-”,“-”與根節點“+”比較運算子的優先順序,優先順序相等則新節點成為根節點,原表示式樹則成為新節點的左子樹;

  (7)獲取第7與第8個字元組合為數字16,沿著根節點右鏈插入到最右端;

  (8)獲取第九個字元“/”,與根節點比較運算子的優先順序,優先順序高則成為根節點的右孩子,原根節點右子樹則成為新節點的左子樹;

  (9)獲取第十個字元“4”,還是沿著根節點右鏈查到最右端。至此,運算表示式已全部遍歷,一棵表示式樹就已經建立完成。

SUMMARY:從以上過程中我們可以將表示式樹的建立演算法歸結如下

①第一個節點先成為表示式樹的根;

②第二個節點插入時變為根節點,原根節點變為新節點的左孩子;

③插入節點為數字時,沿著根節點右鏈插入到最右端;

④插入節點為操作符時,先跟根節點操作符進行對比,分兩種情況進行處理:

  一是當優先順序不高時,新節點成為根節點,原表示式樹成為新節點的左子樹;【如上面的步驟(6)】

  二是當優先順序較高時,新節點成為根節點右孩子,原根節點右子樹成為新節點的左子樹。【如上面的步驟(8)】

二、二叉表示式樹的模擬實現

2.1 二叉表示式樹節點的定義

        private class Node
        {
            private bool _isOptr;

            public bool IsOptr
            {
                get { return _isOptr; }
                set { _isOptr = value; }
            }
            private int _data;

            public int Data
            {
                get { return _data; }
                set { _data = value; }
            }
            private Node _left;

            public Node Left
            {
                get { return _left; }
                set { _left = value; }
            }
            private Node _right;

            public Node Right
            {
                get { return _right; }
                set { _right = value; }
            }

            public Node(int data)
            {
                this._data = data;
                this._isOptr = false;
            }

            public Node(char optr)
            {
                this._isOptr = true;
                this._data = optr;
            }

            public override string ToString()
            {
                if (this._isOptr)
                {
                    return Convert.ToString((char)this._data);
                }
                else
                {
                    return this._data.ToString();
                }
            }
        }
View Code

  與普通二叉樹節點定義不同,這裡新增了一個isOptr標誌,來判斷該節點是數字節點還是運算子節點;

2.2 二叉表示式樹的建立實現

        private Node CreateTree()
        {
            Node head = null;
            
            while(_pos < _expression.Length)
            {
                Node node = GetNode(); // 將當前解析字元轉換為節點
                if(head == null)
                {
                    head = node;
                }
                else if (head.IsOptr == false) // 根節點為數字,當前節點為根,原根節點變為左孩子
                {
                    node.Left = head;
                    head = node;
                }
                else if (node.IsOptr == false) // 如果當前節點是數字
                {
                    // 當前節點沿右路插入最右邊成為右孩子
                    Node tempNode = head;
                    while(tempNode.Right != null)
                    {
                        tempNode = tempNode.Right;
                    }
                    tempNode.Right = node;
                }
                else // 如果當前節點是運算子
                {
                    if (GetPriority((char)node.Data) <= GetPriority((char)head.Data)) // 優先順序低則成為根,原二叉樹成為插入節點的左子樹
                    {
                        node.Left = head;
                        head = node;
                    }
                    else // 優先順序高則成為根節點的右子樹,原右子樹成為插入節點的左子樹
                    {
                        node.Left = head.Right;
                        head.Right = node;
                    }
                }
            }

            return head;
        }

  這裡按照我們在上面所歸納的建立過程演算法進行了實現,程式碼中的註釋已經比較完善,這裡就不再贅述。

2.3 二叉表示式的先序遍歷計算運算結果實現

        // 先序遍歷進行表示式求值
        private int PreOrderCalc(Node node)
        {
            int num1, num2;
            if (node.IsOptr)
            {
                // 遞迴先序遍歷計算num1
                num1 = PreOrderCalc(node.Left);
                // 遞迴先序遍歷計算num2
                num2 = PreOrderCalc(node.Right);
                char optr = (char)node.Data;

                switch (optr)
                {
                    case '+':
                        node.Data = num1 + num2;
                        break;
                    case '-':
                        node.Data = num1 - num2;
                        break;
                    case '*':
                        node.Data = num1 * num2;
                        break;
                    case '/':
                        if (num2 == 0)
                        {
                            throw new DivideByZeroException("除數不能為0!");
                        }
                        node.Data = num1 / num2;
                        break;
                }
            }

            return node.Data;
        }

  這裡通過遞迴地進行先序遍歷,也就是求得根節點(運算子)的兩個子樹的值,最後再通過對這兩個值進行根節點運算子的計算得到最終的結果。

2.4 四則運算執行結果

  由於本表示式樹的設計較為簡單,沒有考慮到帶括號的情形,因此這裡只用不帶括號的表示式進行檢視,執行結果如下圖所示:

  (1)3+2*9-16/4

  (2)4*5-16/4+2*9

附件下載

參考資料

(1)陳廣,《資料結構(C#語言描述)》

(3)zhx6044,《棧和二叉樹的使用

(4)zero516cn,《算術表示式—二叉樹

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

資料結構基礎-4.

上面兩篇我們瞭解了樹的基本概念以及二叉樹的遍歷演算法,還對二叉查詢樹進行了模擬實現。數學表示式求值是程式設計語言編譯中的一個基本問題,表示式求值是棧應用的一個典型案例,表示式分為字首、中綴和字尾三種形式。這裡,我們通過一個四則運算的應用場景,藉助二叉樹來幫助求解表示式的值。首先,將表示式轉換為二叉樹,然後通過

資料結構基礎-4.

在上一篇中,我們瞭解了樹的基本概念以及二叉樹的基本特點和程式碼實現,還用遞迴的方式對二叉樹的三種遍歷演算法進行了程式碼實現。但是,由於遞迴需要系統堆疊,所以空間消耗要比非遞迴程式碼要大很多。而且,如果遞迴深度太大,可能系統撐不住。因此,我們使用非遞迴(這裡主要是迴圈,迴圈方法比遞迴方法快, 因為迴圈避免了一系

資料結構基礎-4.

前面所討論的線性表元素之間都是一對一的關係,今天我們所看到的結構各元素之間卻是一對多的關係。樹在計算機中有著廣泛的應用,甚至在計算機的日常使用中,也可以看到樹形結構的身影,如下圖所示的Windows資源管理器和應用程式的選單都屬於樹形結構。樹形結構是一種典型的非線性結構,除了用於表示相鄰關係外,還可以表示層次

資料結構基礎-6.查詢:基本查詢表查詢

只要你開啟電腦,就會涉及到查詢技術。如炒股軟體中查股票資訊、硬碟檔案中找照片、在光碟中搜DVD,甚至玩遊戲時在記憶體中查詢攻擊力、魅力值等資料修改用來作弊等,都要涉及到查詢。當然,在網際網路上查詢資訊就更加是家常便飯。查詢是計算機應用中最常用的操作之一,也是許多程式中最耗時的一部分,查詢方法的優劣對於系統的執

資料結構基礎-5.圖:圖的遍歷演算法

上一篇我們瞭解了圖的基本概念、術語以及儲存結構,還對鄰接表結構進行了模擬實現。本篇我們來了解一下圖的遍歷,和樹的遍歷類似,從圖的某一頂點出發訪問圖中其餘頂點,並且使每一個頂點僅被訪問一次,這一過程就叫做圖的遍歷(Traversing Graph)。如果只訪問圖的頂點而不關注邊的資訊,那麼圖的遍歷十分簡單,使用

資料結構基礎-1.線性表

在上一篇中,我們學習了線性表最基礎的表現形式-順序表,但是其存在一定缺點:必須佔用一整塊事先分配好的儲存空間,在插入和刪除操作上需要移動大量元素(即操作不方便),於是不受固定儲存空間限制並且可以進行比較快捷地插入和刪除操作的連結串列橫空出世,所以我們就來複習一下連結串列。 一、單鏈表基礎 1.1 單鏈表的

資料結構基礎-6.查詢:雜湊表

雜湊(雜湊)技術既是一種儲存方法,也是一種查詢方法。然而它與線性表、樹、圖等結構不同的是,前面幾種結構,資料元素之間都存在某種邏輯關係,可以用連線圖示表示出來,而雜湊技術的記錄之間不存在什麼邏輯關係,它只與關鍵字有關聯。因此,雜湊主要是面向查詢的儲存結構。雜湊技術最適合的求解問題是查詢與給定值相等的記錄。

資料結構基礎-5.圖:最小生成樹演算法

圖的“多對多”特性使得圖在結構設計和演算法實現上較為困難,這時就需要根據具體應用將圖轉換為不同的樹來簡化問題的求解。 一、生成樹與最小生成樹 1.1 生成樹   對於一個無向圖,含有連通圖全部頂點的一個極小連通子圖成為生成樹(Spanning Tree)。其本質就是從連通圖任一頂點出發進行遍歷操作所經過

資料結構基礎-5.圖:圖的基本概念

前面幾篇已經介紹了線性表和樹兩類資料結構,線性表中的元素是“一對一”的關係,樹中的元素是“一對多”的關係,本章所述的圖結構中的元素則是“多對多”的關係。圖(Graph)是一種複雜的非線性結構,在圖結構中,每個元素都可以有零個或多個前驅,也可以有零個或多個後繼,也就是說,元素之間的關係是任意的。現實生活中的很多

資料結構基礎-5.圖:最短路徑

圖的最重要的應用之一就是在交通運輸和通訊網路中尋找最短路徑。例如在交通網路中經常會遇到這樣的問題:兩地之間是否有公路可通;在有多條公路可通的情況下,哪一條路徑是最短的等等。這就是帶權圖中求最短路徑的問題,此時路徑的長度不再是路徑上邊的數目總和,而是路徑上的邊所帶權值的和。帶權圖分為無向帶權圖和有向帶權圖,但如

資料結構基礎-2.棧

現實生活中的事情往往都能總結歸納成一定的資料結構,例如餐館中餐盤的堆疊和使用,羽毛球筒裡裝的羽毛球等都是典型的棧結構。而在.NET中,值型別線上程棧上進行分配,引用型別在託管堆上進行分配,本文所說的“棧”正是這種資料結構。棧和佇列都是常用的資料結構,它們的邏輯結構與線性表相通,不同之處則在於操作受某種特殊限制

資料結構基礎-1.線性表

在上一篇中,我們瞭解了單鏈表與雙鏈表,本次將單鏈表中終端結點的指標端由空指標改為指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱為單迴圈連結串列,簡稱迴圈連結串列(circular linked list)。 一、迴圈連結串列基礎 1.1 迴圈連結串列節點結構   迴圈連結串列和單鏈表的

資料結構基礎-3.佇列

在日常生活中,佇列的例子比比皆是,例如在車展排隊買票,排在隊頭的處理完離開,後來的必須在隊尾排隊等候。在程式設計中,佇列也有著廣泛的應用,例如計算機的任務排程系統、為了削減高峰時期訂單請求的訊息佇列等等。與棧類似,佇列也是屬於操作受限的線性表,不過佇列是隻允許在一端進行插入,在另一端進行刪除。在其他資料結構如

資料結構基礎-7.排序

排序(Sorting)是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為按關鍵字“有序”的記錄序列。如何進行排序,特別是高效率地進行排序時計算機工作者學習和研究的重要課題之一。排序有內部排序和外部排序之分,若整個排序過程不需要訪問外存便能完成,則稱此類排序為內部排序,反之則為外部排序。本篇主

3、非線性結構--——數據結構基礎篇】

位置 enter 深度 基礎 表達式 左右 -a 基礎篇 先序遍歷 非線性結構--樹與二叉樹 二叉樹的基礎知識:         二叉樹的特點:             1、每個結點的度<=2             2、二叉樹是有序樹         二叉樹的五種不

Android版資料結構演算法(六):

/** * 前序遍歷——迭代 * @author Administrator * */ public void preOrder(TreeNode node){ if(node == null){ return;

資料結構

上面兩篇我們瞭解了樹的基本概念以及二叉樹的遍歷演算法,還對二叉查詢樹進行了模擬實現。數學表示式求值是程式設計語言編譯中的一個基本問題,表示式求值是棧應用的一個典型案例,表示式分為字首、中綴和字尾三種形式。這裡,我們通過一個四則運算的應用場景,藉助二叉樹來幫助求解表

資料結構演算法】002—Python

概念 樹 樹是一類重要的非線性資料結構,是以分支關係定義的層次結構 定義: 樹(tree)是n(n>0)個結點的有限集T,其中: 有且僅有一個特定的結點,稱為樹的根(root) 當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1,T2,……Tm,其中每一個集合本身又是一棵

資料結構例題

樹與二叉樹例題 例1 高度為K(K>=2)的完全二叉樹至少有()個葉子結點。 解: 根據二叉樹性質 二叉樹第i(i>=1)層上至多有2^(i-1)個結點 第K-1層有 2^(K-1-1)=2^(K-2) 個結點 求二叉樹至少有多

資料結構

  前幾天被面試官問到了二叉樹,因為沒有去複習所以回答的很糟糕,資料結構是大二的時候學的,在平時的web開發我能用到的機會其實不多,所以也沒有去整理,但是資料結構也是程式的靈魂架構,是需要認真研究的,故在此繼續進行整理複習。  一、什麼是樹?1.1  樹是n(n>=0)