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

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

教材學習內容總結

本週學習了第10章php

  • 10.1概述
    • 樹是一種非線性結構,有一個包含結點和邊的集構成。元素保存在結點中,邊則將結點鏈接起來。
    • 一些術語html

      根結點是位於樹頂層的惟一結點
      位於樹中較低層的結點是上一層結點的孩子,一個結點只有一個雙親
      同一雙親的兩個結點稱爲兄弟
      根結點是樹中惟一沒有雙親的結點,沒有任何孩子的結點稱爲葉子,其他爲內部結點
      結點的層是從根結點到該結點的路徑長度
      樹的高度是指從根到葉子之間最遠路徑的長度java

    • 樹中任一結點能夠具備的最大孩子數目稱爲該樹的度。若是度沒有限制,就稱爲廣義樹,每一結點孩子數不超過n個的樹稱爲n元樹。度爲2的樹稱爲二叉樹
    • 若是某樹是平衡的(樹的全部葉子都位於同一層或至少是彼此相差不超過一個層)且底層全部葉子都位於樹的左邊,則認爲該樹是徹底樹。
    • 若是一顆n元樹的全部葉子都位於同一層且每一結點要麼是一片葉子要麼正好有n個孩子,則稱此樹是滿的node

  • 10.2實現樹的策略
    • 計算策略:將元素n的左孩子置於位置(2xn - 1),將右孩子置於位置(2x(n + 1))。缺點是浪費大量存儲空間。
    • 模擬連接策略:數組的每一元素都是一個結點類,每一結點存儲的是每一孩子的數組索引。該策略容許連續分配數組位置而不用考慮樹的徹底性。
    • 通常而言,一棵含有m個元素的平衡n元樹具備的高度爲lognm
  • 10.3樹的遍歷
    • 前序遍歷:從根結點開始,訪問每一結點及其孩子
    • 中序遍歷:根結點-->左邊孩子-->結點-->剩餘結點
    • 後序遍歷:根結點-->孩子-->結點
    • 層序遍歷:從根結點開始,訪問每一層的全部結點,一次一層
  • 10.4二叉樹
操做 說明
getRoot 返回指向二叉樹根的引用
isEmpty 斷定該樹是否爲空
size 斷定樹中的元素數目
contains 斷定指定目標是否在該樹中
find 若是找到指定元素,返回指向其的引用
toString 返回樹的字符串表示
iteratorInOrder 爲樹的中序遍歷返回一個迭代器
iteratorPreOrder 爲樹的前序遍歷返回一個迭代器
iteratorPostOrder 爲樹的後序遍歷返回一個迭代器
iteratorLevelOrder 爲樹的層序遍歷返回一個迭代器

在全部列舉的操做中 ,不存在往樹中添加元素的操做。在一些樹中也沒有刪除樹元素的操做git

  • 10.5使用二叉樹:表達式樹
    • 對錶達式樹的求值是從下往上的
  • 10.6背部疼痛診斷器
    • 決策樹:其結點表示決策點,其子結點表示該決策點的候選項
    • 比較形象的一個決策樹(有點誤差在於這一節裏講的都是二叉決策樹)
  • 10.7用鏈表實現二叉樹
    • 方法以遞歸式編寫的時候,它們一般須要一個私有支持方法,由於第一個調用和隨後每一個用的簽名和行爲多是不相同的

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

  • 問題1:程序列表10.3中判斷是不是運算符的方法
public boolean isOperator(){
    return (termType == 1);
}

爲何須要判斷termType爲一時就是運算符web

  • 問題1解決方案:參考前面的ExpressionOp的定義,設置了兩個int變量和一個char變量
private int termType;
private char operator;
private int value;

