日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

真c++ 从二叉树到红黑树(6)之红黑树RedBlack

發(fā)布時間:2023/12/8 c/c++ 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 真c++ 从二叉树到红黑树(6)之红黑树RedBlack 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

??此文章為從二叉樹到紅黑樹系列文章的第六節(jié),主要介紹介紹紅黑樹,相信,有了之前BST,AVL和B樹的鋪墊,你會很快地理解紅黑樹。但紅黑樹的情況也十分復雜,因此,推薦分兩天來看紅黑樹。一天看插入,一天看刪除。


文章目錄

  • 一、所有文章鏈接~(點擊右邊波浪線可以返回目錄)
    • 理解紅黑樹之前,需要了解的知識點:~
  • 二、引入紅黑樹~
  • 三、紅黑樹的性質~
    • 1.紅黑樹的外部節(jié)點~
    • 2.紅黑樹的性質~
      • 紅黑樹性質解讀~
    • 3.紅黑樹的適度平衡~
    • 4.紅黑樹與B樹(2,4)樹的關系(提升變換)~
      • 提升變換的四種組合~
  • 四、紅黑樹類~
    • (一)定義變量和接口~
      • 1.利用已有的變量~
      • 2.需要的接口~
      • 3.重要輔助函數(shù)~
        • (1)重寫高度更新算法~
        • (2)雙紅,雙黑缺陷~
      • 4.類內輔助靜態(tài)函數(shù)~
      • 5.RedBlack.h~
    • (二)高度更新~
      • 高度更新代碼~
    • (三)紅黑樹的插入代碼~
    • (四)雙紅修復~
      • 1) u為黑色~
        • a) LL型~
        • b) RR型~
        • c) LR型~
        • d) RL型~
      • 2) u為紅色~
        • a) LL型~
        • b) RR型~
        • c) LR型~
        • d) RL型~
      • 3) 求當前節(jié)點的叔叔代碼~
      • 4) 雙紅修復代碼遞歸版~
      • 5) 雙紅修復代碼迭代版~
      • 6) 雙紅修復的復雜度~
    • (五)紅黑樹的刪除~
      • 1.再探removeAt語義~
        • (1)removeAt的第一種和第二種情況~
        • (2)removeAt的第三種情況~
        • (3)結論~
        • (4)removeAt其他細節(jié)~
      • 2.紅黑樹的刪除~
        • (1)刪除情況分析~
        • (2)刪除代碼~
        • (3)判斷黑高度是否平衡代碼~
    • (六)雙黑缺陷~
      • (1)s為黑,其至少有一個紅孩子~
      • (2)s為黑,s的兩個孩子為黑,p為紅~
      • (3)s為黑,s的兩個孩子為黑,p為黑~
      • (4)s為紅,s的兩個孩子必然為黑,p必然為黑~
      • (5)雙黑修復遞歸版~
      • (6)雙黑修復迭代版~
      • (7)雙黑修復復雜度~
  • 五、完整RedBlack.h~
  • 六、紅黑樹測試~
    • 1.插入測試代碼~
    • 2.插入測試圖示~
    • 3.刪除測試代碼~
    • 4.刪除測試圖示~
  • 七、結語~

一、所有文章鏈接~(點擊右邊波浪線可以返回目錄)

