20172310 2017-2018《程序設計與數據結構》(下)第七週學習總結

20172310 2017-2018《程序設計與數據結構》(下)第七週學習總結

教材學習內容總結

本章學習的是二叉查找樹


11.1 概述

  • 二叉查找樹(binay scarch tree)是種帶有附加屬性的二叉樹,即對樹中的每一個結點,其左孩子都要小於其父結點,而父結點又小於或等於其右孩子。
    html

  • 二叉查找樹的定義是上章中討論的二叉樹定義的擴展。所以,下面的操做是二叉樹中已定義的那些操做的補充。二叉查找樹和平衡二叉查找樹的接口是同樣的程序列表。java


11.2 用鏈表實現二叉查找樹

  • BinaryTreeNode在二叉查找樹中也用來表示樹中的每一個節點,每一個BinaryTreeNode對象要維護一個指向結點所存儲元素的引用,另外還要維護指向結點的每一個孩子的引用。

  • addElement 操做:addElement方法根據給定元素的值,在樹中的恰當位置添加該元素。
    添加元素位置的幾種狀況
    • 若是這個元素不是Comparable,則addElement方法會拋出NoComparableElementException異常。
    • 若是樹爲空,則這個新元素就將成爲根結點。
    • 若是樹非空,且它小於根結點中存儲的那個元素且根的左孩子爲null, 則這個新元素就將成爲根的左孩子。若是這個新元素小於根結點中存儲的那個元素且根的左孩子不是null, 則會遍歷根的左孩子,並再次進行比較操做。
    • 若是這個新元素大於或等於樹根存儲的那個元素且根的右孩子爲null, 則這個新元素會成爲根的右孩子。若是這個新元素大於成等於樹根處存儲的那個元素且根的右孩子不是null,則會遍歷根的右孩子,並再次進行比較操做

  • 一旦定義了但願構造的樹的類型和樹的使用方式,也就可以定義出該樹的接口和各類實現。

  • removeElement操做
    • removeElemcnt方法必須選出另外一個結點來代替要被刪除的那個結點。private方法replacement返回指向一個結點的引用,該結點將代替要刪除的結點。
      選擇替換結點的三種狀況以下:
    • 若是被刪除結點沒有孩子,則replacement返回null.
    • 若是被刪除結點只有一 個孩子, 則replacement返回這個孩子。
    • 若是被刪除結點有兩個孩子,則replacement 會返回中序後繼者(相等元素會放到右邊)。
      且這個後繼者不可能有左孩子節點,由於若是有左孩子節點則它的左孩子節點會成爲那個後繼者,而不是它,所以後繼者要麼沒有孩子節點,要麼只有右孩子。

二叉查找樹的最右側結點會存放最大元素,而其最左側結點會存放最小元素.node

  • removeMin操做
    按照二叉樹的定義,最小元素在二叉查找樹中只會存在於最左邊的位置,最左的位置分爲有3種可能情形:
    • 若是樹根沒有左孩子,則樹根就是最小元素,而樹根的右孩了會變成新的根結點。
    • 若是樹的最左側結點是片葉子, 則這片葉子就是最小元素, 這時只需設置其父結點的左孩子引用爲mull便可。
    • 若是樹的最左側結點是個內部結點,則須要設置其父結點的左孩 子引用指向這個將刪除結點的右孩子。
  • removeMax操做同理

11.3用有序列表實現二叉查找樹

  • 樹的主要使用之一就是爲其餘集合提供高效的實現。

  • BinarySearchTreeList實現的分析:BinarySearchTreeList 的實現是一種任何結點的最大深度爲log2(n)(其中n爲樹中存儲的元素數目)的平衡二叉查找樹。因此,add 操做和remove操做都要求從新平衡化樹。
  • 雖然樹實現中的有些操做更爲有效,好比removeLast、last 和contains:但在利用樹實現時,也有一些操做會變得低效,好比removeFirst和first。

11.4平衡二叉查找樹

  • 蛻化樹:如同鏈表通常,只有單邊的子樹。實際效率比鏈表還低。
  • 若是二叉查找樹不平衡,其效率可能比線性結構的還要低。git

  • 平衡二叉樹:任何一個節點的左右子樹深度差不超過1.經過這個限定,阻止了二叉樹的左右子樹深度差較大的狀況,維持了二叉樹的穩定。算法

  • 平衡化技術中的一些方法:
    • 右旋:節點插入在最小不平衡樹的右子樹的右子樹上面。 

      數據結構

    • 左旋:節點插入在最小不平衡節點的左子樹的左子樹上。

      函數

    • 右左旋:節點插入在最小不平衡樹的右子樹的左子樹上面。
      學習

    • 左右旋:節點插入在最小不平衡節點的左子樹的右子樹上面
      設計

    • 用法總結:從發生不平衡的結點起,沿剛纔回溯的路徑取直接下兩層的結點,若是這三個結點在一條直線上,則採用單旋轉進行平衡化,若是這三個結點位於一條折線上,則採用雙旋轉進行平衡化。如圖:

      3d