結點存儲只須要一個int變量存儲操做數,一個char變量存儲運算符。多出來的int變量termType說明就不是用於存儲操做數。在這個類中,我發現termType的用處都是用來判斷是否爲操做數的,因此設置這個變量的意義就應該在於事先在設置結點時,就標明這個結點存儲的是什麼東西,若是是運算符就把termType改成1,不是就設爲其餘數,這樣就避免了在判斷時要寫一長串的判斷條件if (operator == "+" || operator == "-" || operator == "*" || operator == "/")的狀況express


  • 問題2:程序列表10.2ExpressionTree類提供了一個輸出樹的方法,不容易理解且輸出是有問題的。
  • 問題2解決方案:結合具體代碼進行分析
public String printTree() {
        UnorderedListADT<BinaryTreeNode<ExpressionTreeOp>> nodes = new ArrayUnorderedList<BinaryTreeNode<ExpressionTreeOp>>();//建立了一個存儲BinaryTreeNode類的無序列表
        UnorderedListADT<Integer> levelList = new ArrayUnorderedList<Integer>();//建立一個存儲int變量的無序列表

        BinaryTreeNode<ExpressionTreeOp> current;//設置指向無序列表的一個臨時變量current
        String result = "";
        int printDepth = this.getHeight();//定義printDepth表示樹的高度
        int possibleNodes = (int) Math.pow(2, printDepth + 1);//定義possibleNodes表示可能的樹的結點數量多少,possibleNodes = 2 ^ (printDepth + 1)
        int countNodes = 0;

        nodes.addToRear(root);//把根結點添加到無序列表末尾
        Integer currentLevel = 0;
        Integer previousLevel = -1;//設置兩個int變量,爲接下來的換行操做進行判斷
        levelList.addToRear(currentLevel);//currentLevel添加到levelList末尾

        while (countNodes < possibleNodes) {
            countNodes = countNodes + 1;
            current = nodes.removeFirst();//current爲nodes移除的首位
            currentLevel = levelList.removeFirst();
            if (currentLevel > previousLevel) {
                result = result + "\n\n";//若是curreLevel大於previousLevel,就進行換行操做
                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();//將當前樹的根結點加到result串中
                nodes.addToRear(current.getLeft());
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(current.getRight());
                levelList.addToRear(currentLevel + 1);//將前一個根結點的左右孩子都存入nodes的尾部,curreLevel+1存入levelList表示左右孩子是在根結點的下一行
            } else {
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                result = result + " ";
            }

        }

        return result;
    }

我以爲這部分代碼難理解的地方在於爲了輸出時的圖像接近於咱們通常的樹的結構圖而進行的換行操做和添加空格的操做。
判斷某個結點在第幾層時運用了一個levelList的一個列表,以及兩個int變量currentLevel和previousLevel分別賦值爲0和-1,經過currentLevel和previousLevel的關係判斷是否須要換行,例如循環到第二層時,nodes裏有了左右孩子兩個結點,levelList裏首位和第二位此時存儲的都是1,而previousLevel此時爲0,首先循環時currentLevel = levelList.removeFirst,currentLevel=1>previousLevel=0,因此須要另起一行result = result + "\n\n",以後將previousLevel賦爲當前的currentLevel的值previousLevel = currentLevel;,current此時爲nodes首位即左孩子,加入result字符串,再將左孩子的左右孩子分別加入列表尾,它們對應的levelList裏的值爲currentLevel+1=2,以後將列表頭右孩子賦給current,此時的currLevel=previousLevel=1,因此走else這條路,不須要換行而後進行接下來的操做,若是右孩子爲葉結點,雖然沒有子結點,可是依然會留有存儲空間nodes.addToRear(current.getLeft());,只不過裏面存放的是null,因此輸出時計算機走到此處會繼續執行,並不斷建立新的存儲空間。因此在外面須要套一層while循環,由於二叉樹前n層有不高於2^(n+1)個元素,因此定義possibleNodes的值爲int possibleNodes = (int) Math.pow(2, printDepth + 1);
另外關於加空格的方法不是太懂,譬如爲什麼要這麼定義j < ((Math.pow(2, (printDepth - currentLevel))) - 1)在兩個結點之間添加空格。
關於二者的取值好像也不是那麼嚴格,也就是不必定非要取0和-1,換成1和0,-1和-2之類也是能夠的,只不過取值的不一樣會影響樹結構的寬窄,或者二者的差值也不必定須要爲1,差2差3好像都沒有問題,只不事後面levelList.addToRear(currentLevel + 1);的方法,對應就該加2或者加3了。數組

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

  • 問題1:運行expressionTree類的printTree方法時,樹的結構沒有所有打印出來,如圖
    數據結構

  • 問題1解決方案:debug一下,發現我尚未完成getHeight方法,getHeight方法那兒仍是return 0;天然表示上會出錯。返回height的方法我想應該分做三步,即判斷樹的結點分別是根結點、內部結點以及葉結點,首先判斷根結點是否爲空,不爲空高度加一,以後用一個while循環以及PP10.3的方法反覆判斷接下的結點是否爲內部結點,如果則高度加一,結點賦爲該結點的左孩子,若判斷出該結點爲葉結點,則高度加一再返回高度值。
