20172303 2018-2019-1《程序設計與數據結構》第7周學習總結

20172303 2018-2019-1《程序設計與數據結構》第7周學習總結

教材學習內容總結

本週在上週學習了二叉樹的基礎上,學習了一種二叉樹的特殊形式——二叉查找樹,又叫有序二叉樹、排序二叉樹。本章學習了兩種二叉查找樹的實現方法,以及兩種二叉查找樹的應用。html

1、概述

1.二叉查找樹

  • 概念:樹中的全部結點,其左孩子都小於父結點,父結點小於或等於其右孩子。
  • 性質:
    • 任意結點的左子樹不空,則左子樹上全部結點的值均小於它的根結點的值;
    • 任意結點的右子樹不空,則右子樹上全部結點的值均大於它的根結點的值;
    • 任意結點的左、右子樹也分別爲二叉查找樹;
    • 沒有元素相等的結點。

2.二叉查找樹ADT

  • 二叉查找樹的ADT是上一章中討論的二叉樹的擴展,其中的操做是二叉樹中已定義的那些操做的補充。
  • 二叉查找樹中的操做:
    • addElement:向樹中添加一個元素
    • removeElement:從樹中刪除一個元素
    • removeAllOccurrences:從樹中刪除所指定元素的任何存在
    • removeMin:刪除樹中的最小元素
    • removeMax:刪除樹中的最大元素
    • findMin:返回樹中的最小元素的引用
    • findMax:返回樹中的最大元素引用

2、二叉查找樹的實現

1.查找

  • 二叉查找樹的查找方法與二分查找相似,將所要查找的元素與根結點的元素進行比較,若是小於根結點則繼續與左孩子對比,大於根結點則繼續與右孩子對比,若是相等則返回元素。實現方法有迭代和遞歸兩種。
  • 迭代實現
public void find(T element)
{
    T result = null;
    BinaryTreeNode node = root;
    while (node != null)
    {
 
        if (element.CompareTo(node.getElement) > 0)
        {
            node = node.right;
        }
        else if (element.CompareTo(node.getElement) < 0)
        {
            node = node.left;
        }
        else
        {
            result = node.getElement;
            break;
        }
    }
    return result;
}
  • 遞歸實現
public void find(T element)
{
    return find(root, element);
}
 
private void find(BinaryTreeNode root, T element)
{
    if (root == null) {
        return element;
    }
    
    int comparable = element.CompareTo(root.getElement);
    if (comparable > 0){
        find(root.right,element);
    }
    else if (comparable < 0){
        find(root.left,element);
    }
    else {
        return root.getElement;
    }
}

2.插入

  • 進行插入的操做有三種狀況:
    • 若當前的二叉查找樹爲空,加入的元素會成爲根結點。
    • 若所插入結點的元素小於根結點的元素:
      • 若根的左孩子爲null,插入結點將會成爲新的左孩子。
      • 若根的左孩子不爲null,則會繼續對左子樹進行遍歷,遍歷的同時進行比較操做。
    • 若所插入結點的元素大於或等於根結點的元素
      • 若根的右孩子爲null,插入結點將會成爲新的右孩子。
      • 若根的右孩子不爲null,則會繼續對右子樹進行遍歷,遍歷的同時進行比較操做。

3.刪除

  • 二叉查找樹的刪除操做是全部操做中最爲複雜的,咱們先來考慮一種特殊狀況:所刪除的元素是樹中的最大值或最小值
(1)特殊狀況:所刪除元素爲樹中的最大值或最小值
  • 因爲二叉查找樹的特殊形式,其最小值通常位於樹的左子樹,最大值位於樹的右子樹。二者的刪除方法是相似的,惟一不一樣的地方就是「左」和「右」,下面咱們以刪除最大值爲例來講明,刪除最小值的狀況只要把例子中的「左」和「右」交換一下便可。
  • 刪除最大值有三種狀況:
    • 若根結點沒有右孩子,那麼根結點的元素就爲最大元素,原樹根的左孩子則會變成新的根結點。
    • 若最大值的結點是一個葉子結點,那麼直接將其父結點的右孩子的引用設置爲null便可。
    • 若最大值的結點是一箇中間結點,則須要設置其父結點的右孩子的引用爲該結點的左孩子。
