20172332 2017-2018-2 《程序設計與數據結構》第六週學習總結

20172332 2017-2018-2 《程序設計與數據結構》第六週學習總結

教材學習內容總結

第十章 樹

  • 1.非線性結構:樹。(由結點和邊的集構成)
  • 2.元素被存儲在樹的結點中,邊將每一個結點鏈接起來。每一個結點都位於該樹層次結構中的某一特定層上。
  • 3.樹的根是該樹頂層的惟一結點,一棵樹只有一個根。
  • 4.基本的樹的知識。根不是內部結點html

  • 5.這裏的樹與實際的樹是上下顛倒的,樹的根是進入點,根是樹中全部結點的最終祖先,沿着起始自某一特定結點的路徑能夠到達的結點是該結點的子孫。
  • 6.結點的層就是從根結點到該結點的路徑長度,從根到該結點所必須越過的邊數目就能夠肯定其路徑長度。根位於層0,根的孩子位於層1,依次類推。
  • 7.樹的高度是指根到葉子之間最遠路徑的長度。例如上圖從A(根)到F(最遠的葉子)的路徑長度爲2,因此高度爲2。
  • 8.樹的分類方式中最重要的一條標準是樹中任一結點能夠具備的最大孩子數目(被稱做度)。①廣義樹:對結點所含有的孩子數目無限制的樹。②n元樹:每一結點限制爲不超過n個孩子的樹。
  • 9.二叉樹:結點最多具備兩個孩子的樹(包括沒有孩子和一個孩子的狀況)。
  • 10.樹的全部葉子都位於同一層或者至少是彼此相差不超過一個層,就稱之爲是平衡。上圖的F,G,H,I,J位於同一層,與D只相差一層,因此上圖的樹是平衡的。下圖的樹由於O與D相差了兩層超過一層,因此這個樹是不平衡的。前端

  • 11.滿樹:若是一棵n元樹的全部葉子都位於同一層且每一結點要麼是一片葉子要麼正好具備n個孩子,則稱此樹是滿的。如圖:java

  • 12.用鏈式結構實現樹,每個結點均可以定義成一個TreeNode類,每一結點都將包含一個指針,它指向將要存儲在該結點的元素,以及該結點全部可能孩子的指針,也能夠在每一結點中存儲指向其雙親的指針。
  • 13.用數組實現樹有兩個原則方法:計算策略和模擬連接策略。
  • 14.計算策略:特別是二叉樹,一種策略是使用數組來存儲一棵樹。node

  • 15.模擬連接:按照先來先服務的基準連續分配數組位置,而不是經過其在樹中的定位將樹元素指派到數組位置上。每一結點存儲的將是每一孩子(可能還有其雙親)的數組索引,而不是做爲指向其孩子(可能還有其雙親)指針的對象引用變量。【不用考慮樹的徹底性】
  • 16.模擬連接使元素可以連續存儲在數組中,不會浪費空間,可是增長了刪除樹中元素的成本,須要進行移位操做,要麼須要保留一個空閒列表。
  • 17.通常而言,一棵含有m個元素的平衡n元樹具備的高度爲logn m。
  • 18.樹的遍歷:
    • ①前序遍歷:從根結點開始,訪問每一結點及其孩子。git

    • ②中序遍歷:從根結點開始,訪問結點的左孩子,而後是該結點,再而後是任何剩餘結點。數組

    • ③後序遍歷:從根結點開始,訪問結點的孩子,而後是該結點。數據結構

    • ④層序遍歷:從根結點開始,訪問每一層的全部結點,一次一層。post

  • 19.樹的操做及說明:學習

  • 20.二叉樹的性質:
    • (1)在二叉樹的第i層上至多有2的(i-1)次方個結點。(i>=1)
    • (2)深度爲k的二叉樹上至多含有2的k次方-1個結點(k>=1)
    • (3)對任何一棵二叉樹,若它含有與n0個葉子結點,n2個度爲2的結點,則有n0=n2+1(分支數等於結點數-1)
  • 21.徹底二叉樹的性質:
    • (1)具備n個結點的徹底二叉樹的高度爲[log2 n]+1
    • (2)若是將一顆有n個結點的徹底二叉樹自頂向下,同一層自左向右連續給節點編號1,2,3...,則對於任意結點i(1<=i<=n),
      • ①若i=1,則該i結點是樹根,他無雙親。②若2i>n,則編號爲i的結點無左孩子,不然他的左孩子是編號爲2i的結點。③若2i+1>n,則編號爲i的結點無右孩子,不然其右孩子結點編號爲2i+1。
  • 22.決策樹:結點表示決策點(也就是進行判斷的條件),左子結點表示「否」,右子結點表示「是」。this

  • 23.已知兩個序列(必須有一箇中序),能肯定惟一樹。方法:
    • ①已知中序和後序。後序最後一個爲根,中序根的左邊爲左子樹,右邊爲右子樹,再看後序,找出左邊與右邊,最後的又爲根,以此類推。(後序定根中序分左右。)
    • ②已知中序和先序。先序第一個爲根,中序與上面一條同樣,再看先序,找出左邊與右邊,第一個又爲根,以此類推。(先序定根中序分左右。)

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

  • 問題1:一種可能的計算策略,任何存儲在數組位置n處的元素而言,左孩子和右孩子的位置有計算公式,若是隻有一個孩子,位置怎麼肯定?
  • 問題1解決方案:按照左孩子的計算公式算就能夠,實際上它的位置至關於滿樹時所在的位置。

  • 問題2:樹的徹底性是什麼?
  • 問題2解決方案:徹底樹從根結點到倒數第二層知足滿樹,最後一層能夠不徹底填充,其葉子結點都靠左對齊。滿樹必定是一棵徹底樹。以下圖:

  • 問題3:對於樹的四種遍歷方法的通俗理解及代碼實現。
  • 問題3解決方案:二叉樹的前序、中序、後序遍歷,就能夠分紅左右兩支看,先按照規律走左支,走到最高層的葉子或回到根結點時,再按照一樣的規律走右支。
    • ①前序:遍歷過程:根結點——左結點(沒有左結點時輸出右結點)——右結點(而後返回上一個結點找上一個結點的右孩子如沒有再向上找上上個結點的右孩子。)
    public void preOrder(BinaryTreeNode<T> root) {
          if (root != null) {
              System.out.print(root.element + " ");
              preOrder(root.left);
              preOrder(root.right);
          }
      }
    • ②中序:遍歷過程:先找到層數最高的左孩子——該孩子的雙親——這個左孩子的兄弟(右結點)——而後再向低一層的層數也就是該孩子的爺爺。按照定義,會發現這個爺爺的左結點已經輸出過了,該結點也輸出過了,因此輸出該孩子的右結點,依次類推。
    public void inOrder(BinaryTreeNode<T> root) {
          if (root != null) {
              inOrder(root.left);
              System.out.print(root.element + " ");
              inOrder(root.right);
    
          }
      }
    • ③後序:遍歷過程:先找到層數最高的左孩子——這個左孩子的兄弟(右結點)——這兩個結點的雙親。按照定義,這個雙親也會有兄弟,因此再輸出這個雙親的兄弟,在輸出他們的雙親,以此類推。
    public void postOrder(BinaryTreeNode<T> root) {
          if (root != null) {
              postOrder(root.left);
              postOrder(root.right);
              System.out.print(root.element + " ");
          }
      }
    • ④層序:遍歷過程:一層一層從根結點開始自左向右的輸出。
    public void levekOrder() {
          if (root != null) {
              LinkedList<BinaryTreeNode> queue = new LinkedList<>();
              BinaryTreeNode<T> p;
              queue.push(root);
    
              while (!queue.isEmpty()) {
                  p = queue.removeFirst();
                  System.out.print(p.element + " ");
                  if (p.left != null)
                      queue.addLast(p.left);
                  if (p.right != null)
                      queue.addLast(p.right);
              }
          } else
              System.out.println("null");
      }
  • 問題4:表達式樹的計算過程是什麼?

  • 問題4解決方案:
    • 表達式樹的求值是從下往上的,對本樹的分析過程,先是5-3=2,再是2*4=8,而後8+9=17

    • 對於上圖這種左右支都有不少孩子的樹,計算過程:從層數最高的開始求,2-1=1,3+1=4,先把4放在根的右孩子上,再算左支,5+4=9,而後根的左右孩子相乘,4*9=36
    • 總結:①從下往上計算;②左右兩支分開計算;③四則運算前面的數都是左孩子,後面的數都是右孩子。

  • 問題5:樹的高度和深度區別是什麼?
  • 問題5解決方案:最深的葉結點的深度就是樹的深度;樹根的高度就是樹的高度。同結點的,深度數=高度數+1。

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

  • 問題1:BinaryTreeNode類中此方法的代碼解讀。

  • 問題1解決方案:此方法運用了遞歸原理,判斷左孩子是否存在,只要存在就+1,最後得出左孩子的總數用children變量存儲。以後再算右孩子的數量,一樣運用遞歸原理,判斷右孩子是否存在,存在就+1,最後與左孩子的總數加起來,成爲全部孩子(也就是沒有根結點)的數量。

  • 問題2:顯示出樹的形式的代碼理解。
  • 問題2解決方案:整體思想有點相似於層序輸出,從根開始從右往左依次分層輸出。可是須要在輸出結點的同時,須要記錄本層每一個結點對應下一層的孩子數