這是一篇很好的參考資料:數據結構——平衡二叉樹

  • 爲了解決上述二叉排序樹這種左右子樹深度由於插入或刪除結點而不均勻的狀況引入了兩種方式紅黑樹和AVL樹。

  • AVL樹:是一種平衡二叉樹的變體,其中最重要的一個概念是平衡因子:右子樹的高度減去作紫薯的高度稱爲該結點的平衡因子。其旋轉方式同上。
  • 紅黑樹:
    一、根節點是黑色。
    二、若是一個節點是紅色的,則它的子節點必須是黑色的。
    三、從樹根到樹葉的全部路徑上包含相同數目的黑色結點。
    四、每一個節點或者是黑色,或者是紅色。
    五、每一個空結點爲黑色。

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

  • 問題1:對課本removeElement方法的代碼理解有困難,主要是後面的刪除元素的圖給錯了,因而理解了半天。

  • 問題1解決方案:其實最重要的是replacement代碼段的理解,課本給出了三種狀況的分類,「若是被刪除結點有兩個孩子,則replacement 會返回中序後繼者」這種類型的時候中序後繼者是什麼意思呢?我結合着課本給出的刪除結點的示意圖來理解。

若是刪除的節點是10,它有左右孩子,中序遍歷是先查左孩子,再是該節點,而後是右孩子,中序遍歷查找的順序是7,10,13,15 。如今10是當前所指向的結點,因此從這個步驟開始,繼續接下去的遍歷,也就是看下一個查找的元素,而後返回它,也就是返回13 。

private BinaryTreeNode<T> replacement(BinaryTreeNode<T> node) 
    {
        BinaryTreeNode<T> result = null;
        // 若是被刪除結點沒有孩子
        if ((node.left == null) && (node.right == null))
            result = null;
        //被刪除的結點只有一個孩子,則返回孩子
        else if ((node.left != null) && (node.right == null))
            result = node.left;
        
        else if ((node.left == null) && (node.right != null))
            result = node.right;
        //被刪除結點有兩個孩子,就要找的比左邊孩子小,但比該結點大的孩子。
        else
        {
            BinaryTreeNode<T> current = node.right;//建立一個結點current,存放當前節點的右孩子
            BinaryTreeNode<T> parent = node;
            
            while (current.left != null)//中序遍歷找到下一個結點
            {
                parent = current;
                current = current.left;
            }
            
            current.left = node.left;//將被刪除結點的左孩子鏈到找到的這個結點的左邊

            if (node.right != current)//若是這個找到的節點不是原要被刪除的結點的右孩子
            {
                parent.left = current.right;//這時current.right爲null,parent.left從被找到的結點變爲null
                current.right = node.right;
            }
            
            result = current;
        }
        
        return result;
    }
  • 問題2:對課本中紅黑樹的操做徹底看不懂。
  • 問題2解決方案:看完了課本對於紅黑樹操做的講解,我不得不說,這些字我都認識,可放在一塊兒我怎麼就理解不了呢?(〃>皿<)
    對課本上給出的合法的紅黑樹的例子,我也表示懷疑,因此就去百度上找了一些資料來看看。
  • 紅黑樹中元素的插入:
    1.首先插入結點。插入的結點定爲紅色( 由於將插入的節點着色爲紅色,不會違背"從樹根到樹葉的全部路徑上包含相同數目的黑色結點。",少違背一條特性,就意味着咱們須要處理的狀況越少)。
    2.插入結點後的紅黑樹還是一棵二叉查找樹,可是插入後變得再也不平衡了。以後從新平衡化或者說從新着色的過程則是一種迭代的過程,因此插入節點後,咱們要作的就是將破壞紅黑樹規則的結點經過從新着色上移到別的結點。
    3.接下來是分狀況討論如何變色和旋轉,使其從新平衡。
    (1)其父節點爲black,這種狀況下沒有違背紅黑樹任何條件,直接插入便可(包含被插入的節點爲根節點的狀況);
    (2)插入的結點的父節點是red的狀況下,又可根據叔叔結點的case劃分爲三種狀況來處理;
    - case1:當叔叔結點也爲紅色時,第一,先將父節點和叔叔結點變爲black;第二,將祖父結點變爲red;最後運用遞歸繼續改變動底層的結點。

    - case2:當叔叔結點是black時,若是當前結點是父節點的左孩子,則將父節點染爲black,在把祖父結點染爲red,最後以祖父節點爲支點進行右旋。

    - case3:當叔叔結點是black時,而當前結點是父節點的右孩子,則以該插入的結點的父節點做爲當前節點進行左旋,變爲case2進行處理。

  • 紅黑樹中元素的刪除
    我以前已經總結過,在一棵平衡二叉樹要刪除一個結點有三種狀況。
    這三種狀況在紅黑樹中同樣,但咱們須要作的是將刪除後的二叉樹從新平衡和染色,如今,又可分爲如下幾種狀況:
    1)刪除的結點爲葉子節點,而後判斷該節點是否爲黑色的,若爲true,則在刪除後會致使黑高不相等。因而須要對紅黑樹進行調整。
    2)刪除的結點有一個孩子節點,此時直接使用其孩子節點代替z節點,而後判斷刪除的節點是否爲黑,若爲true,則對紅黑樹進行調整。
    3)刪除的結點有左右孩子節點,這時首先找到該節點的後繼節點,有兩種狀況:第一種狀況是y是z的右孩子節點,第二種狀況是y不是z的右孩子節點。

