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

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

教材學習內容總結

  • 概述
    • 二叉查找樹:二叉查找樹是一種含有附加屬性的二叉樹,即其左孩子小於父結點,而父結點又小於或等於右孩子。二叉查找樹的定義是二叉樹定義的擴展
    • 二叉查找樹的各類操做
操做 說明
addElement 往樹中添加一個元素 
removeElement 從樹中刪除一個元素 
removeAllOccurrences 從樹中刪除所指定元素的任何存在 
removeMin 刪除樹中的最小元素 
removeMax 刪除樹中的最大元素 
findMin 返回一個指向樹中最小元素的引用 
findMax 返回一個指向樹中最大元素的引用 
  • 用鏈表實現二叉查找樹:每一個BinaryTreeNode對象要維護一個指向結點所存儲元素的引用,另外還要維護指向結點的每一個孩子的引用。
  • addElement操做:咱們只要利用二叉查找樹的特性(即對每一個父結點,它的左子樹中全部項的值小於父結點中的值,而它的右子樹中全部項的值都大於T中的值),找到只對應的插入位置便可,若是樹爲空,則這個新元素就將成爲新結點,若是樹非空,沿着樹查找(根據element的大小來判斷向左仍是向右)。假如咱們如今要插入element爲4的結點,若是找到element(4),則什麼也不作,不然將element插入到遍歷的路徑上的最後一個點,以下圖所示:
  • removeElement操做:對於二叉查找樹來講,刪除元素的時候要考慮三種狀況:
    • ①若是要刪除的結點q剛好是葉子結點,那麼它能夠當即被刪除
    • ② 若是要刪除的結點q擁有一個孩子結點,則應該調整要被刪除的父結點(p.left 或 p.right)指向被刪除結點的孩子結點(q.left 或 q.right)
    • ③若是要刪除的結點q擁有兩個孩子結點,則刪除策略是用q的右子樹的最小的數據替代要被刪除結點的數據,並遞歸刪除用於替換的結點(此時該結點已爲空),此時二叉查找樹的結構並不會被打亂,其特性仍舊生效。採用這樣策略的主要緣由是右子樹的最小結點的數據替換要被刪除的結點後能夠知足維持二叉查找樹的結構和特性,又由於右子樹最小結點不可能有左孩子,刪除起來也相對簡單些。
  • removeAllOccurrences操做:能夠看作調用了removeElement,當在樹中找不到指定元素是,則拋出ElementNotFoundException異常,若是指定的元素不是Comparable,則removeAllOccurrences方法也會拋出ClassCaseException異常。只要樹中還含有目標元素,就會再次調用removeElement方法。
public void removeAllOccurrences(T targetElement)
            throws ElementNotFoundException
    {
        removeElement(targetElement);

        try
        {
            while (contains((T)targetElement))
                removeElement(targetElement);
        }

        catch (Exception ElementNotFoundException)
        {
        }
    }
  • removeMin和removeMax操做:對於findMin(),則須要從根結點開始而且只要有左孩子就向左進行便可,其終止點即爲最小值的元素;而對於findMax(),也須要從根結點開始而且只要有右孩子就向右進行便可,終止點即爲值最大的元素。
  • 用有序列表實現二叉查找樹:add操做和remove操做均可能致使樹變得不平衡。