BinaryTreeNode cur = root;
        int height = 0;
        if (root != null){
            height++;
        }
        cur = cur.left;
        while (cur.judge() != true){
            height++;
            cur = cur.left;
        }
        height++;
        return height;

代碼修改後的運行截圖爲
學習


  • 問題2:使用LinkedBinaryTree的toString方法時最終輸出一串亂碼

  • 問題2解決方案:在網上尋找解決方案,而後瞭解到數組是沒有toString方法的。

    Object中的toString()方法,是將傳入的參數的類型名和摘要(字符串的hashcode的十六進制編碼)返回,直接對數組使用了toString()方法,就會獲得 一個Ljava.lang.String;@175d6ab
    其中,Ljava.lang.String 是指數據是String類型的
    雖然這裏給出的亂碼信息是3ecf72fd,可是他給出的解決方案是使用循環的方法來輸出數組中的每個值,姑且一試
    這裏輸出樹的結構須要用到遞歸,而後輸出時有點奇怪的就是我把右孩子放在其雙親的上面,左孩子放在雙親的下面,因此會有些不像樹的形狀

public void oString() {
        string(root);
    }

private void string(BinaryTreeNode temp){
        if(temp != null){
            string(temp.getRight());
            System.out.println(temp.getElement() + " ");
            string(temp.getLeft());
        }
    }

輸出結果如圖

湊合也能看

代碼託管

上週考試錯題總結

上週沒有錯題哦

結對及互評

  • 博客中值得學習的或問題:
    • 譚鑫的博客問題記錄詳細,並結合了各家之長,把幾個優秀同窗的代碼都作了對比分析
    • 方藝雯的博客學習過程記錄清晰,關於樹的幾種遍歷方法用了圖片來說解,建議能夠加上老師課堂上補充的關於遍歷的知識
  • 基於評分標準,我給譚鑫的博客打分:8分。得分狀況以下:
    正確使用Markdown語法(加1分):
    模板中的要素齊全(加1分)
    教材學習中的問題和解決過程, 三個問題加3分
    代碼調試中的問題和解決過程, 三個問題加3分

  • 基於評分標準,我給方藝雯的博客打分:8分。得分狀況以下:、
    正確使用Markdown語法(加1分):
    模板中的要素齊全(加1分)
    教材學習中的問題和解決過程, 兩個問題加2分
    代碼調試中的問題和解決過程, 四個問題加4分

  • 本週結對學習狀況
  • 上週博客互評狀況

其餘

本週的學習狀況還算不錯,最困難的地方不在於幾個PP項目,反而在於問題中提到的那個printTree的代碼的理解。不過在考試中發揮得有點糟糕,不知道怎麼的錯了兩道不應錯的題

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 8/8
第二週 470/470 1/2 12/20
第三週 685/1155 2/4 10/30
第四周 2499/3654 2/6 12/42
第六週 1218/4872 2/8 10/52
第七週 590/5462 1/9 12/64

參考資料

相關文章
相關標籤/搜索