(2)正常狀況
  • 正常狀況下刪除元素也有三種狀況,但這三種狀況就不是那麼簡單了。
  • 狀況一:所刪除的爲葉子結點
    • 這種狀況下能夠直接刪除該結點。不論該結點是根結點仍是普通的有父類的葉子結點,都直接將root或父結點與之鏈接的指針設置爲空便可。
  • 狀況二:所刪除的單支結點(即只有左子樹或右子樹)
    • 當刪除的結點是根結點時,將root指針指向被刪除結點的單支(左子樹或右子樹)
    • 當刪除的結點只有左子樹時,將所刪除結點的父結點的指針指向所刪除結點的左孩子。當刪除的結點只有右子樹時,將所刪除結點的父結點的指針指向所刪除結點的右孩子。

  • 狀況三:所刪除的結點既有左子樹又有右子樹
    • 這裏須要瞭解兩個概念——前驅結點後繼結點。分別是樹中小於它的最大值和大於它的最小值,若是把樹結構中的全部節點按順序排好的話,它的前驅和後繼兩個結點恰好在它的左右。當一個節點被刪除時,爲了保證二叉樹的結構不被破壞,要讓它的前驅結點或者後繼結點來代替它的位置,而後將它的前驅結點或者後繼結點作一樣的刪除操做。
    • 將當前結點與左子樹中最大的元素交換,而後刪除當前結點。左子樹最大的元素必定是葉子結點,交換後,當前結點即爲葉子結點,其刪除方式便可參考狀況一。還能夠將當前結點與右子樹中最小的元素交換,而後刪除當前結點。

3、應用——平衡二叉查找樹

1.使二叉查找樹平衡的方法

  • 會有兩種方式使二叉查找樹變得不平衡:插入元素或刪除元素。若是二叉查找樹不平衡,其效率可能比線性結構還要低。咱們的目標是保持樹的最大路徑長度接近log2n。爲了使樹達到平衡,咱們有四種方法:右旋、左旋、右左旋和左右旋。
  • 左旋和右旋
    • 左旋和右旋是針對不平衡因素是葉子結點的狀況,在這種狀況下,將根結點的左孩子/右孩子(與不平衡的子樹相反的一方)成爲新的根結點,而後使原樹根的左孩子的右孩子(右孩子的左孩子)成爲原樹根的新的右孩子(左孩子)

  • 右左旋和左右旋
    • 右左旋和左右旋是針對不平衡因素是內部結點的狀況,在這種狀況下,要先針對不平衡因素所在的子樹進行一次左旋/右旋(與不平衡的子樹相同的一方),而後再針對新獲得的樹根進行與以前相反的一次操做。

2.AVL樹

  • AVL樹是一種帶有平衡條件的二叉查找樹。這個平衡條件必須容易保持,並且它保證樹的深度必須是O(logn)。在一顆AVL樹中,其每一個結點的左子樹和右子樹的高度最多相差|1|(空樹的高度定義爲-1)。即平衡因子(右子樹高度-左子樹高度)最多爲|1|。
  • 對AVL樹進行插入操做可能會使其失去平衡的條件,但這能夠經過對樹進行簡單的修正來保持其平衡的屬性,這種操做就是咱們剛剛在上面所講的四種方法。
    • 結點的平衡因子大於等於+2,其左孩子的平衡因子爲-1——左旋
    • 結點的平衡因子小於等於-2,其右孩子的平衡因子爲+1——右旋
    • 結點的平衡因子爲+2,其右孩子的平衡因子爲-1——右左旋
    • 結點的平衡因子爲-2,其左孩子的平衡因子爲+1——左右旋