操做 說明 LinkedList BinarySearchTreeList
removeFirst 刪除列表的首元素  O(1) O(logn)
removeLast 刪除列表的末元素  O(n) (logn)
remove 刪除列表中的一個特定元素 O(n) O(logn)*
first 考察列表前端的那個元素  O(1) O(logn)
last 考察列表末端的那個元素  O(n) O(logn)
contains 判斷列表是否含有一個特定元素  O(n) O(logn)
is Empty 斷定列表是否爲空  O(1) O(1)
size 斷定列表中的元素數目  O(1) O(1)
add(有序列表特有) 向列表添加一個元素  O(n) O(logn)*
  • 平衡二叉查找樹:若是沒有平衡假設,最壞狀況下addElement操做的實踐複雜性是O(n)而不是O(logn),由於可能存在樹根是樹中的最小元素,而將被插入的元素多是樹中的最大元素,這種狀況下,它的效率比鏈表的還低,由於每一個結點還附帶額外的開銷。
  • AVL樹的旋轉:當一棵樹的最大路徑長度大於log2^n,或最小路徑長度小於log2^n-1時,就要平衡化該樹。對於AVL樹,樹中的每個結點,咱們都會跟蹤其左右子樹的高度。對於樹中的任何結點,若是其平衡因子(左右子樹的高度差)大於1或小於-1,則以該節點爲樹根的子樹須要從新平衡。
  • 上左圖是一棵平衡二叉樹,它每一個結點的左子樹和右子樹的高度最多相差1,它同時也是一棵二叉查找樹,而上右圖雖然也是一棵二叉查找樹,可是它每一個結點的左子樹和右子樹的高度相差爲2,因此它不是平衡二叉樹。當引發結點數量變化時,即進行刪除和插入操做,以下圖,插入一個新的結點,本來的平衡二叉樹就失去了平衡。
  • 既然二叉樹失去了平衡,咱們就要使用適當的操做來使它恢復平衡。若是某結點的平衡因子爲-2,左孩子的平衡因子是-1,這就意味着該結點的左子樹中有一條過長的路徑,因此應該採用右旋。在原始AVL樹插入7結點後,結點9變爲失衡點,樹再知足AVL性質,所以須要對9結點進行左左單旋轉(即向右旋轉)後,獲得下右圖,咱們發現此時並無操做樹的根結點(6),實際上這是由於正常狀況下,沒必要從樹的根結點進行旋轉,而是從插入結點處開始,向上遍歷樹,並更新和修復在這個路徑上的每一個結點的平衡及其平衡信息(高度)便可。(左旋相似,當某結點的平衡因子是+2,右孩子的平衡因子是+1的時候使用左旋,左旋中的「左」,意味着「被旋轉的結點將變成一個左結點」。右旋同理)
  • 當某結點的平衡因子是+2,若是其右孩子的平衡因子是-1,這時子樹太「深」了,不管是左旋仍是右旋,都沒法使操做後的數成爲AVL樹,這個時候就須要使用雙旋,首先讓出初始結點右孩子的左孩子,繞着初始結點的右孩子進行一次右旋,而後再讓初始結點的右孩子,繞着初始結點進行一次左旋。(左右旋相似,當某結點的平衡因子是-2,左孩子的平衡因子是+1的時候使用左右旋)
  • 紅黑樹
    • 樹中的每個結點都儲存着一種顏色(紅色或黑色,一般使用一個布爾值來實現,值false等價於紅色)。
    • 根結點爲黑色。
    • 每一個葉子結點(null)是黑色。(**注意:這裏的葉子結點,是指爲空(null)的葉子結點!)
    • 從樹根到樹葉的每條路徑都包含有一樣數目的黑色結點。
    • 若是一個結點的顏色爲紅色,那麼它的子結點一定是黑色。
    • 在紅黑樹中,元素的查找仍然是一種O(n)操做,因爲紅色結點不能有紅色孩子,因而路徑中至多有一半結點時紅色結點、至少有一半結點是黑色結點,據此咱們能夠論證紅黑樹的最大高度約爲2*logn,因而遍歷最長路徑的序仍然是logn。
  • 紅黑樹的添加操做:紅黑樹自己就是一棵二叉查找樹,因此當添加元素或刪除元素後,咱們仍然須要使所獲得的是一棵二叉查找樹,這就使得咱們要對紅黑樹進行從新着色。咱們先來回頭看上面所說的紅黑樹的性質,若是要保證從樹根到樹葉的每條路徑都包含有一樣數目的黑色結點,那麼咱們把插入的元素設置爲紅色,就能夠保持,接下來咱們只需從該結點向上遍歷,保證紅色結點的子結點一定爲黑色便可知足紅黑樹的全部要求。咱們把要插入的結點設置爲紅色,那麼根據父結點的顏色又能夠分爲三種狀況。
    • ①被插入的結點是根結點。(咱們能夠直接將該結點塗成黑色)
    • ②被插入的結點的父結點是黑色。 (咱們無需進行操做,插入以後仍爲紅黑樹)
    • ③被插入的結點的父結點是紅色。(咱們對該種狀況要進行着重討論,由於被插入的結點的父結點是紅色,因此該結點的祖父結點一定存在(即父結點的父結點),父結點的兄弟結點也一定存在。(即「叔叔」結點,即便叔叔結點爲空,咱們也視之爲存在,空結點自己就是黑色結點)咱們根據叔叔結點的顏色又能夠分紅三種狀況)
