20172313 2018-2019-1 《程序設計與數據結構》第六週學習總結

20172313 2018-2019-1 《程序設計與數據結構》第六週學習總結

教材學習內容總結

  • 概述
    • 樹是一種非線性結構,其中的元素被組織成一個層次結構。
    • 樹由一個包含結點(node)和(edge)的集構成,其中的元素被存儲在這些結點中,邊則將一個結點和另外一個結點鏈接起來。每一結點都位於該數層次結構中的某一特定層上,樹的根就是那個位於該樹頂層的惟一結點。一棵樹只有一個根結點。
    • 基本術語:
      • 結點:存儲數據元素和指向子樹的連接,由數據元素和構造數據元素之間關係的引用組成。
      • 孩子結點:樹中一個結點的子樹的根結點稱爲這個結點的孩子結點,如圖1中的A的孩子結點有B、C、D
      • 雙親結點:樹中某個結點有孩子結點(即該結點的度不爲0),該結點稱爲它孩子結點的雙親結點,也叫前驅結點。雙親結點和孩子結點是相互的,如圖1中,A的孩子結點是B、C、D,B、C、D的雙親結點是A。
      • 兄弟結點:具備相同雙親結點(即同一個前驅)的結點稱爲兄弟結點,如圖1中B、B、D爲兄弟結點。
      • 結點的度:結點全部子樹的個數稱爲該結點的度,如圖1,A的度爲3,B的度爲2。
      • 樹的度:樹中全部結點的度的最大值稱爲樹的度,如圖1的度爲3。
      • 葉子結點:度爲0的結點稱爲葉子結點,也叫終端結點。如圖1的K、L、F、G、M、I、J
      • 分支結點:度不爲0的結點稱爲分支結點,也叫非終端結點。如圖1的A、B、C、D、E、H
      • 結點的層次:從根結點到樹中某結點所經路徑的分支數稱爲該結點的層次。根結點的層次通常爲1(也能夠本身定義爲0),這樣,其它結點的層次是其雙親結點的層次加1。
      • 樹的深度:樹中全部結點的層次的最大值稱爲該樹的深度(也就是最下面那個結點的層次)
      • 有序樹和無序樹:樹中任意一個結點的各子樹按從左到右是有序的,稱爲有序樹,不然稱爲無序樹。
      • 咱們將每一節點限制爲不超過n個孩子的樹稱爲n叉樹。
      • 若是說樹的全部葉子都位於同一層或者至少是彼此相差不超過一個層,就稱之爲平衡的。
      • 若是某樹是平衡的,且底層全部葉子都位於樹的左邊,則認爲該樹是徹底的。
  • 樹的數組實現之計算策略
    • 樹的數組實現之計算策略:
      • 一種可能的計算策略是將元素n的左孩子置於位置(2 * n-1),將右孩子置於位置(2 *(n+1))
      • 優勢:咱們能夠依照容量對其進行管理,這與咱們用數組實現列表、隊列和棧且對其進行容量管理時的方式基本同樣。
      • 缺點:若是存儲的樹不是徹底的或只是相對徹底的,則該數組會爲不包括數據的樹位置分配空間,浪費了大量的存儲空間。
    • 樹的數組實現之模擬連接策略:
      • 模擬連接策略容許連續分配數組位置而不用考慮該樹的徹底性。
      • 該數組的每一元素都是一個結點類,每一節點存儲的是每一孩子(可能還有其雙親)的數組索引,而不是做爲指向其孩子(可能還有其雙親)指針的對象引用變量。
      • 優勢:這種方式是的元素可以連續存儲在數組中,所以不會浪費空間。
      • 缺點:該方式增長了刪除樹中元素的成本,由於它要麼須要對生育元素進行移位以維持連續狀態,要麼須要保留一個空閒列表。
    • 通常而言,一棵含有m各元素的平衡n元樹具備的高度爲lognm。
  • 樹的遍歷
    • 前序遍歷: 從根結點開始,訪問每一結點及其孩子。
    Visit node
    Traverse(left child)
    Traverse(right child)
    • 中序遍歷: 從根節點開始,訪問結點的左孩子,而後是該結點,再而後是任何剩餘結點。
    Traverse(left child)
    Visit node
    Traverse(right child)
    • 後序遍歷: 從根結點開始,訪問結點的孩子,而後是該結點。
    Traverse(left child)
    TRaverse(right child)
    Visit node
    • 層序遍歷: 從根結點開始,訪問每一層的全部結點,訪問每一層的全部結點,一次一層。
    Creat a queue called nodes
    Create an unordered list calles results
    Enqueue the root onto the nodes queue
    while the nodes queue is not empty
    {
        Dequeue the first element from the queue
        If that element is not null
           Add that element to the rear of the results list
           Enqueue the children of the element on the nodes queue
         Else
            Add null on the result list
    
    }
    Return an iterator for the result list
  • 二叉樹