public String printTree()
    {
        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;
    }
  • 代碼理解:由於這段代碼很長,不懂的地方不少,因此一段一段的分析。
  • ①printDepth顯而易見是獲得這棵樹的高度(根結點所在層爲0),根據二叉樹的性質(2)深度爲k的二叉樹上至多含有2的k次方-1個結點(k>=1)由於個人getHeight()是高度,因此能夠知道possibleNodes(最多結點數爲)2的^(printDepth+1)-1
int printDepth = this.getHeight();
    int possibleNodes = (int)Math.pow(2, printDepth +1);
  • ②currentLevel表示輸出結點的下一層的孩子數,previousLevel表示當前層數的孩子數分配,由於剛開始根爲0,因此須要等於-1。在循環中,把數據從根依次放進名爲nodes無序列表的前端,levelList的無序列表記錄下一次的孩子數的分配
nodes.addToRear(root);
    Integer currentLevel = 0
    Integer previousLevel = -1;
    levelList.addToRear(currentLevel);
  • ③當前的結點數以前定義爲0,比最大可能的結點數小的時候就進入循環。保證輸出所有的結點。
while (countNodes < possibleNodes)
        {
            countNodes = countNodes + 1;
            current = nodes.removeFirst();
            currentLevel = levelList.removeFirst();
  • 無上面代碼的效果圖:

  • result = result + "\n\n";是由於二叉樹,爲了造成樹的形狀,因此至關於\n\n爲一組的存在,不會讓數在一行,同時保證了每行第一個數以前的空格數。其中for循環的循環體爲了保證每行第一個數以前的空格數。
if (currentLevel > previousLevel)
                {
                    result = result + "\n\n";
                    previousLevel = currentLevel;
                    for (int j = 0; j < ((Math.pow(2, (printDepth - currentLevel))) - 1); j++)
                        result = result + " ";
                }
  • 無上面代碼的效果圖:

  • 無上面代碼for循環的效果圖:

  • ⑤保證結點間的距離,不讓每一個元素緊挨在一塊兒,分不清。
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 + " ";
                }
        }
  • 無上面代碼的效果圖:

  • 問題3:背部診斷器的相關文件是存在的,可是不能運行。

  • 問題3解決方案:由於文件放錯位置了,默認是工程根目錄,和src是同一個級別的文件。不能放在src文件中,更不能放在src文件的子文件中。

  • 問題4:DecisionTree類的代碼理解。