3.紅黑樹

  • 概念:紅黑樹是平衡二叉樹的另外一種實現,它的每一個結點都存儲一種顏色——紅色或者黑色。控制結點顏色的規則爲:
    • 根結點和葉子節點爲黑色。
    • 紅色結點的孩子都爲黑色。
    • 從樹根到葉子的每條路徑都包含相同數目的黑色結點。
紅黑樹的插入
  • 狀況一:當前結點的父結點是紅色,祖父結點的另外一個子結點(叔叔結點)是紅色。
    • 對策:將當前結點的父結點和叔叔結點變爲黑色,祖父結點變爲紅色,再把當前結點指向祖父結點,重新的當前結點從新開始算法。
  • 狀況二:當前結點的父結點是紅色,叔叔結點是黑色,當前結點是其父結點的右子樹
    • 對策:當前結點的結點作爲新的當前結點,針對新的當前結點進行一次左旋。
  • 狀況三:當前結點的父結點是紅色,叔叔結點是黑色,當前結點是其父結點的左子樹
    • 對策:將父結點變爲黑色,祖父結點變爲紅色,針對祖父結點進行一次右旋。
紅黑樹的刪除
  • 紅黑樹的操做較爲複雜,可是這篇博客中的介紹比較詳細具體,它把刪除紅黑樹的操做分紅了三類,第一類是刪除的結點爲葉子結點,第二類是刪除的結點有一個子樹(這裏博客中分紅了有左子樹和右子樹兩種狀況)第三類是刪除的結點既有左子樹又有右子樹。但其實紅黑樹的刪除操做不是難點,難點在於刪除以後的平衡處理,在這篇博客中講的很是詳細,我就再也不贅述了。

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

  • 問題1:遞歸和迭代的區別在哪裏?
  • 問題1解決方案:
    • 概念不一樣:程序調用自身的編程技巧稱爲遞歸,其實就是函數本身調用本身。迭代是指利用變量的原值推算出變量的一個新值,若是遞歸是本身調用本身的話,迭代就是A不停的調用B。
    • 使用的方法不一樣:迭代使用的是循環(for,while,do-while)或者迭代器,當循環條件不知足時退出。而遞歸通常是函數遞歸,能夠是自身調用自身,也能夠是非直接調用,即方法A調用方法B,而方法B反過來調用方法A,遞歸退出的條件爲if-else語句,當條件符合基的時候退出。
    • 對比:遞歸的代碼比較簡單易懂,實現一個計算邏輯每每只須要很短的代碼就能解決。可是因爲它要不停地調用函數,就可能浪費大量的空間。遞歸中函數調用的局部狀態是用棧來記錄的,因此若是遞歸太深的話還有可能致使堆棧溢出。而對於迭代而言,能使用遞歸實現的均可以使用迭代來實現,而且效率會很高,在空間消耗上也很小,惟一的缺點就是代碼比較難懂。
    • 總結:遞歸中必定有迭代,可是迭代中不必定有遞歸,大部分能夠相互轉換。能用迭代的不要用遞歸,遞歸調用函數不只浪費空間,若是遞歸太深的話還容易形成堆棧的溢出。
  • 問題2:在討論AVL樹的平衡化過程當中,爲何孩子結點的平衡因子不會爲+2或-2?
  • 問題2解決方法:平衡化操做在刪除或插入結點以後進行,在這一過程當中,平衡化從由於刪除或插入操做而改變的結點處開始,以後沿着路徑一直上溯到根結點爲止。在這一過程當中,會根據狀況進行旋轉,所以咱們永遠不會遇到父結點和孩子結點的平衡因子都爲|2|的狀況,由於在抵達父結點前孩子結點的平衡因子已經被修正過了。
  • 問題3:紅黑樹最長路徑遍歷的時間複雜度爲多少?
  • 問題3解決方法:由於紅黑樹「紅色結點的孩子必須都爲黑色」的性質,因此一顆紅黑樹中的任意一條路徑中,至多有一半的結點是紅色結點,至少有一半的結點是黑色結點,因此紅黑樹的最大深度爲2logn,所以紅黑樹最長路徑遍歷的時間複雜度爲O(logn)。

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

  • 問題1:在實現PP11.3時,雖然刪除/查找最大值和最小值都能實現,可是沒法把樹輸出
  • 問題1解決方法:拋出錯誤的提示說問題是出在LinkedBinaryTree裏面的getHeight方法中,可是我很是不理解爲何拋出的會是NullPointerException。因而去查了一下會拋出NullPointerException異常的緣由有哪些,通常有三種:字符串變量未初始化;接口類型的對象沒有用具體的類初始化;當一個對象的值爲空時,沒有判斷爲空。
  • 可是我又以爲這個異常的拋出好像都不是這三種,應該是由於所要輸出的樹不是滿樹的緣由,由於當時作課堂測試計算背部疼痛診斷器的高度時修改過getHeight的代碼,但背部疼痛診斷器是一顆滿樹,若是當我創建的樹不是一顆滿樹時,在gerHeight調用getLeft或者getRight的時候,必定會出現指針爲空的狀況,而後就會拋出異常,因此我就將gerHeight的方法改回來了。