調整的方式:

  1. 當刪除狀況爲第一二種且結點爲black時,咱們能夠將替代原結點的節點x再加一種再額外增長一種黑色。(網上的資料都是這樣解釋的,可是而額外增長的究竟是怎麼回事我尚未研究透徹,若是弄懂了後續再修改╭(╯^╰)╮)

在第三種狀況中,經過使用z的後繼節點y替換z節點,而後使用y的右孩子x來填補y的位置。在此過程須要將節點y進行移動,因爲移動以後的y節點保持原來z節點的顏色,而x節點在代替y節點以後可能會出現問題,固然只會在y節點是黑色的狀況下才會出現問題,當y節點爲紅色時,移動時紅黑樹的性質不會被破壞,y節點爲黑色時,必定會出現黑高的不相等,而且也可能會出現兩個連續的紅色節點。這時須要對其進行下一步調整。

紅黑樹的刪除
這是關於刪除的一份資料,跟着這裏面看能夠理解不少,但本身仍是有些內容理解不了,主要是刪除比插入的狀況更多並且更復雜,理解起來仍是頗有難度的。

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

  • 問題1:

本身編寫AVL樹時,按照對左右旋和右左旋的理解編寫的代碼是這樣的,可是參考網上的資料並和結對夥伴討論發現,你們的代碼是

這是爲何呢?

  • 問題1解決方案:我發現其實本身的理解並無錯,左右旋確實是先左旋,再右旋,那麼問題在哪?你們廣泛都是根據網上的代碼來寫的,左、右旋是這樣的


但實際上左旋中的內容實際上是右旋時進行的,而右旋時的內容纔是左旋的,我使用了參考的左、右旋代碼,因而接下去本身寫左右旋和右左旋的代碼時出現了錯誤。
其實對這幾個操做的理解仍是正確的。

  • 問題2:

  • 問題2解決方案:我看到這裏出錯了,還覺得是旋轉操做的書寫又發生了錯誤,但是再從新解讀了一遍本身的代碼發現徹底沒有邏輯上的錯誤,因而又花了挺久的時間,還在同窗的幫助下發現本身真的是粗心,由於本身是仿照以前的代碼寫的,因此最開始定義的是一個node,而後如今須要一個element,結果構造函數中居然沒有寫,因而發生了這種錯誤。

代碼託管

(statistics.sh腳本的運行結果截圖)

上週考試錯題總結

  • 上週無錯題(ノ ̄▽ ̄)

結對及互評

點評:

  • 本週結對學習狀況
  • 博客和代碼中值得學習的或問題:

    • 看問題的角度很新穎,就像是咱們你們都在解決如何理解紅黑樹的插入和刪除操做時,個人隊友的想法倒是AVL樹和紅黑樹能夠達到相同的目的,那爲何要創造出紅黑樹呢,進而去了解這二者的優缺點,又讓我漲了知識,點贊。
    • 代碼問題很細緻,解答也挺詳細的。

點評過的同窗博客和代碼

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

這周的學習是創建在上週所學知識的基礎上的,果真基礎要打好,後面的學習纔會更有效率。不得不批評一下淘寶買來的課本了,太坑爹了,圖錯了好幾個,我一直研究,一直以爲不對,後來事實證實我是正確的,不過仍是浪費了一些時間在這個上面。雙週的課程比單週要少一些,因此這周自學Java的時間要多一些,果真知識都是靠時間換來的。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積)
目標 5000行 30篇 400小時
第一週 0/0 1/1 10/10
第二週 326/326 1/2 18/28
第三週 784/1110 1/3 25/53
第四周 2529/3638 2/5 37/90
第五週 1254/4892 2/7 20/110
第六週 1403/6295 2/9 32/142
第七週 1361/7656 1/10 35/177
  • 計劃學習時間:30小時

  • 實際學習時間:35小時

  • 改進狀況:這周在概念的理解上比以往花的時間要多。

參考資料

相關文章
相關標籤/搜索