public DecisionTree(String filename) throws FileNotFoundException
    {
        File inputFile = new File(filename);
        Scanner scan = new Scanner(inputFile);
        int numberNodes = scan.nextInt();
        scan.nextLine();
        int root = 0, left, right;

        List<LinkedBinaryTree<String>> nodes = new java.util.ArrayList<LinkedBinaryTree<String>>();
        for (int i = 0; i < numberNodes; i++)
            nodes.add(i,new LinkedBinaryTree<String>(scan.nextLine()));

        while (scan.hasNext())
        {
            root = scan.nextInt();
            left = scan.nextInt();
            right = scan.nextInt();
            scan.nextLine();

            nodes.set(root, new LinkedBinaryTree<String>((nodes.get(root)).getRootElement(),
                                                       nodes.get(left), nodes.get(right)));
        }
        tree = nodes.get(root);
    }
  • 問題4解決方案:這個方法是爲了把文件中的問題放入樹中的結點中,讓問題組成一個樹(決策樹),而後再用斷定的方法進行判斷。
  • 文件內容以下圖,第一個數13爲結點數。

  • 下圖是從文件中讀出的信息所組成的樹。

  • 關鍵代碼及解釋:
  • ①爲了把讀入的內容變爲結點。
List<LinkedBinaryTree<String>> nodes = new java.util.ArrayList<LinkedBinaryTree<String>>();
        for (int i = 0; i < numberNodes; i++)
            nodes.add(i,new LinkedBinaryTree<String>(scan.nextLine()));
  • ②肯定結點的位置,第一個數爲根的位置,第二個數爲左孩子的位置,第三個數爲右孩子的位置,而後把相應的結點放入樹中。
while (scan.hasNext())
        {
            root = scan.nextInt();
            left = scan.nextInt();
            right = scan.nextInt();
            scan.nextLine();

            nodes.set(root, new LinkedBinaryTree<String>((nodes.get(root)).getRootElement(),
                                                       nodes.get(left), nodes.get(right)));
        }

代碼託管

上週考試錯題總結

  • 無。

點評過的同窗博客和代碼

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

  • 由於是第一次學習這種非線性結構,因此剛開始的時候有點摸不着頭腦,有一些較難的點,花費了本身大量的時間去學習,可是以爲頗有收貨。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 2/2
第二週 1010/1010 1/2 10/12
第三週 651/1661 1/3 13/25
第四周 2205/3866 1/4 15/40
第五週 967/4833 2/6 22/62
第六週 1680/6513 1/7 34/96
  • 計劃學習時間:20小時

  • 實際學習時間:34小時

  • 改進狀況:這周的新內容難度很大,學習時間遠超本身的計劃時間

參考資料

相關文章
相關標籤/搜索