狀況 處理方式
當前結點的父結點是紅色,且當前結點的祖父結點的另外一個子結點(叔叔結點)也是紅色。 ①將「父結點」設爲黑色。
② 將「叔叔結點」設爲黑色。
③ 將「祖父結點」設爲「紅色」。
④ 將「祖父結點」設爲「當前結點」(紅色結點);指針current由指向插入的結點變爲「當前結點「」,以後繼續對「當前結點」向上進行操做。
當前結點的父結點是紅色,叔叔結點是黑色,且當前結點是其父結點的右孩子 ①將「父結點」做爲「新的當前結點」。
②以「新的當前結點」爲支點進行左旋。
當前結點的父結點是紅色,叔叔結點是黑色,且當前結點是其父結點的左孩子 ① 將「父節點」設爲「黑色」。
②將「祖父節點」設爲「紅色」。
③以「祖父結點」爲支點進行右旋。
  • 如上圖介紹瞭如何進行操做,下面咱們再來談談爲何要這麼操做。
    • 第一種狀況,因爲紅色結點的子結點不能是紅色,因此咱們把父結點要變爲黑色,但當咱們把父結點變爲黑色之後,從樹根到樹葉之間的黑色結點的個數就不相等了,因此把祖父結點變爲紅色,一樣的,由於紅色結點的子孩子不能是紅色,因此要把叔叔結點變爲黑色。祖父結點必定是黑色嗎?答案是確定的,由於在元素添加以前,該二叉樹就是紅黑樹,父結點是紅色的,那麼祖父結點必定是黑色的。按照上述步驟處理以後,當前結點,父結點,叔叔結點都知足了紅黑樹的性質。若此時,祖父結點是根結點,直接將祖父結點設爲「黑色」,那就徹底解決這個問題了;若祖父結點不是根結點,那咱們須要將「祖父結點」設爲「新的當前結點」,接着對「新的當前結點」進行分析。
    • 第二種狀況,咱們在上面表中說到,要以父結點爲支點進行左旋,那麼爲何要進行左旋呢?處理紅黑樹的核心思想:將紅色的結點移到根節點;而後,將根結點設爲黑色。新插入的結點爲紅色,破壞了整棵紅黑樹,因此咱們要經過左旋來將它上移。上移以後,若是該結點變成了根結點,那麼直接把它塗成黑色,若該結點不是 根結點,那麼咱們須要對父結點進行操做(在下圖中也就是40) ,爲何不直接對60的當前結點進行操做,而是轉而處理原來的父結點40呢?由於經過左旋以後,原來的父結點(40)變成了子結點(60)的子結點,而處理紅黑樹的時候須要從下往上處理,因此要先對40的結點操做。
    • 第三種狀況,當按照上圖第二種狀況左旋後,就變成了下面這種狀況。因爲(40)和(60)兩個結點都是紅色,因此咱們能夠先把(60)結點變爲黑色,但變爲黑色的話,由根結點通過(60)結點的路徑黑色結點數就會增長,因此咱們可讓(60)的父結點(即(80))變爲紅色,並以該父結點做爲支點進行右旋。
  • 紅黑樹的刪除操做:紅黑樹的刪除和常規二叉樹刪除元素的操做同樣,也分爲三種狀況。
    • ①被刪除的結點沒有子結點(即葉子節點),直接將該結點刪除。
    • ②被刪除的結點有一個子結點,將該結點刪除,並讓父結點指向該結點的子結點。(能夠看前文的示意圖)
    • ③被刪除的結點有兩個子結點,用該結點的右子樹的最小的數據替代要被刪除結點的數據,並遞歸刪除用於替換的結點。(前文有,在此再也不過多贅述)
  • 在刪除結點的時候,咱們還要考慮到結點的顏色問題,當刪除的結點顏色是紅色時,直接按照上述操做拿孩子結點填補空位便可,由於刪除紅色結點不會影響黑色結點的數量。若是要刪除的結點的顏色爲黑色,爲了保持黑色結點數量的一致,若是該結點的孩子結點爲紅色,那麼把孩子結點變爲紅色並拿來填補空位便可。若是孩子結點爲黑色,狀況就複雜得多,咱們在下面再細細探討。在這裏先來統一一下下文字母表明的含義。這裏假設最終被刪除的節點爲X(至多隻有一個孩子節點,能夠有兩個,但演示的時候不必),其孩子結點爲N,X的兄弟結點爲S,S的左結點爲 SL,右結點爲 SR。接下來討論是創建在結點 X 被刪除,結點 N 替換X的基礎上進行的。
    • ①要刪除的結點X爲根結點,且無左右孩子結點,直接刪除。
    • ②S結點爲紅色,其它結點爲黑色,這種狀況下能夠對 N 的父節點進行左旋操做,而後互換 P 與 S 顏色。但通過節點 P 和 N 的路徑刪除前有3個黑色節點(P -> X -> N),如今只剩兩個了(P -> N)。比未通過 N 的路徑少一個黑色節點,此時從根節點到各路徑的黑色結點數量不一樣,而後可使用下面4、5、六種狀況進行處理。
    • ③N 的父結點,兄弟結點 S 和 S 的孩子結點均爲黑色。這種狀況下能夠簡單的把 S 染成紅色,全部通過 S 的路徑比以前少了一個黑色結點,這樣通過 N 的路徑和通過 S 的路徑黑色結點數量一致了。但通過 P 的路徑比不通過 P 的路徑少一個黑色結點,此時須要從狀況一開始對 P 進行平衡處理。
    • ④N 的父節點是紅色,S 和 S 孩子爲黑色。這種狀況比較簡單,咱們只需交換 P 和 S 顏色便可。這樣全部經過 N 的路徑上增長了一個黑色節點,全部經過 S 的節點的路徑必然也經過 P 節點,因爲 P 與 S 只是互換顏色,並不影響這些路徑。
    • ⑤S 爲黑色,S 的左孩子爲紅色,右孩子爲黑色。N 的父節點顏色可紅可黑,且 N 是 P 左孩子。這種狀況下對 S 進行右旋操做,並互換 S 和 SL 的顏色。此時,全部路徑上的黑色數量仍然相等,N 兄弟節點的由 S 變爲了 SL,而 SL 的右孩子變爲紅色。接下來咱們到狀況六繼續分析。
    • ⑥S 爲黑色,S 的右孩子爲紅色。N 的父節點顏色可紅可黑,且 N 是其父節點左孩子。這種狀況下,咱們對 P 進行左旋操做,並互換 P 和 S 的顏色,並將 SR 變爲黑色。由於 P 變爲黑色,因此通過 N 的路徑多了一個黑色節點,通過 N 的路徑上的黑色節點與刪除前的數量一致。對於不通過 N 的路徑,則有如下兩種狀況:
      • 該路徑通過 N 新的兄弟節點 SL ,那它以前必然通過 S 和 P。而 S 和 P 如今只是交換顏色,對於通過 SL 的路徑不影響。
      • 該路徑通過 N 新的叔叔節點 SR,那它以前必然通過 P、 S 和 SR,而如今它只通過 S 和 SR。在對 P 進行左旋,並與 S 換色後,通過 SR 的路徑少了一個黑色節點,性質5被打破。另外,因爲 S 的顏色可紅可黑,若是 S 是紅色的話,會與 SR 造成連續的紅色節點,打破性質4(每一個紅色節點必須有兩個黑色的子節點)。此時僅需將 SR 由紅色變爲黑色便可同時恢復性質4和性質5(從任一節點到其每一個葉子的全部簡單路徑都包含相同數目的黑色節點。)。

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

  • 問題一:在學習教材的時候,p225的代碼中有這樣一行判斷條件「(!(element instanceof Comparable))」,不是很理解這行代碼的意思。
  • 問題一解決方法:原來是學習過的,時間久了有些忘記。java 中的instanceof 運算符是用來在運行時指出對象是不是特定類的一個實例。instanceof經過返回一個布爾值來指出,這個對象是不是這個特定類或者是它的子類的一個實例。用法:result = object instanceof class參數:Result:布爾類型。Object:必選項。任意對象表達式。Class:必選項。任意已定義的對象類。說明:若是 object 是 class 的一個實例,則 instanceof 運算符返回 true。若是 object 不是指定類的一個實例,或者 object 是 null,則返回 false。 舉例:
interface A{}
    class B implements A{

    }
    class C extends B {

    }

    class instanceoftest {
        public static void main(String[] args){
            A a=null;
            B b=null;
            boolean res;

            System.out.println("instanceoftest test case 1: ------------------");
            res = a instanceof A;
            System.out.println("a instanceof A: " + res);

            res = b instanceof B;
            System.out.println("b instanceof B: " + res);

            System.out.println("/ninstanceoftest test case 2: ------------------");
            a=new B();
            b=new B();

            res = a instanceof A;
            System.out.println("a instanceof A: " + res);

            res = a instanceof B;
            System.out.println("a instanceof B: " + res);

            res = b instanceof A;
            System.out.println("b instanceof A: " + res);

            res = b instanceof B;
            System.out.println("b instanceof B: " + res);

            System.out.println("/ninstanceoftest test case 3: ------------------");
            B b2=(C)new C();

            res = b2 instanceof A;
            System.out.println("b2 instanceof A: " + res);

            res = b2 instanceof B;
            System.out.println("b2 instanceof B: " + res);

            res = b2 instanceof C;
            System.out.println("b2 instanceof C: " + res);
        }
    }

  • 問題二:對於紅黑樹的添加和刪除原理不是很理解。
  • 問題二解決方法:在上方教材總結中體現。

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

  • 問題一:在編寫pp的時候,爲了比較兩個對象的大小,時常須要在類的頭部聲明「T extends Comparable」,那麼代碼泛型中T繼承的Comparable究竟是整個方法是Comparable類型仍是其中的變量是Comparable類型。
  • 問題一解決方法:
    • 你們能夠明白的是這裏應用到了Java的泛型,那麼首先向你們說明一下這裏extends的做用extends後面跟的類型,如 <任意字符 extends 類 接口> 表示泛型的上限。示例代碼以下:
import java.util.*;
class Demo<T extends List>{}
public class Test
{
   public static void main(String[] args) {
    Demo<ArrayList> p = null; // 編譯正確
//這裏由於ArrayList是List的子類因此經過
//若是改成Demo<Collection> p = null;就會報錯這樣就限制了上限
    }
}
  • 在理解了extends所表示的泛型的上限後,接下來介紹一下super的做用,它與extends相反,表示的是泛型的下限。因此結合上述兩點,咱們來分析一下這句話總體表明什麼意思。首先,extends對泛型上限進行了限制即T必須是Comparable<? super T>的子類,而後<? super T>表示Comparable<>中的類型下限爲T!那麼 <T extends Comparable > 和 <T extends Comparable<? super T>> 到底有什麼不一樣呢?
  • 咱們首先來理解<T extends Comparable >的含義,它表明的意思是:類型T必須實現Comparable接口,而且這個接口的類型是T。這樣,T的實例之間才能相互比較大小。這邊咱們以Java中GregorianCalendar這個類爲例。代碼以下所示:
import java.util.GregorianCalendar;
class Demo<T extends Comparable<T>>{}
//注意這裏是沒有? super的
public class Test
{
   public static void main(String[] args) {
      Demo<GregorianCalendar> p = null;
        }
}
  • 這裏編譯報錯,由於這裏的<T extends Comparable >至關於<GregorianCalendar extends Comparable >,可是GregorianCalendar中並無實現Comparable ,而是僅僅持有從Calendar繼承過來的Comparable ,這樣就會由於不在限制範圍內而報錯。
  • 咱們接下來再來看看<T extends Comparable<? super T>>的含義。它表明的意思是:類型T必須實現Comparable接口,而且這個接口的類型是T或者是T的任一父類。這樣聲明後,T的實例之間和T的父類的實例之間能夠相互比較大小。一樣仍是以GregorianCalendar爲例。代碼以下所示:

import java.util.GregorianCalendar; class Demo<T extends Comparable<? super T>>{} public class Test1 { public static void main(String[] args) { Demo<GregorianCalendar> p = null; // 編譯正確 } }    html

  • 此時編譯經過,這裏能夠理解爲<GregorianCalendar extends Comparable >!由於Calendar爲GregorianCalendar 的父類而且GregorianCalendar 實現了Comparable >。
  • 問題二:在學習紅黑樹的時候,書上提到終止迭代的條件也能夠是「current.parent.color == black」,子樹的結點不也能夠是黑色的嗎?爲何判斷當前結點的父結點的顏色就能夠終止迭代了呢?
  • 問題二解決方法:這裏是我沒有好好理解紅黑樹,對書上的解釋沒有弄明白。由於插入的結點爲紅色,要根據父結點和叔叔結點的顏色進行操做並自下而上進行迭代,噹噹前結點的父結點爲紅色時,這時候整個二叉樹已經知足紅黑樹的全部特性,因此能夠不在進行操做。(具體操做在前文中有細緻體現)

代碼託管

上週考試錯題總結

  這周沒有錯題哦~前端

結對及互評

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

點評過的同窗博客和代碼

  • 本週結對學習狀況

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

  這周的內容仍是比較可貴,不難在代碼上,難在內容的理解和理清它們之間的關係,根據不一樣的狀況進行不一樣的操做,上週因爲剛剛接觸學習樹,因此在代碼的理解上花費很多時間,這周主要是上網查詢了很多資料來弄明白各類狀況。但這周的學習時間並無減小,相反,我以爲比上週還要吃力,但學習完以後仍是明顯可以感受到本身的進步的,但願能在之後的學習生活中繼續努力,繼續進步吧!java

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
第一週 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
第七週 2071/10795 1/7 20/105
  • 計劃學習時間:20小時git

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

參考資料

相關文章
相關標籤/搜索