??在閱讀本文前,強烈建議你看下前面的文章的目錄、前言以及基本介紹,否則你無法理解后面的內容。鏈接如下:

  • 基本二叉樹節(jié)點,通用函數(shù) 二叉樹節(jié)點
  • 基本二叉樹類的定義和實現(xiàn) 二叉樹基類
  • BST(二叉搜索樹的實現(xiàn)) BST
  • AVL(二叉平衡搜索樹的實現(xiàn))AVL
  • B樹的實現(xiàn)(如果你只想了解B樹,可以跳過所有章節(jié),直接看B樹,B樹的理解是紅黑樹的基礎)B樹
  • 紅黑樹的實現(xiàn) RedBlack
  • 理解紅黑樹之前,需要了解的知識點:~

  • 在本系列文章第三部分BST中的刪除的基本原理
  • 在本系列文章第四部分AVL中的connect34和rotateAt的基本原理
  • 在本系列文章第五部分B樹中的插入和刪除,上溢和下溢的基本原理
  • 如果你還不了解,那么看接下來的內容,你可能會有點吃力。


  • 二、引入紅黑樹~

    ??AVL樹盡管可以保證最壞情況下的單次操作速度,但需在節(jié)點中嵌入平衡因子等標識;更重要的是,刪除操作之后的重平衡可能需做多達?log2n?次旋轉,從而頻繁地導致全樹整體拓撲結構的大幅度變化。

    ??紅黑樹即是針對后一不足的改進。通過為節(jié)點指定顏色,并巧妙地動態(tài)調整,紅黑樹可保證:在每次插入或刪除操作之后的重平衡過程中,全樹拓撲結構的更新僅涉及常數(shù)個節(jié)點。



    三、紅黑樹的性質~

    1.紅黑樹的外部節(jié)點~

    ??一棵樹,所有葉節(jié)點都有空孩子指針,因此,為了方便理解,可以將這些空孩子指針全部視為外部節(jié)點。即假想地加入外部節(jié)點(實際并沒有加入),使得樹中任何節(jié)點都可以視為有左右孩子。

    ??下面是一顆紅黑樹

    ??若按1中給這顆樹增加外部節(jié)點,就可以得到

    2.紅黑樹的性質~

    ??提示:如果你看紅黑樹的算法,感到某些地方不好理解時,不妨來看看紅黑樹的性質,你就會明白算法為什么要這么設計。

    由紅、黑兩色節(jié)點組成的二叉搜索樹若滿足以下條件,即為紅黑樹

    (1) 樹根始終為黑色
    (2) 外部節(jié)點均為黑色(NULL LEAF)(假想,實際不存在)
    (3) 其余節(jié)點若為紅色,則其孩子節(jié)點必為黑色,反之,其父親也必然為黑色。
    (4) 從根節(jié)點到任一外部節(jié)點的沿途,黑節(jié)點的數(shù)目相等(黑深度相等)

    紅黑樹性質解讀~

  • 由條件(1)(2)可知,紅節(jié)點必然為內部節(jié)點。
  • 由條件(3)可知紅節(jié)點的孩子和父親必然為黑色。即樹中任何一條通路中絕對不可能有相鄰的紅節(jié)點。
  • 由以上兩個分析可知,在從根節(jié)點通往任一節(jié)點的沿途,黑節(jié)點都不少于紅節(jié)點
  • 從根節(jié)點到任意節(jié)點所經的黑節(jié)點數(shù)目稱為該節(jié)點的黑深度(由上往下)。(根節(jié)點黑深度為0)。由條件(4)可知,所有外部節(jié)點的黑深度必然相等
  • 從外部節(jié)點到內部任意節(jié)點,所經的黑節(jié)點的個數(shù)的最大值,稱之為這個內部節(jié)點的黑高度(由下往上)。因此,外部節(jié)點的黑高度為0,根節(jié)點的黑高度等于外部節(jié)點的黑深度。
  • 由以上可以得知,任意一個節(jié)點,其左右子樹的黑高度都必然相等。
  • 3.紅黑樹的適度平衡~

    ??由2中的紅黑樹的性質解讀的第三條,可以得知

    在從根節(jié)點通往任一節(jié)點的沿途,黑節(jié)點都不少于紅節(jié)點。

    ??而一棵樹,就是由紅節(jié)點和黑節(jié)點組成,這樣就代表,黑節(jié)點的數(shù)目,至少比全樹所有節(jié)點的數(shù)目的一半大。而這一點,恰恰就是紅黑樹適度平衡的條件。

    TB為黑高度(H),T為全樹的高度(h)。

    更嚴格的有l(wèi)og2(n + 1) <= h <=2?log2(n + 1)(證明略)

    ??盡管紅黑樹不能如完全樹那樣可做到理想平衡,也不如AVL樹那樣可做到較嚴格的適度平衡,但其高度仍控制在最小高度的兩倍以內,從漸進的角度看仍是O(logn),依然保證了適度平衡—這正是紅黑樹可高效率支持各種操作的基礎。

    4.紅黑樹與B樹(2,4)樹的關系(提升變換)~

    ??往下看之前,建議你理解一下B樹的上溢和下溢。不懂的就看看本系列文章的第五部分,我對B樹進行了詳解。

    ??在后面就可以得知,經適當轉換之后,紅黑樹和(2,4)樹相互等價!
    ??具體地,自頂而下逐層考查紅黑樹各節(jié)點。每遇到一個紅節(jié)點,都將對應的子樹整體提升一層,從而與其父節(jié)點(必黑)水平對齊,二者之間的聯(lián)邊則相應地調整為橫向。
    ???????????????
    ??由紅黑樹的性質(3)可得,對于有紅孩子的黑節(jié)點而言,提升過程中,所涉及的節(jié)點至多不超過3個(可能為2個,當只有一個紅孩子時),因為其最多只有兩個紅孩子,而對應的紅孩子必然只有黑孫子,沒有紅孫子。

    ??因此由變換之后的結果可以觀察到,可以把變換之后的3個節(jié)點(或2個節(jié)點)看做一個整體,其恰好可以構成4階B樹(3個關鍵碼)中的一個節(jié)點。因此,變換之后,每一顆紅黑樹都對應一顆(2,4)樹

    提升變換的四種組合~

    1、 通往黑節(jié)點的邊對紅黑樹的黑高度有貢獻,以實線表示,保留下來。
    2、 通往紅節(jié)點的邊對紅黑樹的黑高度沒有貢獻,以虛線表示,不予保留。

    下圖中,上方是紅黑樹,下方是對應的B樹。


    ??從上圖可以看出,對應的(2,4)B樹。每個節(jié)點有且僅有一個黑色的關鍵碼,同時紅色的關鍵碼不超過兩個,若某個節(jié)點果真包含兩個紅關鍵碼,則黑關鍵碼的位置必然居中。



    四、紅黑樹類~

    (一)定義變量和接口~

    1.利用已有的變量~

    在第一部分定義二叉樹節(jié)點的時候,我們定義了一個

    RBColor _color;//紅黑樹專用

    這個枚舉類,主要是用于表示紅黑樹的顏色信息。具體為

    namespace {enum class RBColor { RED, BLACK }; }

    并且同樣,我們會用到在BST定義的_hot節(jié)點

    BinNodePtr _hot;//"命中節(jié)點"的"父親"

    2.需要的接口~

    ??由于在BST中,我們已經定義了查找search算法,因此,不需要給RedBlack重新寫查找算法,只需要對插入和刪除算法進行重寫既可(并且在后面可以發(fā)現(xiàn),其插入和刪除的本質,跟BST和AVL一模一樣!)。并且在BinTree中,我們也定義了遍歷算法,因此,也沿用即可。

    在樹中插入一個節(jié)點insert 在樹中刪除一個節(jié)點remove

    3.重要輔助函數(shù)~

    (1)重寫高度更新算法~

    ??由于紅黑樹的高度的表示方式為黑高度,所以其高度更新的算法也需要進行重寫

    更新高度updateHeight

    (2)雙紅,雙黑缺陷~

    ??這兩個輔助函數(shù),正是紅黑樹得以保持平衡的最主要原因。在接下來介紹插入時,會解釋雙紅缺陷,在介紹刪除時,會解釋雙黑缺陷。

    solveDoubleRed解決雙紅缺陷 solveDoubleBlack解決雙黑缺陷

    4.類內輔助靜態(tài)函數(shù)~

    ??為了加快算法執(zhí)行的效率,和方便理解,在紅黑樹類內定義了4個靜態(tài)內聯(lián)函數(shù)。前兩個很好理解,后面兩個在介紹插入和刪除算法時會進行解釋。

    IsBlack//判黑//當然x為空,也為黑色 IsRed//非黑即紅IsBlackHeightBalanced//判斷是否需要更新黑高度 uncle//獲取當前節(jié)點的叔叔

    5.RedBlack.h~

    template<typename T=int> class RedBlack :public BST<T> { protected:using BinNodePtr = BinNode<T>*;protected:void solveDoubleRed(BinNode<T>* x);//雙紅修正void solveDoubleBlack(BinNode<T>* replacer);//雙黑修正constexpr int updateHeight(BinNode<T>* x)const override;//更新高度public:BinNode<T>* insert(const T& data)override;//插入重寫bool remove(const T& data)override;//刪除重寫/*查找沿用BST的查找*//*遍歷沿用BinTree的遍歷*/protected:static constexpr bool IsBlack(const BinNodePtr& x) {//判黑//當然x為空,也為黑色return ((!x) || (RBColor::BLACK == x->_color));}static constexpr bool IsRed(const BinNodePtr& x) {//非黑即紅return !IsBlack(x);}static constexpr bool IsBlackHeightBalanced(const BinNodePtr& x) {//判斷是否需要更新黑高度bool is_L_C_Equal = (stature(x->_lchild) == stature(x->_rchild));int rbHeight = (IsRed(x) ? stature(x->_lchild) : stature(x->_lchild) + 1);//對于rbHeight的計算而言,取左孩子還是右孩子,均一樣bool is_Height_Equal = (x->_height == rbHeight);return is_L_C_Equal && is_Height_Equal;//只要有一個為假,即為假//所以只要左孩子和右孩子高度相等,或者x沒有高度變化,就黑高度平衡 }static inline BinNodePtr uncle(const BinNodePtr& x) {/*獲取x的叔叔*/return IsLChild(x->_parent) ? x->_parent->_parent->_rchild : x->_parent->_parent->_lchild;}};//class RedBlack

    (二)高度更新~

    ??下面是我們在本系列文章第一部分定義的獲取當前節(jié)點高度的全局靜態(tài)函數(shù)。并且我們規(guī)定當沒有節(jié)點時,高度為-1,當有一個節(jié)點時,高度為0(見第一部分關于樹的語義規(guī)定中樹的高度的定義)。此規(guī)定依然適用于紅黑樹,也就是說,哪怕紅黑樹此時只有一個根節(jié)點(必然為黑),其高度為0而不是1。
    ??此規(guī)定,對于后序紅黑樹的平衡不造成任何影響,但若讀者要獲取紅黑樹的高度的話,就需要明白此時的黑高度,比實際的黑高度少1。

    template<typename BinNodePtr> static constexpr int stature(const BinNodePtr& x) {//獲取高度return x ? x->_height : -1;//空指針高度為-1 }

    高度更新代碼~

    template<typename T> constexpr int RedBlack<T>::updateHeight(BinNode<T>* x) const//由于stature視空節(jié)點高度為-1,所以height會比黑高度少一 {x->_height = std::max(stature(x->_lchild), stature(x->_rchild));//孩子一般黑高度相等,除非出現(xiàn)雙黑return IsBlack(x) ? x->_height++ : x->_height;//若當前節(jié)點為黑,則計入黑高度 }

    由于重寫了更新高度函數(shù),所以此時x的高度,為黑高度。
    要更新紅黑樹的高度(即黑高度),只有當:

    (1)左右孩子黑高度不相等。 (2)x為黑節(jié)點時,其高度要加1。 (3)x為紅節(jié)點時,其高度不需要額外更新。

    (三)紅黑樹的插入代碼~

    ??紅黑樹的插入算法,與BST,AVL的基本插入方式一模一樣,唯一不同的是后續(xù)要進行雙紅修復。

    ??先用BST的search確定不存在這個節(jié)點,并且更新_hot的位置,并以_hot為父親,創(chuàng)建一個新節(jié)點。并將其黑高度更新為-1,以及染色成紅色(我們默認新加入的節(jié)點均為紅色節(jié)點,除非新加的是根節(jié)點)。

    ??由BinNode節(jié)點的構造函數(shù),默認新節(jié)點為紅色。

    template<typename T> BinNode<T>* RedBlack<T>::insert(const T& data) {BinNode<T>*& x = this->search(data);//沿用BST的查找//并更新_hot//用引用接收if (x)//如果節(jié)點存在,則返回return x;x = new BinNode<T>(data, this->_hot, nullptr, nullptr, -1);//設定黑高度-1,并默認節(jié)點為紅色this->_size++;solveDoubleRed(x);//雙紅修正//x此時必為紅return x; }

    (四)雙紅修復~

    ??因新節(jié)點的引入,而導致父子節(jié)點同為紅色的此類情況,稱作“雙紅”(double red)。每引入一個關鍵碼,雙紅修正函數(shù)都可能迭代地調用多次。在此過程中,當前節(jié)點x的兄弟及兩個孩子(初始時都是外部節(jié)點),必然均為黑色。

    因為x的父親為紅色,所以其只可能有黑孩子,所以x若有兄弟,則必為黑色。 由于x為新節(jié)點,其外部節(jié)點為空,即默認均為黑孩子。

    ??將x的父親與祖父分別記作pg。既然此前的紅黑樹合法,故作為紅節(jié)點p的父親,g必然存在且為黑色。

    此時的g,必然存在,否則作為樹根的節(jié)點p不可能為紅色;并且g作為紅色節(jié)點p的父親,其必然為黑色的

    在下面的過程中,僅僅有x p g這三個節(jié)點還不夠,因此,還需要一個額外的節(jié)點,即p的兄弟(x的叔叔)u。

    ??以下,視節(jié)點u的顏色不同(若u不存在,其顏色也視為黑,這符合之前外部節(jié)點的顏色定義),分兩類情況分別處置。

    1) u為黑色~

    ??u為黑色時,具體來說,對應四種結構.

    ??此時x的兄弟和兩個孩子的黑高度必然都與u的黑高度相等。

    a) LL型~

    在原來的樹中,插入了新節(jié)點x。構成下圖所示的結構。

    此時,可以利用B樹來理解,不妨先將紅黑樹,經過提升變換,提升為對應的(2,4)B樹。

    從B樹的結構可以看出,其不滿足先前提升變換的四種情況中的任何一種。
    因此,要想其滿足提升變換的四種情況,最簡單的做法,就是將p與g互換顏色,讓對應的(2,4)B樹變成下圖所示形式

    再將對應的(2,4)B樹還原成紅黑樹,即為

    即原來的 x 變成 a,原來的 p 變成 b ,原來的 g 變成 c 。
    但也注意到,相應的孩子節(jié)點的位置也發(fā)生了新的變化。

    ??因此,如何做到這樣的變化呢?此時不妨想想在本系列文章第三部分定義的AVL中的connect34算法,其對應的形狀也是這樣的形狀

    ??沒錯,只要我們將對應的x p g按照connect34的形式進行重構,就可以達到目的。

    b) RR型~

    ??同LL型一樣處理,不多贅述。

    c) LR型~

    ??在原來的樹中,插入了新節(jié)點x。構成下圖所示的結構。此時仍然滿足x的兄弟和兩個孩子的黑高度必然都與u的黑高度相等這個條件。

    此時,同樣可以利用B樹來理解,不妨先將紅黑樹,經過提升變換,提升為對應的(2,4)B樹。

    ??情況總是驚人的相似,可以發(fā)現(xiàn),現(xiàn)在的形狀的調整方式,不正是同LL型的調整方式一模一樣么?只是x p g的相對位置有所變化。因此,也是需要進行connect34重構。

    d) RL型~

    ??同LR型一樣處理,不多贅述。

    2) u為紅色~

    ??u為紅色時,具體來說,對應四種結構

    ??此時,u的左、右孩子均為黑色(可能為空),其黑高度必與x的兄弟以及兩個孩子相等。

    a) LL型~

    在原來的樹中,插入了新節(jié)點x。構成下圖所示的結構。

    此時,同樣可以利用B樹來理解,不妨先將紅黑樹,經過提升變換,提升為對應的(2,4)B樹。

    在介紹LR型的時候,會展示怎么處理這種情況。

    b) RR型~

    ??同LL型一樣處理,不多贅述。

    c) LR型~

    ??在原來的樹中,插入了新節(jié)點x。構成下圖所示的結構。此時仍然滿足x的兄弟和兩個孩子的黑高度必然都與u的黑高度相等這個條件。

    此時,同樣可以利用B樹來理解,不妨先將紅黑樹,經過提升變換,提升為對應的(2,4)B樹。

    ??可以看到,提升變換之后,其這個大節(jié)點的關鍵碼數(shù)目,均必然為4個,超出了4階B樹的個數(shù)限制(4階B樹的一個大節(jié)點的關鍵碼數(shù)最多只能有3個)。所以可以仿照B樹的情況,進行一次上溢。同時進行染色以滿足原來B樹提升變換后的四種形態(tài)。(問號節(jié)點中必然有一個為黑,按照紅黑樹的提升變換,只有當g染成紅時才可以進行提升變換)

    從紅黑樹的角度來看,對比沒有變換之前


    ??從宏觀上來看,對于紅黑樹而言,只需要將p u的顏色由紅色變成黑色,并且若g不為根節(jié)點的話,就將g染色成紅色。當然,若g此時就是根節(jié)點,其強制變成黑色。

    ??同樣,由于g變成了紅色,所以還需要繼續(xù)判斷g的父親是否是紅色,因此要繼續(xù)進行雙紅修復。最壞的情況,可能要持續(xù)到根節(jié)點。(由x到g,上升了兩層)。累計最多迭代logn次。

    d) RL型~

    ??同LR型一樣處理,不多贅述。

    3) 求當前節(jié)點的叔叔代碼~

    static inline BinNodePtr uncle(const BinNodePtr& x) {/*獲取x的叔叔*/return IsLChild(x->_parent) ? x->_parent->_parent->_rchild : x->_parent->_parent->_lchild; }

    4) 雙紅修復代碼遞歸版~

    ??注意要是已經遞歸到樹根,則樹根強制轉黑

    template<typename T>void RedBlack<T>::solveDoubleRed(BinNode<T>* x){if (IsRoot(x)) {//若已遞歸到樹根,則樹根轉黑,整樹高度也隨之遞增this->_root->_color = RBColor::BLACK;this->_root->_height++;return;}//否則x的父親必然存在BinNode<T>* p = x->_parent;//x的父親if (IsBlack(p))//如果x的父親為黑,則終止調整return;//否則x的父親必然為紅,則BinNode<T>* g = p->_parent;//x的祖父必然存在,并且,其顏色必然為黑色BinNode<T>* u = uncle(x);//x的叔叔,可能為空節(jié)點if (IsBlack(u)) {//當u為黑色時(u為空時,也為黑色)if (IsLChild(x) == IsLChild(p))p->_color = RBColor::BLACK;elsex->_color = RBColor::BLACK;g->_color = RBColor::RED;/// 以上雖保證總共兩次染色,但因增加了判斷而得不償失/// 在旋轉后將根置黑、孩子置紅,雖需三次染色但效率更高BinNode<T>*& newNode = this->FromParentTo(g);//先記錄祖父的父親的孩子指針newNode = this->rotateAt(x);/*對x進行調整,調整之后的返回值必然為調整之后的局部子樹的樹根位置,此時,只需要將此樹根作為原來祖父的父親的孩子既可,當然,高度也隨之更新*/return;//只要旋轉了一次,就調整完整,退出循環(huán)}else {//若u為紅色p->_color = RBColor::BLACK;//父親此時必然為紅色,將其轉黑p->_height++;u->_color = RBColor::BLACK;//叔叔此時必然為紅色,將其轉黑u->_height++;if (!IsRoot(g))//如果祖父不為根節(jié)點,就轉紅g->_color = RBColor::RED;solveDoubleRed(g);//遞歸用}}

    5) 雙紅修復代碼迭代版~

    ??為了方便理解,我將遞歸的部分變成了注釋,并未進行刪除,方便讀者比對,迭代版中,不僅將尾遞歸轉換成了迭代,也將染色的效率進行了優(yōu)化。

    template<typename T> void RedBlack<T>::solveDoubleRed(BinNode<T>* x) {while (true) {if (IsRoot(x)) {//若已迭代到樹根,則樹根轉黑,整樹高度也隨之遞增this->_root->_color = RBColor::BLACK;this->_root->_height++;return;}//否則x的父親必然存在BinNode<T>* p = x->_parent;//x的父親if (IsBlack(p))//如果x的父親為黑,則終止調整return;//否則x的父親必然為紅,則BinNode<T>* g = p->_parent;//x的祖父必然存在,并且,其顏色必然為黑色BinNode<T>* u = uncle(x);//x的叔叔,可能為空節(jié)點if (IsBlack(u)) {//當u為黑色時(u為空時,也為黑色)//if (IsLChild(x) == IsLChild(p))// p->_color = RBColor::BLACK;//else// x->_color = RBColor::BLACK;//g->_color = RBColor::RED;/// 以上雖保證總共兩次染色,但因增加了判斷而得不償失/// 在旋轉后將根置黑、孩子置紅,雖需三次染色但效率更高BinNode<T>*& newNode = this->FromParentTo(g);//先記錄祖父的父親的孩子指針newNode = this->rotateAt(x);/*對x進行調整,調整之后的返回值必然為調整之后的局部子樹的樹根位置,此時,只需要將此樹根作為原來祖父的父親的孩子既可,當然,高度也隨之更新*///重染色/*能省去之前的判斷*/newNode->_color = RBColor::BLACK;newNode->_lchild->_color = RBColor::RED;newNode->_rchild->_color = RBColor::RED;return;//只要旋轉了一次,就調整完整,退出循環(huán)}else {//若u為紅色p->_color = RBColor::BLACK;//父親此時必然為紅色,將其轉黑p->_height++;u->_color = RBColor::BLACK;//叔叔此時必然為紅色,將其轉黑u->_height++;if (!IsRoot(g))//如果祖父不為根節(jié)點,就轉紅g->_color = RBColor::RED;//solveDoubleRed(g);//遞歸用x = g;//將x變成其祖父,進入迭代循環(huán)。}} }

    6) 雙紅修復的復雜度~

    ??鄧老師已經用一個流程圖和一個表格,幫助我們詳細地分析了雙紅修復算法的復雜度。其中zag,zig是左右旋(也就是我們的connect34重構)

    RR代表雙紅


    ??可見,對于u為黑的情況,只需做一輪修正;u為紅的情況雖有可能需要反復修正,但由于修正位置的高度會嚴格單調上升,故總共也不過O(logn)輪。另外從該表也可看出,每一輪修正只涉及到常數(shù)次的節(jié)點旋轉或染色操作。

    ??因此,節(jié)點插入之后的雙紅修正,累計耗時不會超過O(logn)。即便計入此前的關鍵碼查找以及節(jié)點接入等操作,紅黑樹的每次節(jié)點插入操作,都可在O(logn)時間內完成

    (五)紅黑樹的刪除~

    ??紅黑樹的刪除算法,本質同BST,AVL的刪除。在刪除節(jié)點方面。兩者沒什么區(qū)別,唯一的區(qū)別即高度的更新方式不同。因此,可以調用在BST中設定的給AVL的刪除接口removeAt全局函數(shù)。

    現(xiàn)在我們繼續(xù)來看看removeAt函數(shù)的作用

    定義在BST中的removeAt函數(shù)

    template<typename T>//適用于AVL Splay,RedBlack等,必須這么設計,才能做到完美刪除,且保持BST的性質 static BinNode<T>* removeAt(BinNode<T>*& x, BinNode<T>*& hot) {//這里x必須用引用,才不會使指針亂指using BinNodePtr = BinNode<T>*; //記錄x的地址里面保存的值,若刪除temp里面的值,即刪除x里面的值,但x的本身地址不會影響temp,反之亦然。BinNodePtr temp = x;//替代被刪除節(jié)點的接替者,一般為被刪除節(jié)點的左孩子或者右孩子,而不是x的左孩子或者右孩子BinNodePtr replacer = nullptr;if (!HasLChild(x)) {//如果x沒有左孩子,或者x左右孩子均無,則將x的右孩子作為x,并將接替者設為x的右孩子x = x->_rchild;replacer = x;}else if (!HasRChild(x)) {//如果x沒有右孩子,則將x的左孩子作為x,并將接替者設為x的左孩子x = x->_lchild;replacer = x;}else {temp = temp->succ();//取得中序遍歷的后繼//這個后繼必將沒有左孩子std::swap(x->_data, temp->_data);//交換對應的值if (temp->_parent == x) {//如果后繼的父親是原來的x,后繼必然為x的右孩子replacer = temp->_rchild; //就將后繼的右孩子作為父親的右孩子temp->_parent->_rchild = replacer;}else {//如果后繼的父親不是原來的x,后繼必然為某一節(jié)點的左孩子replacer = temp->_rchild;temp->_parent->_lchild=replacer;//就將后繼的右孩子作為這個節(jié)點左孩子}}//hot即被刪除節(jié)點的父親。而temp正是要刪除的節(jié)點。hot = temp->_parent;if (replacer)//若replacer存在,則必須將其父指針指向hot。不然如同x->_rchild的父親指向的還是原來的replacer->_parent = hot;//釋放原來x所指的堆區(qū)的數(shù)據(jù),或者x的后繼的堆區(qū)的數(shù)據(jù)release(temp->_data);release(temp);return replacer; }

    1.再探removeAt語義~

    ??從removeAt的語義(3種刪除的情況)來看,刪除的節(jié)點可能是x,可能是x的后繼。

    (1)removeAt的第一種和第二種情況~

    ??從removeAt的第一種和第二種情況來看,刪除的節(jié)點必然為x。即表明此時的x要么沒有孩子,要么只有一個孩子。即此時x必然有一個空孩子(也一定是黑孩子)。

    下面圖的情況,全部是用來刪除節(jié)點x ?? 刪除節(jié)點1,實際刪除的也是1(removeAt的第一種情況)

    刪除節(jié)點1,實際刪除的也是1(removeAt的第一種情況)

    刪除節(jié)點1,實際刪除的也是1(removeAt的第二種情況)


    ??可以看到,此時x必然有一個空孩子(也一定是黑孩子)。

    (2)removeAt的第三種情況~

    ??從removeAt的第三種情況來看,刪除的節(jié)點為x的后繼,由后繼的定義可知,這個后繼必然沒有左孩子(這種后繼的情況,必然屬于在本系列文章第一節(jié)談到的求后繼的第一種情況,并且我用紅字標明了這種后繼一定沒有左孩子),不然不可能刪這個后繼。

    刪除節(jié)點10,實際刪除的是15(removeAt的第三種情況)


    ??可以看到,15必然沒有左孩子,其左孩子為空節(jié)點(黑節(jié)點)。

    因此,x的后繼必然有一個空孩子(也是黑孩子)。

    (3)結論~

    綜合(1)(2)可以得到一個非常重要的結論:

    無論被刪除的是x還是x的后繼,被刪除節(jié)點,都必然有一個空孩子(也是黑孩子)。

    (4)removeAt其他細節(jié)~

    ??1. 在removeAt函數(shù)中,交換x與后繼的值時,也只是交換了它們的data值,并非交換了它們的所有東西,因此,x的原來的顏色,和其后繼的顏色均沒變

    std::swap(x->_data, temp->_data);//交換對應的值

    ??2.在刪除完成后_hot節(jié)點也指向了被刪除節(jié)點的父親節(jié)點。

    //hot即被刪除節(jié)點的父親。而temp正是要刪除的節(jié)點。 hot = temp->_parent;

    ??3.removeAt的返回值是replacer,即被刪除節(jié)點的接替者。

    BinNodePtr replacer//替代被刪除節(jié)點的接替者,一般為被刪除節(jié)點的左孩子或者右孩子,可能為空

    2.紅黑樹的刪除~

    (1)刪除情況分析~

    ??對于刪除時節(jié)點(可能是x被刪,也可能是其后繼被刪)的顏色進行分類討論的話,無外乎就五種情況。

    (1)被刪除后,原樹沒有任何節(jié)點,刪除即可完成。

    (2)被刪除的是根節(jié)點,只需要將其接替者replacer染成黑色,并更新高度既可,刪除完成。

    (3)實際被刪除節(jié)點x為紅色,其必然只有黑孩子和黑父親,并且w必然為空(根據(jù)removeAt的結論),此時只需將r接替x的位置既可。(當然r也可能為空)

    x代表被刪除節(jié)點,w代表必然為空的那個節(jié)點,r代表replacer也就是被刪除節(jié)點的接替者。p為x的父親


    ??此時,毫無例外,刪除x對原樹的黑高度肯定沒有影響,因此直接刪除既可。刪除完成后,即可結束。

    (4)實際被刪除節(jié)點為黑色,w同樣也為空(根據(jù)removeAt的結論)。若r為紅色,此時,只需要在r接替x的位置之后,將r轉成黑色,那么原樹的黑高度也必然恢復,刪除完成。

    (5)實際被刪除節(jié)點為黑色,w同樣也為空(根據(jù)removeAt的結論)。若r為黑,即出現(xiàn)雙黑情況,則需要做進一步的調整變換。(當然,這種情況,也必然包含了r為空孩子的情況)

    (2)刪除代碼~

    template<typename T> bool RedBlack<T>::remove(const T& data) {BinNode<T>*& x = this->search(data);//找有沒有這個節(jié)點,如果沒有,則返回false,記住用引用if (!x)return false;BinNode<T>* replacer = removeAt(x, this->_hot);//調用在BST定義的全局靜態(tài)函數(shù)removeAt,返回被刪除節(jié)點的接替者,同時更新_hot--this->_size;//更新規(guī)模//1.如果這個被刪除節(jié)點是樹中唯一節(jié)點,則直接返回if (this->_size==0) {this->_root = nullptr;//將根節(jié)點置空return true;}//2.如果被刪除節(jié)點為根節(jié)點,則_hot必然為空//但如果進行到此,說明此時_root必然不為空,不然上一就會退出if (this->_hot == nullptr) {this->_root->_color = RBColor::BLACK;//就將此時的根節(jié)點直接染成黑色updateHeight(this->_root);//并更新根節(jié)點的高度return true;}//如果進行到此,說明被刪除節(jié)點必然存在,并且不為根節(jié)點。_hot也必然存在/*3.如果_hot的黑高度不變則返回*//*此時也必然包括了被刪除節(jié)點為紅色節(jié)點的情況,若為紅色,則刪除對高度沒有影響*//*當然,也包括了雙黑的可能情況*/if (IsBlackHeightBalanced(this->_hot))return true;/*4.如果_hot的黑高度變了,說明被刪除節(jié)點必然為黑色*/if (IsRed(replacer)) {//就看x的接替者是不是紅色,如果是紅色,將其染成黑色,就必然可以使樹的高度恢復。replacer->_color = RBColor::BLACK;replacer->_height++;return true;}/*5.如果進行到此,就必然說明被刪除節(jié)點和replacer均為黑色節(jié)點(replacer可能為空),此時,就需要進行雙黑缺陷判斷*//*要進行到這里的條件即為被刪除節(jié)點的父親的黑高度變了,并且被刪除節(jié)點的接替者也為黑色時。*/solveDoubleBlack(replacer);return true; }

    (3)判斷黑高度是否平衡代碼~

    在紅黑樹的刪除過程中,我們需要判斷黑高度是否平衡。

    static constexpr bool IsBlackHeightBalanced(const BinNodePtr& x) {//判斷是否需要更新黑高度bool is_L_C_Equal = (stature(x->_lchild) == stature(x->_rchild));int rbHeight = (IsRed(x) ? stature(x->_lchild) : stature(x->_lchild) + 1);//對于rbHeight的計算而言,取左孩子還是右孩子,均一樣bool is_Height_Equal = (x->_height == rbHeight);return is_L_C_Equal && is_Height_Equal;//只要有一個為假,即為假//所以只要左孩子和右孩子高度相等,或者x沒有高度變化,就黑高度平衡 }

    (六)雙黑缺陷~

    ??若被刪除節(jié)點和其接替者(可能為空)均為黑。則顯然,為了保持紅黑樹的平衡性,原來被刪除節(jié)點必然有一個非空兄弟(其孩子可能均為空),不然黑高度就無法維持。

    ??不妨將被刪除節(jié)點x的兄弟記作s。被刪除節(jié)點的父親記作p。無論哪一個,顏色都不確定。

    ??因此,分s和p的顏色情況,分四種情況來討論。

    (1)s為黑,其至少有一個紅孩子~

    ??先以s為p的左孩子,x為p的右孩子來處理(對稱情況處理方式完全相同)
    ??左為紅黑樹,右為對應的B樹

    ??此時刪除了節(jié)點x,類似于B樹的處理方式,由于其左兄弟有一個多余的關鍵碼,所以要從其左兄弟借一個關鍵碼。調整后為(當然,調整之后,要滿足提升變換的四種情況,因此需要重染色)

    左側是B樹,右側是紅黑樹


    ??并且圖(b)的結構也是令人十分熟悉,沒錯,也就是connect34重構。
    ??因此,從紅黑樹的角度來看,此過程等效于對節(jié)點t,s,p進行3+4重構。

    ??位置調整好后,再進行重染色,即把t,p染成黑色,s繼續(xù)沿用之前p的顏色。并且在此過程中,r的顏色沒有發(fā)生變化。

    ??顯然,調整完之后,紅黑樹的高度得以復原。因此調整完畢。

    (2)s為黑,s的兩個孩子為黑,p為紅~

    ??先以s為p的左孩子,x為p的右孩子來處理(對稱情況處理方式完全相同)
    ??左為紅黑樹,右為對應的B樹

    (p的位置不可能是中間,只可能是左邊或者右邊) ??

    ??此時的B樹,被刪除的x無法從s中借關鍵碼,所以只有父親下溢。為保持紅黑樹的性質不變,因此下溢后,只需將s和p的顏色互換,就能保持性質。

    (p的左右節(jié)點中有且僅有一個黑色關鍵碼,因此p下溢,不會造成對應B樹結構的破壞) ??

    ??因此,從紅黑樹的角度來看,只需將s與p的顏色進行互換,就能使紅黑樹的高度得到復原。

    (3)s為黑,s的兩個孩子為黑,p為黑~

    ??先以s為p的左孩子,x為p的右孩子來處理(對稱情況處理方式完全相同)
    ??左為紅黑樹,右為對應的B樹

    ??由于刪除了x,s也沒有足夠的關鍵碼,因此,只能p下溢。并且由于p所在層次,必然只有p一個關鍵碼,因此,p的下溢,必將導致上層下溢。

    ??下溢之后,將s置為紅色,對應的紅黑樹為

    ??因此,從紅黑樹的角度來看,即把s由黑轉紅。

    ??由于p是下溢過來的,所以需要做進一步的檢查,此時等效于原樹中p的黑父親剛刪除,因此可以看做又是一次雙黑缺陷。所以需要再次做迭代循環(huán)。

    ??這也是雙黑修正過程中,需要再次迭代的唯一可能。(可能進入情況1 2 3 4中任何一種)。

    (4)s為紅,s的兩個孩子必然為黑,p必然為黑~

    ??先以s為p的左孩子,x為p的右孩子來處理(對稱情況處理方式完全相同)
    ??左為紅黑樹,右為對應的B樹

    ??從B樹的角度來看,此時可以將p下溢,并將s’轉紅,s轉黑,但這樣會導致以s’為根的子樹的高度變化,并且由于x被刪除,以x為根的子樹的高度必然也下降。所以,如果僅僅這么調整,會造成兩顆子樹的高度減少,使得情況變得更加復雜。

    ??而先輩們已經有了很好的解決方式。

    ??即先將s與p互換顏色,得到左圖所示的B樹,將其轉換為紅黑樹為右圖所示。

    ??從紅黑樹的角度來看,這一轉換對應于以節(jié)點p為軸做一次旋轉,并交換p與s的顏色。

    ??可以發(fā)現(xiàn),經過上述處理后,雙黑缺陷依然存在,而且缺陷位置的高度也未上升。但此次變換并非沒有意義,仔細觀察圖b可以發(fā)現(xiàn),被刪除節(jié)點x有了一個新兄弟s’,并且s’必然為黑。

    ??并且,調整之后,可以發(fā)現(xiàn)a與b所示的紅黑樹,完全等價。

    ??再仔細觀察,不難發(fā)現(xiàn),此時x p s’對應的情況,不正是之前雙黑修復過程中出現(xiàn)的情況(1)與(2)么

    ??所以,只需要將調整之后的紅黑樹,再進行一次雙黑修復,就必然可以修復高度。

    (5)雙黑修復遞歸版~

    template<typename T> void RedBlack<T>::solveDoubleBlack(BinNode<T>* replacer) {BinNode<T>* p = replacer ? replacer->_parent : this->_hot;if (p == nullptr)return;//如果replacer的父親為空,則返回/*由下面的情況來分析,無論哪種情況,遞歸的這個節(jié)點,必然為黑色*/所以不需要考慮將根節(jié)點強轉黑色BinNode<T>* sibling = (replacer == p->_lchild) ? p->_rchild : p->_lchild;//原來x的兄弟,也就是replacer此時的兄弟if (IsBlack(sibling)) {BinNode<T>* s_Red_child = nullptr;//sibling的紅孩子(若左右孩子皆為紅,則左者優(yōu)先;皆黑時為nullptr)if (IsRed(sibling->_rchild))//需要判斷sibling是不是空指針s_Red_child = sibling->_rchild;//右孩子if (IsRed(sibling->_lchild))s_Red_child = sibling->_lchild;//左孩子 /*1.第一種情況,黑s有紅孩子*/if (s_Red_child != nullptr) {//如果sibling有紅孩子RBColor oldColor = p->_color;//備份父親的顏色/*接下來對s的紅孩子,s以及p進行3+4重構*//*根據(jù)3+4重構后的定義,其返回的節(jié)點為根節(jié)點指針,這個根節(jié)點的名字不妨設為newNode*/BinNode<T>*& newNode = this->FromParentTo(p);//首先記錄父親的 父親的孩子的指針newNode = this->rotateAt(s_Red_child);//3+4重構//對3+4重構后的節(jié)點進行重染色if (HasLChild(newNode)) {newNode->_lchild->_color = RBColor::BLACK;updateHeight(newNode->_lchild);}if (HasRChild(newNode)) {newNode->_rchild->_color = RBColor::BLACK;updateHeight(newNode->_rchild);}newNode->_color = oldColor;//新子樹根節(jié)點繼承原根節(jié)點的顏色updateHeight(newNode);//更新高度/*至此,就調整完畢,紅黑樹恢復平衡*/}/*黑s沒有紅孩子,及其孩子均為黑色(可能為空),對其父親是否為紅色進行判斷*/else {sibling->_color = RBColor::RED;//無論父親是否為紅色,都需要把s設為紅色sibling->_height--; /*2.黑s只有黑孩子(可能為空,并且其父親為紅色*/if (IsRed(p)) {p->_color = RBColor::BLACK;//直接將父親設定為黑色,父親的高度必然沒有發(fā)生變化。//因為p原來為紅,現(xiàn)在由于兩個孩子高度都減了一,所以將其變成黑色后,其高度就恢復了原來的高度/*至此,就調整完畢,紅黑樹恢復平衡*/} /*3.黑s只有黑孩子(可能為空,并且其父親為黑色*/else {p->_height--;//相當于p的父親被刪,然后對p是父親的replacer,因此,對p進行遞歸既可。solveDoubleBlack(p);//用遞歸時用/*之后可能進入1 2 3 4四種情況中的任何一種,最壞可能到根*/}}} /*4.s為紅,此時其必然只有黑孩子(黑孩子可能均為空),當然s的父親p此時也必然為黑*/else {sibling->_color = RBColor::BLACK;//將s和p的顏色互換p->_color = RBColor::RED;//取與s同側的孩子BinNode<T>* s_child = IsLChild(sibling) ? sibling->_lchild : sibling->_rchild;this->_hot = p;//首先將p的父親記錄起來BinNode<T>*& newNode = this->FromParentTo(p);//首先記錄p的 父親的孩子的指針newNode = this->rotateAt(s_child);//將s_child,s與p 進行3+4重構/*調整之后,樹的局部結構就發(fā)生了變化*/solveDoubleBlack(replacer);//用遞歸時用//由第四種情況的分析,可知,只需要修復重構后的replacer就可以//并且之后只有可能進入第一種和第二種情況,必然不可能進入第三種情況} }

    (6)雙黑修復迭代版~

    ??為了方便理解,我將遞歸的部分變成了注釋,并未進行刪除,方便讀者比對。

    template<typename T> void RedBlack<T>::solveDoubleBlack(BinNode<T>* replacer) {while (true) {BinNode<T>* p = replacer ? replacer->_parent : this->_hot;if (p == nullptr)return;//如果replacer的父親為空,則返回/*由下面的情況來分析,無論哪種情況,遞歸的這個節(jié)點,必然為黑色*/所以不需要考慮將根節(jié)點強轉黑色BinNode<T>* sibling = (replacer == p->_lchild) ? p->_rchild : p->_lchild;//原來x的兄弟,也就是replacer此時的兄弟if (IsBlack(sibling)) {BinNode<T>* s_Red_child = nullptr;//sibling的紅孩子(若左右孩子皆為紅,則左者優(yōu)先;皆黑時為nullptr)if (IsRed(sibling->_rchild))//需要判斷sibling是不是空指針s_Red_child = sibling->_rchild;//右孩子if (IsRed(sibling->_lchild))s_Red_child = sibling->_lchild;//左孩子 /*1.第一種情況,黑s有紅孩子*/if (s_Red_child != nullptr) {//如果sibling有紅孩子RBColor oldColor = p->_color;//備份父親的顏色/*接下來對s的紅孩子,s以及p進行3+4重構*//*根據(jù)3+4重構后的定義,其返回的節(jié)點為根節(jié)點指針,這個根節(jié)點的名字不妨設為newNode*/BinNode<T>*& newNode = this->FromParentTo(p);//首先記錄父親的 父親的孩子的指針newNode = this->rotateAt(s_Red_child);//3+4重構//對3+4重構后的節(jié)點進行重染色if (HasLChild(newNode)) {newNode->_lchild->_color = RBColor::BLACK;updateHeight(newNode->_lchild);}if (HasRChild(newNode)) {newNode->_rchild->_color = RBColor::BLACK;updateHeight(newNode->_rchild);}newNode->_color = oldColor;//新子樹根節(jié)點繼承原根節(jié)點的顏色updateHeight(newNode);//更新高度/*至此,就調整完畢,紅黑樹恢復平衡*/return;//用遞歸的時候注釋掉}/*黑s沒有紅孩子,及其孩子均為黑色(可能為空),對其父親是否為紅色進行判斷*/else {sibling->_color = RBColor::RED;//無論父親是否為紅色,都需要把s設為紅色sibling->_height--; /*2.黑s只有黑孩子(可能為空,并且其父親為紅色*/if (IsRed(p)) {p->_color = RBColor::BLACK;//直接將父親設定為黑色,父親的高度必然沒有發(fā)生變化。//因為p原來為紅,現(xiàn)在由于兩個孩子高度都減了一,所以將其變成黑色后,其高度就恢復了原來的高度/*至此,就調整完畢,紅黑樹恢復平衡*/return;//用遞歸的時候注釋掉} /*3.黑s只有黑孩子(可能為空,并且其父親為黑色*/else {p->_height--;//相當于p的父親被刪,然后對p是父親的replacer,因此,對p進行遞歸既可。//solveDoubleBlack(p);//用遞歸時用replacer = p;//將replacer變成p進入迭代循環(huán)//用遞歸的時候注釋掉/*之后可能進入1 2 3 4四種情況中的任何一種,最壞可能到根*/}}} /*4.s為紅,此時其必然只有黑孩子(黑孩子可能均為空),當然s的父親p此時也必然為黑*/else {sibling->_color = RBColor::BLACK;//將s和p的顏色互換p->_color = RBColor::RED;//取與s同側的孩子BinNode<T>* s_child = IsLChild(sibling) ? sibling->_lchild : sibling->_rchild;this->_hot = p;//首先將p的父親記錄起來BinNode<T>*& newNode = this->FromParentTo(p);//首先記錄p的 父親的孩子的指針newNode = this->rotateAt(s_child);//將s_child,s與p 進行3+4重構/*調整之后,樹的局部結構就發(fā)生了變化*///solveDoubleBlack(replacer);//用遞歸時用//由第四種情況的分析,可知,只需要修復重構后的replacer就可以//并且之后只有可能進入第一種和第二種情況,必然不可能進入第三種情況}} }

    (7)雙黑修復復雜度~

    ??鄧老師已經用一個流程圖和一個表格,幫助我們詳細地分析了雙黑修復算法的復雜度。其中zag,zig是左右旋(也就是我們的connect34重構)

    ??其中涉及的重構、染色等局部操作,均可在常數(shù)時間內完成,故為了估計整個雙黑修正過程的時間復雜度,也只需統(tǒng)計這些操作各自的累計執(zhí)行次數(shù)。

    ??情況BB-2-B雖可能需要反復修正,但由于待修正位置的高度嚴格單調上升,累計也不致過O(logn)輪,故雙黑修正過程總共耗時不超過O(logn)。

    ??即便計入此前的關鍵碼查找和節(jié)點摘除操作,紅黑樹的節(jié)點刪除操作總是可在O(logn)時間內完成。

    ??一旦在某步迭代中做過節(jié)點的旋轉調整,整個修復過程便會隨即完成。因此與雙紅修正一樣,雙黑修正的整個過程,也僅涉及常數(shù)次的拓撲結構調整操作。

    ??這同樣也是紅黑樹與AVL樹之間最本質的差別。(在本文章的開頭,就說明了AVL的不足就在于刪除時可能要多達logn次調整。)

    五、完整RedBlack.h~

    #pragma once #include "BinNode.h" #include "BST.h"namespace my_redblack {using mytree::BinNode;using mytree::BST;using mytree::RBColor;using mytree_marcro::stature;using mytree_marcro::IsRoot;using mytree_marcro::IsLChild;using mytree_marcro::HasLChild;using mytree_marcro::HasRChild;template<typename T=int>class RedBlack :public BST<T> {protected:using BinNodePtr = BinNode<T>*;protected:void solveDoubleRed(BinNode<T>* x);//雙紅修正void solveDoubleBlack(BinNode<T>* replacer);//雙黑修正constexpr int updateHeight(BinNode<T>* x)const override;//更新高度public:BinNode<T>* insert(const T& data)override;//插入重寫bool remove(const T& data)override;//刪除重寫/*查找沿用BST的查找*//*遍歷沿用BinTree的遍歷*/protected:static constexpr bool IsBlack(const BinNodePtr& x) {//判黑//當然x為空,也為黑色return ((!x) || (RBColor::BLACK == x->_color));}static constexpr bool IsRed(const BinNodePtr& x) {//非黑即紅return !IsBlack(x);}static constexpr bool IsBlackHeightBalanced(const BinNodePtr& x) {//判斷是否需要更新黑高度bool is_L_C_Equal = (stature(x->_lchild) == stature(x->_rchild));int rbHeight = (IsRed(x) ? stature(x->_lchild) : stature(x->_lchild) + 1);//對于rbHeight的計算而言,取左孩子還是右孩子,均一樣bool is_Height_Equal = (x->_height == rbHeight);return is_L_C_Equal && is_Height_Equal;//只要有一個為假,即為假//所以只要左孩子和右孩子高度相等,或者x沒有高度變化,就黑高度平衡 }static inline BinNodePtr uncle(const BinNodePtr& x) {/*獲取x的叔叔*/return IsLChild(x->_parent) ? x->_parent->_parent->_rchild : x->_parent->_parent->_lchild;}};//class RedBlacktemplate<typename T>constexpr int RedBlack<T>::updateHeight(BinNode<T>* x) const//由于stature視空節(jié)點高度為-1,所以height會比黑高度少一{x->_height = std::max(stature(x->_lchild), stature(x->_rchild));//孩子一般黑高度相等,除非出現(xiàn)雙黑return IsBlack(x) ? x->_height++ : x->_height;//若當前節(jié)點為黑,則計入黑高度}template<typename T>void RedBlack<T>::solveDoubleRed(BinNode<T>* x){while (true) {if (IsRoot(x)) {//若已迭代到樹根,則樹根轉黑,整樹高度也隨之遞增this->_root->_color = RBColor::BLACK;this->_root->_height++;return;}//否則x的父親必然存在BinNode<T>* p = x->_parent;//x的父親if (IsBlack(p))//如果x的父親為黑,則終止調整return;//否則x的父親必然為紅,則BinNode<T>* g = p->_parent;//x的祖父必然存在,并且,其顏色必然為黑色BinNode<T>* u = uncle(x);//x的叔叔,可能為空節(jié)點if (IsBlack(u)) {//當u為黑色時(u為空時,也為黑色)//if (IsLChild(x) == IsLChild(p))// p->_color = RBColor::BLACK;//else// x->_color = RBColor::BLACK;//g->_color = RBColor::RED;/// 以上雖保證總共兩次染色,但因增加了判斷而得不償失/// 在旋轉后將根置黑、孩子置紅,雖需三次染色但效率更高BinNode<T>*& newNode = this->FromParentTo(g);//先記錄祖父的父親的孩子指針newNode = this->rotateAt(x);/*對x進行調整,調整之后的返回值必然為調整之后的局部子樹的樹根位置,此時,只需要將此樹根作為原來祖父的父親的孩子既可,當然,高度也隨之更新*///重染色/*能省去之前的判斷*/newNode->_color = RBColor::BLACK;newNode->_lchild->_color = RBColor::RED;newNode->_rchild->_color = RBColor::RED;return;//只要旋轉了一次,就調整完整,退出循環(huán)}else {//若u為紅色p->_color = RBColor::BLACK;//父親此時必然為紅色,將其轉黑p->_height++;u->_color = RBColor::BLACK;//叔叔此時必然為紅色,將其轉黑u->_height++;if (!IsRoot(g))//如果祖父不為根節(jié)點,就轉紅g->_color = RBColor::RED;//solveDoubleRed(g);//遞歸用x = g;//將x變成其祖父,進入迭代循環(huán)。}}}template<typename T>BinNode<T>* RedBlack<T>::insert(const T& data){BinNode<T>*& x = this->search(data);//沿用BST的查找//并更新_hot//用引用接收if (x)//如果節(jié)點存在,則返回return x;x = new BinNode<T>(data, this->_hot, nullptr, nullptr, -1);//設定黑高度-1,并默認節(jié)點為紅色this->_size++;solveDoubleRed(x);//雙紅修正//x此時必為紅return x;}template<typename T>void RedBlack<T>::solveDoubleBlack(BinNode<T>* replacer){while (true) {BinNode<T>* p = replacer ? replacer->_parent : this->_hot;if (p == nullptr)return;//如果replacer的父親為空,則返回/*由下面的情況來分析,無論哪種情況,遞歸的這個節(jié)點,必然為黑色*/所以不需要考慮將根節(jié)點強轉黑色BinNode<T>* sibling = (replacer == p->_lchild) ? p->_rchild : p->_lchild;//原來x的兄弟,也就是replacer此時的兄弟if (IsBlack(sibling)) {BinNode<T>* s_Red_child = nullptr;//sibling的紅孩子(若左右孩子皆為紅,則左者優(yōu)先;皆黑時為nullptr)if (IsRed(sibling->_rchild))//需要判斷sibling是不是空指針s_Red_child = sibling->_rchild;//右孩子if (IsRed(sibling->_lchild))s_Red_child = sibling->_lchild;//左孩子/*1.第一種情況,黑s有紅孩子*/if (s_Red_child != nullptr) {//如果sibling有紅孩子RBColor oldColor = p->_color;//備份父親的顏色/*接下來對s的紅孩子,s以及p進行3+4重構*//*根據(jù)3+4重構后的定義,其返回的節(jié)點為根節(jié)點指針,這個根節(jié)點的名字不妨設為newNode*/BinNode<T>*& newNode = this->FromParentTo(p);//首先記錄父親的 父親的孩子的指針newNode = this->rotateAt(s_Red_child);//3+4重構//對3+4重構后的節(jié)點進行重染色if (HasLChild(newNode)) {newNode->_lchild->_color = RBColor::BLACK;updateHeight(newNode->_lchild);}if (HasRChild(newNode)) {newNode->_rchild->_color = RBColor::BLACK;updateHeight(newNode->_rchild);}newNode->_color = oldColor;//新子樹根節(jié)點繼承原根節(jié)點的顏色updateHeight(newNode);//更新高度/*至此,就調整完畢,紅黑樹恢復平衡*/return;//用遞歸的時候注釋掉}/*黑s沒有紅孩子,及其孩子均為黑色(可能為空),對其父親是否為紅色進行判斷*/else {sibling->_color = RBColor::RED;//無論父親是否為紅色,都需要把s設為紅色sibling->_height--;/*2.黑s只有黑孩子(可能為空,并且其父親為紅色*/if (IsRed(p)) {p->_color = RBColor::BLACK;//直接將父親設定為黑色,父親的高度必然沒有發(fā)生變化。//因為p原來為紅,現(xiàn)在由于兩個孩子高度都減了一,所以將其變成黑色后,其高度就恢復了原來的高度/*至此,就調整完畢,紅黑樹恢復平衡*/return;//用遞歸的時候注釋掉}/*3.黑s只有黑孩子(可能為空,并且其父親為黑色*/else {p->_height--;//相當于p的父親被刪,然后對p是父親的replacer,因此,對p進行遞歸既可。//solveDoubleBlack(p);//用遞歸時用replacer = p;//將replacer變成p進入迭代循環(huán)//用遞歸的時候注釋掉/*之后可能進入1 2 3 4四種情況中的任何一種,最壞可能到根*/}}}/*4.s為紅,此時其必然只有黑孩子(黑孩子可能均為空),當然s的父親p此時也必然為黑*/else {sibling->_color = RBColor::BLACK;//將s和p的顏色互換p->_color = RBColor::RED;//取與s同側的孩子BinNode<T>* s_child = IsLChild(sibling) ? sibling->_lchild : sibling->_rchild;this->_hot = p;//首先將p的父親記錄起來BinNode<T>*& newNode = this->FromParentTo(p);//首先記錄p的 父親的孩子的指針newNode = this->rotateAt(s_child);//將s_child,s與p 進行3+4重構/*調整之后,樹的局部結構就發(fā)生了變化*///solveDoubleBlack(replacer);//用遞歸時用//由第四種情況的分析,可知,只需要修復重構后的replacer就可以//并且之后只有可能進入第一種和第二種情況,必然不可能進入第三種情況}}}template<typename T>bool RedBlack<T>::remove(const T& data){BinNode<T>*& x = this->search(data);//找有沒有這個節(jié)點,如果沒有,則返回false,記住用引用if (!x)return false;BinNode<T>* replacer = removeAt(x, this->_hot);//調用在BST定義的全局靜態(tài)函數(shù)removeAt,返回被刪除節(jié)點的接替者,同時更新_hot--this->_size;//更新規(guī)模//1.如果這個被刪除節(jié)點是樹中唯一節(jié)點,則直接返回if (this->_size==0) {this->_root = nullptr;//將根節(jié)點置空return true;}//2.如果被刪除節(jié)點為根節(jié)點,則_hot必然為空//但如果進行到此,說明此時_root必然不為空,不然上一就會退出if (this->_hot == nullptr) {this->_root->_color = RBColor::BLACK;//就將此時的根節(jié)點直接染成黑色updateHeight(this->_root);//并更新根節(jié)點的高度return true;}//如果進行到此,說明被刪除節(jié)點必然存在,并且不為根節(jié)點。_hot也必然存在/*3.如果_hot的黑高度不變則返回*//*此時也必然包括了被刪除節(jié)點為紅色節(jié)點的情況,若為紅色,則刪除對高度沒有影響*//*當然,也包括了雙黑的可能情況*/if (IsBlackHeightBalanced(this->_hot))return true;/*4.如果_hot的黑高度變了,說明被刪除節(jié)點必然為黑色*/if (IsRed(replacer)) {//就看x的接替者是不是紅色,如果是紅色,將其染成黑色,就必然可以使樹的高度恢復。replacer->_color = RBColor::BLACK;replacer->_height++;return true;}/*5.如果進行到此,就必然說明被刪除節(jié)點和replacer均為黑色節(jié)點(replacer可能為空),此時,就需要進行雙黑缺陷判斷*//*要進行到這里的條件即為被刪除節(jié)點的父親的黑高度變了,并且被刪除節(jié)點的接替者也為黑色時。*/solveDoubleBlack(replacer);return true;}}//namespace my_redblack

    六、紅黑樹測試~

    1.插入測試代碼~

    #include<iostream> #include "RedBlack.h" using namespace std; using namespace my_redblack;template<typename BinNodePtr> void visite(BinNodePtr x) {cout << "數(shù)據(jù)為:" << x->_data << " ";if (x->_color == RBColor::RED) {cout << "顏色為:" << "紅" << " ";}else {cout << "顏色為:" << "黑" << " ";}cout << endl; } int main() {RedBlack r;for (int i = 0; i < 10; ++i) {r.insert(i);}cout << "層次遍歷為:" << endl;r.travLevel(visite<BinNode<int>*>);cout << endl;cout << "先序遍歷為:" << endl;r.travPre(visite<BinNode<int>*>);cout << endl;cout << "中序遍歷為:" << endl;r.travIn(visite<BinNode<int>*>);cout << endl;cout << "后序遍歷為:" << endl;r.travPost(visite<BinNode<int>*>);cout << endl;return 0; } 層次遍歷為: 數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:1 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:0 顏色為:黑 數(shù)據(jù)為:2 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅先序遍歷為: 數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:1 顏色為:黑 數(shù)據(jù)為:0 顏色為:黑 數(shù)據(jù)為:2 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅中序遍歷為: 數(shù)據(jù)為:0 顏色為:黑 數(shù)據(jù)為:1 顏色為:黑 數(shù)據(jù)為:2 顏色為:黑 數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅后序遍歷為: 數(shù)據(jù)為:0 顏色為:黑 數(shù)據(jù)為:2 顏色為:黑 數(shù)據(jù)為:1 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:3 顏色為:黑

    2.插入測試圖示~

    3.刪除測試代碼~

    #include<iostream> #include "RedBlack.h" using namespace std; using namespace my_redblack;template<typename BinNodePtr> void visite(BinNodePtr x) {cout << "數(shù)據(jù)為:" << x->_data << " ";if (x->_color == RBColor::RED) {cout << "顏色為:" << "紅" << " ";}else {cout << "顏色為:" << "黑" << " ";}cout << endl; } int main() {RedBlack r;for (int i = 0; i < 10; ++i) {r.insert(i);}cout << "中序遍歷為:" << endl;r.travIn(visite<BinNode<int>*>);cout << endl;for (int i = 0; i < 10; ++i) {r.remove(i);r.travIn(visite<BinNode<int>*>);cout << endl;}return 0; } 中序遍歷為: 數(shù)據(jù)為:0 顏色為:黑 數(shù)據(jù)為:1 顏色為:黑 數(shù)據(jù)為:2 顏色為:黑 數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:1 顏色為:黑 數(shù)據(jù)為:2 顏色為:紅 數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:2 顏色為:黑 數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:3 顏色為:黑 數(shù)據(jù)為:4 顏色為:紅 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:4 顏色為:黑 數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:紅 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:5 顏色為:黑 數(shù)據(jù)為:6 顏色為:紅 數(shù)據(jù)為:7 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:6 顏色為:黑 數(shù)據(jù)為:7 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:7 顏色為:黑 數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:黑數(shù)據(jù)為:8 顏色為:黑 數(shù)據(jù)為:9 顏色為:紅數(shù)據(jù)為:9 顏色為:黑

    4.刪除測試圖示~

    七、結語~

    本系列文章,總共6篇,分別介紹了

  • 基本二叉樹節(jié)點(1w9字)
  • 基本二叉樹類(6000字)
  • BST(1w2字)
  • AVL(1w1字)
  • B樹(1w5字)
  • 紅黑樹(2w8字)
  • ??其中B樹和紅黑樹的難度最大的,這兩種樹情況分析確實十分復雜,除非背下來,不然是不可能在短時間內一下就能分析出這么多情況,并且不發(fā)生遺漏的。

    ??在此,再次感謝鄧老師的教材和視頻,我才能夠掌握樹的核心框架。并且感謝發(fā)明這些樹的前輩們,多虧了他們的智慧,才有了這些高效的數(shù)據(jù)結構和算法的出現(xiàn),提高了我們的計算機處理問題的能力。

    ??并且不知不覺,算上代碼,此系列文章也寫了將近9w字。零零散散大概寫了兩個星期左右。初衷是想要鍛煉一下c++的編程能力以及數(shù)據(jù)結構和算法。但寫著寫著就想盡可能地將內容完善,簡潔,并且簡單易懂地描述各種插入和刪除的過程。

    ??我相信,只要你從頭看下來,你對樹的理解必然會上升一個檔次。如果你對某些地方的算法有所困惑,那不妨將其手敲一遍,對照著文章再看一遍,或者看下鄧老師的書和講解,你一定能理解這里為什么要用這種算法。

    ??c++就我個人理解而言,是一門非常嚴謹?shù)恼Z言。寫c++代碼的時候,要考慮的因素很多,因此可能初學起來相對java,c#等語言而言,會感到進度緩慢,但只要入門了之后,特別是了解了c++11常用特性,以及c++的常用語法之后,你會對c++越來越喜愛,也會明白為什么c++是世界上效率最高的語言。

    ??廢話不多說,感謝你能耐心看完我精心準備的此系列文章,雖然我已經和鄧老師的代碼的各個實現(xiàn)進行了比對,但難免會有所遺漏之處,希望廣大讀者能夠即時進行指正。

    ??如果你覺得對你有用,請不要光收藏,你的贊是我繼續(xù)分享好東西的動力。

    另外,借用侯捷大師的話:

    學一個東西,不知道其道理,不高明!

    總結

    以上是生活随笔為你收集整理的真c++ 从二叉树到红黑树(6)之红黑树RedBlack的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。

    中文字幕在线观看完整版 | 99久久精品久久久久久清纯 | 激情综合啪啪 | 亚洲欧美视频网站 | 亚洲91在线 | 亚洲资源 | 国产色视频一区二区三区qq号 | 超碰国产人人 | 欧美极品一区二区三区 | 国产精品免费av | 亚洲精品中文字幕视频 | 久久久久久视频 | 激情文学丁香 | 国产一级在线观看视频 | 97视频人人免费看 | 在线观看黄 | 日精品| 日韩精品一区二区久久 | 国产破处在线视频 | 亚洲视频www | 国产成人精品女人久久久 | 中文字幕 欧美性 | 国产成人香蕉 | 亚洲综合精品视频 | 久草在线这里只有精品 | 国产视频69 | 在线激情av电影 | 免费在线观看国产精品 | 天天射天天操天天色 | 午夜丁香网| www.夜夜夜| 最近日本中文字幕a | 亚洲国产欧美在线人成大黄瓜 | 伊人天堂久久 | 成在人线av | 一区二区三区在线不卡 | 久久视了 | 国产一区电影在线观看 | 天天操天天操天天 | 天天综合狠狠精品 | 久久免费毛片视频 | 亚洲精品99久久久久久 | 天天操天天干天天综合网 | 久久一区二区免费视频 | 欧美日韩伦理在线 | 在线视频观看国产 | 成人av高清在线 | 在线观看中文字幕第一页 | 国产精品破处视频 | 国产精品久久久久久久免费观看 | 最近免费中文视频 | 91成人精品一区在线播放69 | 色搞搞 | 久久久精品网 | 欧美视频www | av大全免费在线观看 | 欧洲精品二区 | 久草在线在线视频 | 婷婷六月中文字幕 | av观看网站 | 国产成人在线观看 | 91麻豆精品一区二区三区 | 在线观看完整版免费 | 国产成人亚洲在线电影 | 欧美va天堂在线电影 | 日韩欧美在线国产 | 99国产情侣在线播放 | 日韩在线第一区 | 伊人亚洲综合网 | 成人午夜电影免费在线观看 | 久久午夜免费观看 | 国产精品99久久99久久久二8 | 成人免费观看视频网站 | 久久五月精品 | 国产精品男女 | 一区二区 不卡 | 99久久99久久免费精品蜜臀 | 国产精品毛片一区二区 | 天天草天天爽 | 成人黄色av免费在线观看 | 中文在线字幕观看电影 | 国产在线观看污片 | 久久久久久久久艹 | 国产视频久久久久 | 97av色| 精品在线免费观看 | 天天天在线综合网 | 亚洲狠狠 | 欧美日韩天堂 | 亚洲精品资源在线观看 | 中文字幕一区二区三区乱码不卡 | 最新av电影网址 | 四川妇女搡bbbb搡bbbb搡 | 久久免费福利 | 久草影视在线观看 | 国产精品久久久av | 中文字幕在线久一本久 | 玖玖玖在线观看 | 超碰97人人射妻 | 在线观看国产永久免费视频 | 亚洲精品国产综合久久 | 99国产精品一区二区 | 少妇18xxxx性xxxx片 | 国产精品手机在线播放 | 国产精品一区二区在线观看 | 九草在线视频 | 国产精品99久久久久久人免费 | 国产美女网 | 国产九九精品视频 | 美女一级毛片视频 | 国产又粗又硬又爽的视频 | 日日碰夜夜爽 | 国产小视频在线免费观看 | 国产高清不卡一区二区三区 | av一级片网站| 久久国产精品99久久久久久丝袜 | 日韩高清在线一区二区 | 婷婷在线视频观看 | 91精品老司机久久一区啪 | 人人舔人人 | 天天玩天天干 | 国产精品久久嫩一区二区免费 | 国产精品麻豆三级一区视频 | 国产 日韩 欧美 中文 在线播放 | 亚州精品天堂中文字幕 | 日日夜夜国产 | 久亚洲| 永久免费的啪啪网站免费观看浪潮 | 最新国产福利 | 五月天激情视频在线观看 | 国产一级在线看 | 国产在线a免费观看 | 欧美色插 | 亚洲一级电影 | 99视频这里只有 | 国产伦理一区二区三区 | 天天色棕合合合合合合 | 国产伦理一区 | 久久国产精品系列 | 成人aⅴ视频 | 最近中文字幕mv免费高清在线 | 色是在线视频 | 国产精品乱码一区二三区 | 在线观看成人一级片 | 亚洲男男gaygay无套同网址 | 国产精品99久久久久久宅男 | 少妇视频在线播放 | 香蕉久草 | 亚洲综合成人婷婷小说 | 久久99久久99免费视频 | 日韩成人中文字幕 | 91精品久久久久久久91蜜桃 | 福利一区视频 | 亚洲欧美怡红院 | 91一区二区三区在线观看 | 久久久久激情视频 | 成人97视频一区二区 | 91av电影在线观看 | 国内久久久久久 | 日韩大片免费观看 | 五月婷婷激情综合网 | 天堂网一区二区 | av久久在线 | 蜜臀av性久久久久蜜臀aⅴ流畅 | 欧美伦理一区 | 99精品国产一区二区三区不卡 | 精产嫩模国品一二三区 | 久久最新 | 国产精品video | 在线观看亚洲成人 | 国产伦精品一区二区三区… | 亚洲国产精品99久久久久久久久 | jizz999| 日韩黄色一级电影 | 久久99精品久久久久蜜臀 | 色成人亚洲| 免费日韩av片 | 国产在线中文 | 成人免费在线观看入口 | 欧美俄罗斯性视频 | 亚洲精品无 | 婷婷激情五月 | 久久亚洲欧美 | 日本在线视频网址 | 热re99久久精品国产66热 | 黄色一级大片在线免费看国产一 | 日韩在线视频观看 | 国产超碰在线观看 | 免费精品久久久 | 日本一区二区三区免费看 | 国产99久久久欧美黑人 | 91天堂影院 | 麻豆免费精品视频 | 国产成人精品免费在线观看 | av一级网站 | 亚洲国产高清视频 | 国产精品毛片一区视频播 | 黄色片网站 | 91日韩在线视频 | av免费在线看网站 | 日韩电影一区二区三区 | www夜夜操com | 色噜噜在线观看 | 国产高清不卡 | 黄色小说视频在线 | 国产午夜小视频 | 久久精品一区二区三区国产主播 | 欧美人操人 | 精品一区二区三区在线播放 | 综合在线观看色 | 国产成人黄色网址 | 探花系列在线 | 亚洲精品资源在线 | 97精品国自产拍在线观看 | 日韩欧美一二三 | 狠狠干成人 | www.狠狠插.com | 天无日天天操天天干 | 亚洲国产精品视频 | 久久天天躁狠狠躁亚洲综合公司 | 国产性天天综合网 | 美女免费视频一区二区 | 亚州成人av在线 | 日韩午夜电影 | 中文字幕中文字幕在线中文字幕三区 | 日韩av中文字幕在线 | 成年人免费在线看 | 91亚洲精 | 91探花国产综合在线精品 | 亚洲精品国产品国语在线 | 日本中文一级片 | 久久精品三级 | 国语自产偷拍精品视频偷 | 国产精品自在线 | 99re久久精品国产 | 激情av综合 | 黄色av一区二区三区 | 中文字幕视频一区 | 天天干天天做天天操 | 91人人干| 久久婷亚洲五月一区天天躁 | 色噜噜狠狠狠狠色综合久不 | 免费视频你懂的 | 一区二区三区 中文字幕 | 久久久毛片| 69国产成人综合久久精品欧美 | 丁香激情网 | 久久国产热 | 91av福利视频| 区一区二区三在线观看 | 黄av免费 | 美女搞黄国产视频网站 | 在线成人欧美 | 久久久精品99 | 99爱视频在线观看 | 在线国产专区 | 国产精品18久久久久久久网站 | 亚州精品天堂中文字幕 | 久草在线最新视频 | 欧美国产91| 国产精品婷婷午夜在线观看 | 在线中文字母电影观看 | 97在线播放视频 | 黄色三级免费片 | 欧美在线视频不卡 | 麻豆视频免费在线 | 久草视频在线新免费 | 在线观看一级片 | 激情婷婷av | 国产在线色站 | 五月激情丁香婷婷 | 久久a视频| 美女网色 | 国产99久久久国产精品成人免费 | 国产一二区免费视频 | 在线成人性视频 | 一区二区三区三区在线 | 色视频网页 | 亚洲三级在线播放 | 国产特级毛片 | 国产精品网址在线观看 | 三级黄色三级 | 日韩视频在线观看视频 | 伊人久久国产精品 | 最近中文字幕完整高清 | www.久草.com | 久久草草热国产精品直播 | 免费的成人av | 国产精品黄色影片导航在线观看 | 亚洲国产日韩欧美 | 欧美视频一区二 | 狠狠综合网| 激情久久久| 国产精品 999 | av在线亚洲天堂 | 亚洲精品网址在线观看 | 国产在线国偷精品产拍 | 日韩激情小视频 | 国产精品久久久久久久久毛片 | 美女网站久久 | 美女网站在线看 | 精品久久久亚洲 | 午夜免费福利视频 | 亚洲v欧美v国产v在线观看 | 人人澡av| 欧美大片第1页 | 在线国产福利 | 日本中文字幕在线免费观看 | 一区二区三区视频在线 | 在线一区观看 | 男女啪啪视屏 | 国产精品成人久久 | 国产精品女主播一区二区三区 | 九九免费观看视频 | 欧美91精品久久久久国产性生爱 | 精品久久免费 | 久久天天躁夜夜躁狠狠85麻豆 | 国产精品 视频 | 久久人人精 | 四虎8848免费高清在线观看 | 国产色婷婷精品综合在线手机播放 | 国产精品久久久久久久久久久久午夜 | 亚洲精品视频在线观看免费视频 | 91最新在线观看 | 免费在线电影网址大全 | 在线免费观看黄色小说 | 欧美日韩不卡一区二区 | 国产精品久久99精品毛片三a | 国产精品免费观看在线 | 中文字幕国产一区 | 久久欧洲视频 | 91福利在线导航 | 中日韩三级视频 | 成人永久免费 | 欧美日韩高清一区二区 国产亚洲免费看 | 欧日韩在线视频 | 国内精品久久久久久久久久久久 | 日韩在线观看第一页 | 国产亚洲资源 | 成人黄色在线 | 欧美日一级片 | 爱色av.com | 深夜福利视频在线观看 | 国产精品入口麻豆 | 韩国精品福利一区二区三区 | 亚洲天堂网在线观看视频 | 国产69精品久久久久9999apgf | 99re热精品视频 | 天天操天天爱天天干 | 91免费高清| 综合久久精品 | 国产不卡免费av | 四虎影视成人永久免费观看视频 | 九色精品免费永久在线 | 免费观看黄 | 亚洲蜜桃av| 久99久精品视频免费观看 | 91精品啪在线观看国产81旧版 | 9在线观看免费高清完整版在线观看明 | 亚洲在线激情 | 18久久久久 | 国产精品久久久久一区二区三区共 | 97超碰成人 | 精品久久久久久国产91 | 天天射天天射天天射 | 九九久久精品 | 成人a在线观看高清电影 | 国产五月婷| 国产精品视屏 | 久久大香线蕉app | 婷婷在线播放 | 国产日韩欧美在线免费观看 | 操少妇视频 | 国产原创av在线 | 亚洲精品综合一区二区 | 亚洲精区二区三区四区麻豆 | 91大神电影 | 午夜骚影 | 午夜精品视频免费在线观看 | 亚洲永久精品在线观看 | 在线视频91 | 欧美日韩不卡一区 | 热99在线视频 | 欧美最猛性xxxxx(亚洲精品) | 久久国产色 | 经典三级一区 | 91av在线免费观看 | 麻豆久久久久 | 国产香蕉久久精品综合网 | 午夜免费在线观看 | 在线观看成人 | 久久一区二区三区国产精品 | 婷婷激情综合五月天 | 亚洲欧美一区二区三区孕妇写真 | 高清av影院 | 西西www4444大胆在线 | 国产精品一区二区三区久久久 | 狠狠操欧美 | 中文字幕乱码电影 | 96视频免费在线观看 | 久热色超碰 | 午夜视频在线观看欧美 | 日韩高清一区 | 91在线观看视频 | 激情五月伊人 | 日韩中文在线播放 | www.亚洲精品在线 | 国产精品美女久久久免费 | 在线天堂视频 | 欧美日本不卡视频 | 久热久草在线 | 免费视频资源 | 草免费视频 | 日韩精品免费在线播放 | 91在线日本| 91传媒在线| 色丁香综合| 日韩免费中文字幕 | 奇米影视777影音先锋 | 狠狠色丁香婷婷综合最新地址 | 中文字幕乱码亚洲精品一区 | 可以免费观看的av片 | av大片免费看 | 91精品国产欧美一区二区 | 欧美老人xxxx18| 国产精品久久9 | 婷婷精品进入 | 成人免费视频观看 | 日韩午夜三级 | 一本一本久久a久久精品牛牛影视 | 久久国产精品网站 | 亚洲天堂在线观看完整版 | a视频在线观看 | 欧美国产视频在线 | 免费看黄网站在线 | 国产福利一区在线观看 | 黄色小视频在线观看免费 | 日本中文不卡 | 国产91av视频在线观看 | 欧美一二三区在线观看 | 国产小视频在线观看免费 | 97国产大学生情侣酒店的特点 | 国产精品资源 | 久久影院午夜论 | 五月丁色| 国产高清一区二区 | 精品久久久网 | 在线 国产一区 | 国产中出在线观看 | 国产欧美日韩视频 | 97在线观看| 亚洲丝袜一区 | 精品国产免费人成在线观看 | 日韩精品一区二区免费视频 | 日韩精品一区二区三区不卡 | 在线观看色网 | 91精品婷婷国产综合久久蝌蚪 | 91综合久久一区二区 | av在线专区 | 日韩欧美一区二区在线播放 | 国内一级片在线观看 | 五月天综合婷婷 | 91视频免费网站 | 欧美日韩在线免费视频 | 国产美女久久久 | 亚洲欧美在线综合 | 精品国产三级a∨在线欧美 免费一级片在线观看 | 午夜av电影 | 天天夜操 | 最近高清中文字幕在线国语5 | 亚欧日韩成人h片 | 亚洲精品久久久久999中文字幕 | 国产亚洲成人精品 | 天天干亚洲 | 人人狠狠综合久久亚洲 | 少妇按摩av | 久久不卡免费视频 | 丁香婷婷综合激情 | 日韩在线色| 午夜丁香视频在线观看 | 久久一区二区三区日韩 | 四虎影视成人精品国库在线观看 | 亚洲国产精彩中文乱码av | 久久www免费人成看片高清 | 成年人免费在线 | 亚洲乱码精品久久久久 | 91色在线观看视频 | 天堂在线v | 亚洲综合精品视频 | 精品成人国产 | 中文字幕在线观看完整版电影 | 99久久久久久久 | 国产黄色片在线 | 免费视频三区 | 91精彩在线视频 | 四虎在线永久免费观看 | 免费情趣视频 | 欧美一级乱黄 | 丁香婷五月| 天天综合网 天天综合色 | 在线亚洲人成电影网站色www | 日本在线观看黄色 | 国产精品女教师 | 久久久亚洲精华液 | 婷婷日| 亚州国产视频 | 久久久久成人精品亚洲国产 | 免费一级片久久 | 中日韩免费视频 | 日本精品久久久久影院 | 亚洲春色奇米影视 | 亚洲毛片视频 | 狠狠狠色狠狠色综合 | 999色视频| 久久97精品| 日本精品视频在线播放 | 亚洲精品国产综合99久久夜夜嗨 | 欧美色婷 | 中文字幕在线观看2018 | 色在线中文字幕 | 麻豆久久 | 中文字幕视频三区 | 亚洲日日夜夜 | 国产免费人成xvideos视频 | 欧美坐爱视频 | 97视频在线免费 | 天天综合天天做天天综合 | 黄色激情网址 | 又黄又刺激视频 | 日本一区二区三区视频在线播放 | 久久久久久久久久免费 | 精品高清美女精品国产区 | 国产麻豆剧果冻传媒视频播放量 | 中文av网 | 亚洲综合成人专区片 | 亚洲精品动漫在线 | 久草视频手机在线 | 在线免费观看视频a | 午夜av在线| 免费大片黄在线 | 久久久影院一区二区三区 | 欧美大片第1页 | 日本精品视频一区 | 亚洲尺码电影av久久 | 久久精品资源 | 九草在线观看 | 国产亚洲精品久久久久久无几年桃 | 久草影视在线观看 | 免费网址在线播放 | www.天天操.com | 色综合久久久网 | 91禁看片| 欧美成人中文字幕 | 久久黄色影视 | 日韩亚洲在线观看 | 精品久久久久久亚洲综合网站 | 大型av综合网站 | 午夜av免费在线观看 | 国产精品破处视频 | www.久久久久 | 精品久久久成人 | 免费看色网站 | 亚洲成人国产 | 久久中文精品视频 | 最新高清无码专区 | 亚洲成av人片在线观看香蕉 | 99精品视频免费在线观看 | 99色人| 91视频一8mav | 国产系列在线观看 | 深夜国产福利 | 免费www视频 | 五月婷婷激情网 | 狠狠狠综合 | 国产一区二区三区四区在线 | 国产一区视频免费在线观看 | 99国产精品一区 | 免费一级片观看 | 一区二区三区在线观看免费视频 | 国产青草视频在线观看 | 亚洲日韩欧美一区二区在线 | 色在线亚洲 | 日本一区二区免费在线观看 | 亚洲成a人片在线www | 久久久精品国产一区二区三区 | av黄色免费在线观看 | a级成人毛片 | 天天插狠狠插 | 欧美激情精品一区 | 色综合久久久网 | 久久久久免费精品视频 | www婷婷| 中文字幕亚洲综合久久五月天色无吗'' | 美腿丝袜av | 欧美大片在线看免费观看 | 又污又黄的网站 | 国产高清黄 | 欧美大荫蒂xxx | 国产精品久久久久9999吃药 | 欧美日韩国产综合网 | 亚洲精品美女免费 | 国产一区影院 | 欧美日韩亚洲在线观看 | 免费99精品国产自在在线 | 人人擦 | а天堂中文最新一区二区三区 | 免费在线观看一区二区三区 | 最近在线中文字幕 | 国产精品视频免费在线观看 | 免费色av | 成年人黄色大片在线 | 在线视频 成人 | 黄色精品免费 | 韩国av在线播放 | 亚洲免费a | 91插插插网站 | 日韩欧美一区二区不卡 | 波多野结衣亚洲一区二区 | 亚洲精品乱码久久久久久蜜桃91 | 最近中文字幕完整视频高清1 | 日韩毛片在线一区二区毛片 | 欧美日韩国产色综合一二三四 | 亚洲干视频在线观看 | 在线中文字幕视频 | 91九色国产在线 | 欧美一级免费片 | 国产在线 一区二区三区 | 91麻豆操| 精品国产激情 | 久久国产精品久久久久 | 亚洲最新视频在线 | 99国内精品 | a视频免费在线观看 | 国产成人精品免高潮在线观看 | 国产精品一区二区免费在线观看 | 99九九99九九九视频精品 | 中文字幕高清免费日韩视频在线 | 91精品一区二区三区久久久久久 | 日韩精品专区在线影院重磅 | 国产精品黄色影片导航在线观看 | 日韩欧美高清在线观看 | 日韩精品2区 | 国产国产人免费人成免费视频 | 五月天久久综合网 | 精品国产自在精品国产精野外直播 | 99精品免费久久久久久久久 | 一个色综合网站 | 日韩av黄 | 日韩欧美网站 | 福利电影一区二区 | 久久综合导航 | 成人在线黄色 | 18国产精品白浆在线观看免费 | 99精品免费观看 | 最近中文字幕高清字幕免费mv | 91色一区二区三区 | 日日夜夜天天 | 天天干人人干 | 久久久久久国产精品久久 | 亚洲精品2区| 最近日本字幕mv免费观看在线 | 欧美激情综合色综合啪啪五月 | 一区二区三区免费在线观看 | 亚洲国产97在线精品一区 | 免费在线观看国产黄 | 五月天激情在线 | 欧美久久久久久久久久久久 | 国产精品久久99精品毛片三a | 国产亚洲人成网站在线观看 | 日日爽日日操 | 99久久毛片 | 特级黄色片免费看 | 九九九热精品免费视频观看网站 | 国产精品女人网站 | 免费看污在线观看 | 国产精品色| 国产精品一区免费看8c0m | 日韩午夜精品 | av成人免费观看 | 91久久精品一区二区二区 | www.国产视频 | 国产一区二区在线免费播放 | 久久国产香蕉视频 | 视频一区二区视频 | 97色视频在线| 婷婷去俺也去六月色 | 欧亚日韩精品一区二区在线 | 色丁香综合 | 亚洲激情在线观看 | 99久久久久国产精品免费 | 五月天久久综合网 | 欧美色图视频一区 | 免费黄色特级片 | 黄毛片在线观看 | 亚洲精品国产精品国自产观看浪潮 | 激情五月婷婷综合网 | 四虎影视欧美 | 日韩精品一区二区三区视频播放 | 国产成人精品午夜在线播放 | 成人在线观看资源 | 国产你懂的在线 | 久久er99热精品一区二区三区 | 亚洲日本国产 | 国产麻豆精品久久一二三 | 国产精品国内免费一区二区三区 | 91视频成人免费 | 九九九电影免费看 | 69精品视频在线观看 | 成人av一二三区 | 国产免费看 | 欧美午夜精品久久久久久孕妇 | 国产色影院| 亚洲欧美日韩一级 | 亚洲国产中文字幕在线观看 | 日韩高清精品免费观看 | 国产视频一区二区三区在线 | 久久99网 | 成人免费在线视频 | 色 中文字幕 | 国产中出在线观看 | 成人在线观看资源 | 欧美久久久久久 | 91麻豆精品久久久久久 | 一级特黄av | 福利一区二区三区四区 | 久99久精品视频免费观看 | 在线看欧美 | 91看片在线播放 | 最新中文字幕在线播放 | 美女精品久久久 | 一区二区三区不卡在线 | 中文字幕一区二区在线播放 | 国产精品久久电影网 | 九色视频网址 | 999在线精品 | 国产精品久久久免费看 | 日韩精品一区二区三区免费观看视频 | 精品免费一区 | 亚洲97在线| a黄色影院 | 91成人亚洲 | 国产91亚洲 | 国产一区二区三区在线 | 人人舔人人爽 | 99视频网站| 男女精品久久 | 在线观看成人av | 日本特黄一级 | 在线小视频国产 | 国产成人福利片 | 九热精品 | 亚洲成人精品久久久 | 国产做a爱一级久久 | 久久精美视频 | 中文字幕视频播放 | 在线 视频 一区二区 | 精品国产一区二区三区久久久 | 国产一级片观看 | 中文字幕免费一区二区 | 国产福利91精品 | 色诱亚洲精品久久久久久 | 久久天堂网站 | 精品久久一二三区 | 日韩爱爱网站 | 手机av在线网站 | 麻豆视频入口 | 天天天天天天天操 | 国产高清视频在线播放一区 | 亚洲精品在线一区二区 | 毛片美女网站 | 亚洲一级片 | 热精品| 天天做天天爱天天综合网 | 日韩在线观看a | 成人免费大片黄在线播放 | 91高清在线看 | 五月天色综合 | 久久久精品欧美一区二区免费 | 国产精品久久久久久999 | 久久99欧美| 天天艹天天爽 | 欧美日韩高清一区二区 国产亚洲免费看 | 久久精品国产精品亚洲 | 中文字幕在线观看免费 | 一区二区三区中文字幕在线观看 | 在线a人片免费观看视频 | 成人资源在线观看 | 制服丝袜一区二区 | 在线免费观看国产精品 | 91av在线国产| 日韩午夜精品 | 五月天堂色 | 中文字幕一区二区三区乱码在线 | 一级片观看 | 国产亚洲欧美一区 | 久久99在线观看 | 这里只有精品视频在线 | 日本久久电影网 | 999超碰 | 成人在线免费观看网站 | 91成人精品观看 | 青青草国产免费 | 久久99亚洲精品久久久久 | 国产精品久久久久久久久免费看 | 色综合天天综合 | 亚洲精品字幕在线观看 | 免费看的黄色小视频 | 免费在线观看av电影 | 在线观看黄色小视频 | 欧美精品久久久久久久久久久 | 亚洲情感电影大片 | 日韩动漫免费观看高清完整版在线观看 | 色在线网| 成人久久电影 | 国产在线不卡精品 | 日韩欧在线| 久久久99精品免费观看乱色 | 成 人 黄 色 视频播放1 | 激情五月看片 | 日韩国产精品久久久久久亚洲 | 99热这里只有精品免费 | 国产精品福利久久久 | 亚洲国产美女精品久久久久∴ | 国产成人精品久久久久 | 婷婷香蕉| 91麻豆福利 | 午夜一级免费电影 | 日本少妇久久久 | 久久精品一区八戒影视 | 成人影音av | 久久久精品免费看 | 五月天综合婷婷 | 日韩精品一卡 | 国产亚洲精品久久久久久久久久久久 | 日日爽视频 | 中文字幕专区高清在线观看 | 国产精品久久久久久久妇 | 久久精品精品 | 99久久精品国产亚洲 | 中文字幕在线观 | av高清一区二区三区 | 91福利专区 | av免费在线观看1 | 国产艹b视频 | 人人干狠狠操 | 视频国产精品 | 99免费在线视频 | 手机成人av在线 | 日韩欧美xxxx | www.av在线播放 | 亚洲国产精品成人va在线观看 | 97在线观看视频国产 | 亚洲免费黄色 | 日产av在线播放 | 中文字幕高清在线播放 | 亚洲国产中文在线观看 | 亚洲精品视频网站在线观看 | 久久久激情网 | 欧美精品一区二区三区一线天视频 | 夜夜骑天天操 | 亚洲精品资源在线观看 | 中国一区二区视频 | 免费日韩一区二区三区 | 91福利视频免费观看 | 欧美一区日韩精品 | 国产剧情av在线播放 | 日韩精品视频在线观看网址 | 四虎成人精品永久免费av九九 | av免费观看网站 | 久久成人国产精品免费软件 | 91av原创 | 在线激情网 | 在线午夜电影神马影院 | 国产精品成人av久久 | 日韩理论在线观看 | 日日草天天干 | 日本视频网 | 高清精品在线 | 久久精品高清 | 日韩黄色免费电影 | 九色精品免费永久在线 | 深夜免费福利网站 | 日韩精品在线看 | 日免费视频 | 日韩丝袜视频 | wwwwww国产| 色99之美女主播在线视频 | 97精品国产97久久久久久久久久久久 | 日韩在线中文字幕视频 | 天天做夜夜做 | 亚洲成人精品国产 | 激情小说网站亚洲综合网 | 国产成人免费在线 | 碰碰影院 | 日本精油按摩3 | 成 人 黄 色 视频免费播放 | 夜夜操综合网 | 久久综合九色综合欧美狠狠 | www.神马久久 | 亚洲天堂免费视频 | 免费亚洲片 | 久久国产视屏 | 久久电影国产免费久久电影 | 久久神马影院 | 久久精品国产一区二区电影 | av电影一区 | 亚洲国产精品久久 | 亚洲一级黄色大片 | 黄色小视频在线观看免费 | 久久不卡国产精品一区二区 | 精品视频999 | 久草91视频 | 天天操天天射天天爱 | 亚洲好视频 | 亚洲婷婷伊人 | 精品99免费 | 国产理论影院 | 国产伦精品一区二区三区四区视频 | 国产精品亚洲a | 天天插日日射 | 久久精品国产一区 | 国产高清久久久 | 伊人网站| 视频在线国产 | www91在线观看 | 免费看片网页 | 免费网站在线观看人 | 欧美另类xxx| 最近免费中文字幕大全高清10 | 天天爽夜夜爽精品视频婷婷 | 欧美一区免费观看 | 国产无遮挡猛进猛出免费软件 | 特级黄色片免费看 | 久久久精品国产一区二区三区 | 不卡av在线免费观看 | 成人福利av | 中文字幕人成乱码在线观看 | 国产综合精品久久 | 国产成人精品亚洲a | 国产亚洲视频在线免费观看 | 激情综合网天天干 | 国产精品日韩 | 经典三级一区 | 欧美成人a在线 | 欧美日韩视频在线观看免费 | 欧美成人91| 一区二区三区四区在线 | 日韩精品在线一区 | 国产一级免费在线观看 | 欧美不卡视频在线 | 97超碰资源总站 | 少妇av网| 欧美了一区在线观看 | 91精品国产乱码在线观看 | 亚洲 av网站| 国产小视频在线观看免费 | 久草免费在线视频观看 | 久久久色| 色综合天天色 | 超碰人人在 | 欧洲激情综合 | 欧美视频18| 7777xxxx | 久久久久久蜜桃一区二区 | av中文字幕在线免费观看 | 久久影院中文字幕 | 91麻豆免费看 | 黄网站app在线观看免费视频 | 黄色不卡av| 国产精品中文久久久久久久 | 婷婷成人综合 | 免费久久99精品国产 | 99久久日韩精品视频免费在线观看 | 9999亚洲| 国产成人高清在线 | 久久精品日本啪啪涩涩 | 欧美性一级观看 | 美女免费网站 | 欧美色图88 | 综合色狠狠 | 在线观看日韩中文字幕 | 在线你懂的视频 | 久久久18 | 超碰在线94 | 99免费视频 | 午夜精品久久久久久中宇69 | 中字幕视频在线永久在线观看免费 | 在线观看国产永久免费视频 | 精品视频专区 | 成人av电影在线观看 | 亚洲欧美一区二区三区孕妇写真 | 国产精品毛片一区二区在线看 | 欧美一区二视频在线免费观看 | 国产香蕉久久精品综合网 | 婷婷av电影| 国产精品久久久久久电影 | 国产在线观看网站 | 国产精品99视频 |