操做 最差時間複雜度
getRoot 返回指向二叉樹根的引用 
isEmpty 斷定該樹是否爲空 
size 斷定樹中的元素數目 
contains 斷定指定目標是否在該樹中 
find 若是找到指定元素,則返回指向其的引用 
toString 返回樹的字符串表示 
itertorInOrder 爲樹的中序遍歷返回一個迭代器 
itertorPreOrder 爲樹的前序遍歷返回一個迭代器 
iteratorPostOrder 爲樹的後序遍歷返回一個迭代器 
iteratorLevelOrder 爲樹的層序遍歷返回一個迭代器 
  • 二叉樹的性質
    • 若二叉樹的根節點位於第一層
      • 在二叉樹的第i層最多有2i-1個結點。
      • 深度爲k的二叉樹最多有2k-1個結點。
      • 對任何一棵二叉樹,若是其葉結點個數爲n0,度爲2的結點數爲n2則有n0=n2+1。
    • 兩種特殊的二叉樹

      html

    • 徹底二叉樹的性質
      • 具備n個結點的徹底二叉樹的高度爲log2n(下取整)+1
      • 若是將一棵有n個結點的徹底二叉樹自頂向下,同一層自左向右給結點編號1,2,3......n,則對於任意結點(1<=i<=n),有:
        • 若i= 1,則該i結點是樹根,它無雙親;
        • 若2i>n,則編號爲i的結點無左孩子,不然它的左孩子是編號爲2*i的結點;
        • 若2i+1>n,則編號爲i的結點無右孩子,不然其右孩子結點編號爲2*i+1;

    • 二叉樹的二叉鏈表存儲結構。二叉鏈表結構主要由一個數據域和兩個分別指向左、右孩子的結點組成,其結構以下:
    BinaryNode(T data , BinaryNode<T> left , BinaryNode<T> right )

    • 二叉樹的三叉鏈表存儲結構。三叉鏈表主要是在二叉鏈表的基礎上多添加了一個指向父結點的域,這樣咱們就存儲了父結點與孩子結點的雙向關係,固然這樣也增長了必定的空開銷其結點結構以下:
    ThreeNode(T data ,ThreeNode<T> parent,ThreeNode<T> left,ThreeNode<T> right)

教材學習中的問題和解決過程

  • 問題一:在樹中Expression中有這樣一段代碼(以下圖代碼),由二叉樹的性質可知,一棵二叉樹當滿樹的狀況時它的結點總數最多爲2^(depth+1)-1,而書上在這裏的int型變量posssibleNodes變量爲2^(depth+1)-1,但在實際進行操做的時候並不能保證該二叉樹是滿樹,那麼爲何可使用「countNodes < possibleNodes」做爲循環條件進行遍歷呢?