public int getHeight()
    {
//        if (root == null){
//            return 0;
//        }
//        int leftChildHeight = getLeft().getHeight();
//        int rightChildHeght = getRight().getHeight();
//
//        return Math.max(leftChildHeight,rightChildHeght) + 1;
        int result = height(root);
        return result;
    }
  • 可是在修改以後再次測試時又出現了新的錯誤。
  • 不過這個錯誤就很好解決了,由於當初發現ExpreesionTree中的PrintTree方法能夠直接用做LinkedBinaryTree中的toString方法我就直接複製粘貼過來了,可是沒有改變相應的類型,在把全部的ExpreesionTreeOp類型改爲Integer類型後,程序就能完美地輸出樹了。

代碼託管

  • 上週代碼量:14440

上週考試錯題總結(正確爲綠色,錯誤爲紅色)

上週考試無錯題。java

結對及互評

點評模板:

  • 博客中值得學習的或問題:
    • 能夠明顯地感受出來個人結對夥伴對於課本內容的本身的理解部分愈來愈多了,雖然他對本身第五週的評分有些失落,可是我相信努力是必定會有收穫的!除此以外博客裏的每個「紅黑樹」真的都好俏皮啊哈哈哈哈,不過圖片的排版彷佛有問題?
  • 代碼中值得學習的或問題:
    • 代碼中的備註變多了,值得誇獎。

點評過的同窗博客和代碼

  • 本週結對學習狀況
    • 20172322
    • 結對學習內容
      • 討論了AVL樹的代碼實現。
      • 討論了書上P242圖11.16的錯誤。

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

  • 其實上週剛開始學樹的時候感受本身都是稀裏糊塗的,代碼也不是很懂會有不少錯誤,可是當時由於時間比較緊張因此都是囫圇吞棗作完的,可是這周在學習新的東西的時候發現有些上一章我糊弄過去的東西若是不搞懂搞明白的話是作不出來的,因此又去返工把以前的東西搞明白搞好,因此說學習仍是要踏實啊,真的一點都不能輕易放鬆。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 10/10 1/1 10/10
第二週 246/366 2/3 20/30
第三週 567/903 1/4 10/40
第四周 2346/3294 2/6 20/60
第五週 1343/4637 2/8 30/90
第六週 1343/4637 2/8 20/110
第七週 654/5291 1/9 25/135
  • 計劃學習時間:20小時
  • 實際學習時間:25小時
  • 改進狀況:上個星期學習的時候偶然查到了紅黑樹,當時沒仔細看還以爲挺好玩的,真正這個學期學起來才發現它有多恐怖...

參考資料

相關文章
相關標籤/搜索