UnorderedListADT<BinaryTreeNode<ExpressionTreeOp>> nodes =
            new ArrayUnorderedList<BinaryTreeNode<ExpressionTreeOp>>();
        UnorderedListADT<Integer> levelList =
            new ArrayUnorderedList<Integer>();
        BinaryTreeNode<ExpressionTreeOp> current;
        String result = "";
        int printDepth = this.getHeight();
        int possibleNodes = (int)Math.pow(2, printDepth + 1);
        int countNodes = 0;

        nodes.addToRear(root);
        Integer currentLevel = 0;
        Integer previousLevel = -1;
        levelList.addToRear(currentLevel);

        while (countNodes < possibleNodes)
        {
            countNodes = countNodes + 1;
            current = nodes.removeFirst();
            currentLevel = levelList.removeFirst();
            if (currentLevel > previousLevel)
            {
                result = result + "\n\n";
                previousLevel = currentLevel;
                for (int j = 0; j < ((Math.pow(2, (printDepth - currentLevel))) - 1); j++)
                    result = result + " ";
            }
            else
            {
                for (int i = 0; i < ((Math.pow(2, (printDepth - currentLevel + 1)) - 1)) ; i++)
                {
                    result = result + " ";
                }
            }
            if (current != null)
            {
                result = result + (current.getElement()).toString();
                nodes.addToRear(current.getLeft());
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(current.getRight());
                levelList.addToRear(currentLevel + 1);
            }
            else {
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                result = result + " ";
            }

        }

        return result;
    }
  • 問題一解決方案:咱們先來分析每一行代碼的做用(第一次看到上面這坨代碼,有那麼多循環,真使人頭皮發麻),單用眼看怕是很難看出來,最簡單直接的方法就是run一下看運行出來的結果進行對比。先來run一下本來的代碼:

    OK,沒有問題,很是完美。而後註釋掉第一個for循環

    咱們能夠清楚的看到左邊的空白都沒有了,很明顯,第一個for循環是爲了打印每一行最前面的空白來保證「樹」的形狀,咱們再來註釋掉第二個循環

    一樣的,咱們能夠很明顯的看出每一行的兩結點處的留白都沒有了,如今咱們弄明白了兩個for循環的做用,免去了它們的干擾,接着咱們回過頭來看循環條件的問題。咱們首先要明確的是這裏possibleNodes的值,因爲該程序中height是從一開始算的,因此這裏的possibleNodes值應該是2的五次方,按照問題裏個人想法,這個值的數目應該是遠遠大於所須要的限定值,咱們先適當的縮小possibleNodes的範圍看看結果會發生什麼變化。(對possibleNodes-1)

    結果並無發生什麼變化,這就說明possibleNodes的值確實是偏大的。該二叉樹中共有九個元素因此理論上咱們只須要九次循環就足夠,因此把possibleNodes減去(32-9)(簡單的數學問題)23次再試試!


    此次就和預期的結果不太同樣了,應該是possibleNodes再減去一的時候樹纔會發生變化纔對,爲何如今結點就減小了呢?這時,我發現,樹的最下面一層結點靠右側,在代碼表示時是預留了空間的,分別測試possibleNodes爲15和14時的狀況


      如上圖所示,跟預期的結果同樣,因此綜上所述,possibleNodes的限定範圍確實是過大的,可是在循環完結點後,剩下的循環次數爲打印空白,因此在視覺效果上就跟沒有執行同樣!
  • 問題二:ExpressionTreeOp類中定義了一個int型變量termType,在判斷一個結點儲存的element是都爲操做符時返回「termType==1」,對這行代碼不是特別明白。
  • 問題二解決方案:緊接着教材的學習,有這樣的一段代碼
if ((operator == '+') || (operator == '-') || (operator == '*') || 
                 (operator == '/'))
            {
                operand1 = getOperand(treeStack);
                operand2 = getOperand(treeStack);
                treeStack.push(new ExpressionTree 
                                  (new ExpressionTreeOp(1,operator,0), operand2, operand1));
            }
            else
            {
                treeStack.push(new ExpressionTree(new ExpressionTreeOp
                                    (2,' ',Integer.parseInt(tempToken)), null, null));
            }

原來,termType的用法是接收傳入的形參,判斷傳入的形參是否爲1以此來返回ture或者false來判斷是否爲操做符。java

代碼調試中的問題和解決過程

  • 問題1:在對方法進行測試時,發現使用toString時會出現空指針的狀況
  • 問題一解決方法:我仔細閱讀了報錯給的提示,發現最終是個人ArrayList中的find方法存在毛病,由於所比較的數據類型爲T,因此在判斷兩個元素是否相等的時候不能使用「equals」,更改爲「==」問題就得解決掉了。

代碼託管

上週考試錯題總結

  這周沒有錯題哦~node

結對及互評

  • 博客中值得學習的或問題:
    • 排版精美,對教材的總結細緻,善於發現問題,對於問題研究得很細緻,解答也很周全。
  • 代碼中值得學習的或問題:
    • 代碼寫的很規範,思路很清晰,繼續加油!

點評過的同窗博客和代碼

其餘(感悟、思考等,可選)

  這周的內容相比前面幾周所學習的內容來講難了許多,從代碼的理解難易程度上就可以感受的出來,這周也花費了我更多的實踐,同時也給本身敲了一個警鐘,本身不會和不清楚的只是還有不少,不能再像以前那樣佛系學習,要更加的努力纔是,但願能在之後的學習生活中繼續進步!git

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
第一週 200/200 1/1 5/20
第二週 981/1181 1/2 15/20
第三週 1694/2875 1/3 15/35
第四周 3129/6004 1/4 15/50
第五週 1294/7298 1/5 15/65
第六週 1426/8724 1/6 20/85
  • 計劃學習時間:20小時web

  • 實際學習時間:20小時算法

參考資料

相關文章
相關標籤/搜索