Linux紅/黑樹狀結構(一)——資料結構__html

來源:互聯網
上載者:User
摘要

       茲博文探討四個重點:1、簡單介紹紅/黑樹狀結構;2、紅/黑樹狀結構節點資料結構;3、紅/黑樹狀結構節點中父節點指標域和自身節點顏色有機結合;4、定義紅/黑樹狀結構和操作樹節點父節點指標和節點顏色的介面,包括一系列宏和兩個函數。

註:所有代碼源自kernel 3.10
前言

援引<Documentation/rbtree.txt>中的一部分,並做簡單的翻譯(個別地方不太好),姑且作為紅/黑樹狀結構博文的開篇。

What are red-black trees, and what are they for?------------------------------------------------什麼是紅/黑樹狀結構。並且他們用於什麼。Red-black trees are a type of self-balancing binary search tree, used forstoring sortable key/value data pairs.  This differs from radix trees (whichare used to efficiently store sparse arrays and thus use long integer indexesto insert/access/delete nodes) and hash tables (which are not kept sorted tobe easily traversed in order, and must be tuned for a specific size andhash function where rbtrees scale gracefully storing arbitrary keys).紅/黑樹狀結構是一種自平衡二叉搜尋樹,用於儲存可排序的鍵/數值資料。它不同於基數樹(用來有效地儲存稀疏數組,因此使用長整數索來插入/存取/刪除節點)和雜湊表(它不用進行排序就可以很容易地按序遍曆,但必須設定具體大小和散列函數,而紅/黑樹狀結構優雅的擴充儲存任意鍵)Red-black trees are similar to AVL trees, but provide faster real-time boundedworst case performance for insertion and deletion (at most two rotations andthree rotations, respectively, to balance the tree), with slightly slower(but still O(log n)) lookup time.紅/黑樹狀結構類似於AVL樹,但對於插入(至多兩次旋轉)和刪除(至多三次旋轉來重新平衡紅/黑樹狀結構)提供最壞情況下即時複雜度的更快的效能,和稍慢的查詢時間(依舊是O(log n))To quote Linux Weekly News:    There are a number of red-black trees in use in the kernel.    The deadline and CFQ I/O schedulers employ rbtrees to    track requests; the packet CD/DVD driver does the same.    The high-resolution timer code uses an rbtree to organize outstanding    timer requests.  The ext3 filesystem tracks directory entries in a    red-black tree.  Virtual memory areas (VMAs) are tracked with red-black    trees, as are epoll file descriptors, cryptographic keys, and network    packets in the "hierarchical token bucket" scheduler.援引Linux Weekly News:deadline和CFQ(Completely Fair Queueing)兩種I/O調度,運用rbtree跟蹤請求;CD/DVD塊資料磁碟機也是如此。高解析度定時器代碼中使用rbtree出色的組織定時器的請求。Ext3檔案系統使用rbtree跟蹤目錄項。虛擬儲存區(VMA)的跟蹤,以及epoll的檔案描述符,加密金鑰,“分層令牌桶”調度網路資料包也均有rbtree的使用。

  1、紅/黑樹狀結構節點資料結構

<linux/rbtree.h>struct rb_node {       unsignedlong  __rb_parent_color; //下面詳細解析這個變數       structrb_node *rb_right;//右孩子指標域       structrb_node *rb_left;//左孩子指標域} __attribute__((aligned(sizeof(long))));

——sizeof(long)返回long類型的位元組數n;aligned(n)——指明結構體n位元組對齊;__attribute__是GCC編譯器的擴充,屬性約束。這句話的含義:struct rb_node按照4位元組(long)對齊!對於__attribute__和aligned詳細說明和用法可以查閱gcc線上使用者手冊

structrb_node結構體4位元組對齊,那麼它在記憶體中的基地址base一定是的4的倍數(base % 4 == 0);由於指標在32位系統之上佔用4B,則32bit基地址base的低兩位一定為0(否則“base % 4 == 0”不成立,不再是4B對齊)。

紅/黑樹狀結構節點必須儲存四個值:

(1)自身的顏色(color,紅/黑樹狀結構的必須)

(2)父節點指標(使得紅/黑樹狀結構向上走尋找父節點的時間複雜度是O(1))

(3)左孩子節點指標(使得紅/黑樹狀結構向左走尋找左孩子的時間複雜度是O(1))

(4)右孩子節點指標(使得紅/黑樹狀結構向右走尋找右孩子的時間複雜度是O(1))

<linux/augmented.h>#defineRB_RED0#defineRB_BLACK1

紅/黑樹狀結構節點的顏色只有兩個——紅(0)和黑(1),使用1bit就足夠表示。而struct rb_node結構體基地址低2位“不使用”,那麼可以使用最低位來儲存節點的顏色,把基地址和顏色值或(|)運算整合成一個變數__rb_parent_color,這樣就節省了一個儲存顏色的變數。

【如果單獨定義一個變數來儲存顏色值,假設4B無符號整型(即使是shot或者char類型,在4B對齊狀態下也是佔用4B)那麼當樹的節點數量增長時,每增長一個樹節點,就會多開銷4B。並且儲存父節點地址的變數低兩位又不使用,4個樹節點就浪費1B;記住:核心記憶體有限,每消耗1B,核心就少1B——不損失效能的情況下,能節約就節約。】

  2、紅/黑樹狀結構根

<linux/rbtree.h>struct rb_root {struct rb_node *rb_node;};

通過此指標是否為空白,來判斷樹是否為空白。引入下面介面

#define RB_EMPTY_ROOT(root)  ((root)->rb_node == NULL)

定義一棵紅/黑樹狀結構,初始化為空白樹:

static struct rb_root mytree = RB_ROOT;

其中RB_ROOT在<linux/rbtree.h>中定義

#define RB_ROOT(struct rb_root) { NULL, }

它將{NULL,}強制轉換成(struct rb_root),初始化樹為空白。 3、操縱成員__rb_parent_color的介面 3.1、提取父節點地址

<linux/rbtree.h>#define rb_parent(r)   ((struct rb_node *)((r)->__rb_parent_color & ~3))<linux/rbtree_augmented.h>#define __rb_parent(pc)    ((struct rb_node *)(pc & ~3))

32bit

3=0000 0000 0000 0000 0000 0000 0000 0011

~3 =1111 1111 1111 1111 1111 1111 1111 1100

__rb_parent_color & ~3=過濾掉低2位(非0變成0),保留第3-32位。由於__rb_parent_color是不帶正負號的整數,強制轉換成struct rb_node *類型。

上面兩個宏作用一樣,不過參數不一樣——前者是指向節點的指標r;後者是節點成員unsignedlong  __rb_parent_color。此外,”__”首碼也強調內外之分。

<lib/rbtree.c>static inline struct rb_node *rb_red_parent(struct rb_node *red){return (struct rb_node *)red->__rb_parent_color;}

由於紅色節點的成員變數__rb_parent_color的低兩位為0,因此__rb_parent_color的值就是此紅色節點的父節點基址。轉換下資料類型,直接返回即可。 3.2、提取和判斷結點顏色

<linux/rbtree_augmented.h>#define __rb_color(pc)     ((pc) & 1)//struct rb_node成員“pc”值的最低位。#define __rb_is_black(pc)  __rb_color(pc)//邏輯判斷節點是不是黑色;pc值最低位為1,節點是黑色(真);最低位為0,不是黑色(假)#define __rb_is_red(pc)    (!__rb_color(pc))///邏輯判斷節點是不是紅色;pc值最低位為1,不是紅色(假);最低位為0,是紅色(真)#define rb_color(rb)       __rb_color((rb)->__rb_parent_color)//提取節點顏色;值0紅色,值1為黑色;#define rb_is_red(rb)      __rb_is_red((rb)->__rb_parent_color)//封裝成外部介面,接受struct rb_node* 類型rb為參數#define rb_is_black(rb)    __rb_is_black((rb)->__rb_parent_color) //封裝成外部介面,接受struct rb_node* 類型rb為參數
3.3、設定父節點指標
<linux/rbtree_augmented.h>static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p){rb->__rb_parent_color = rb_color(rb) | (unsigned long)p;}


 

由圖示可以很容易明白,這個函數可以用來更新節點的父節點指標使得其指向新的父節點。 3.4、設定父節點指標和節點顏色

<linux/rbtree_augmented.h>static inline void rb_set_parent_color(struct rb_node *rb, struct rb_node *p, int color)       {rb->__rb_parent_color = (unsigned long)p | color;}

 

<lib/rbtree.c>static inline void rb_set_black(struct rb_node *rb){rb->__rb_parent_color |= RB_BLACK;}



3.5、節點的邏輯判斷

<linux/rbtree.h>/* 'empty' nodes are nodes that are known not to be inserted in an rbree */注釋已經說得夠明白了,代碼也很明白。原因以後再論。#define RB_EMPTY_NODE(node)  \((node)->__rb_parent_color == (unsigned long)(node))#define RB_CLEAR_NODE(node)  \((node)->__rb_parent_color = (unsigned long)(node))


4、擴充紅/黑樹狀結構

針對擴充紅/黑樹狀結構的操作,必須提供特定操作的回呼函數。核心定義一個擴充紅/黑樹狀結構的回呼函數資料結構。包括三部分,分別是:繁殖,拷貝,旋轉。

<linux/rbtree_augmented.h>

struct rb_augment_callbacks {void (*propagate)(struct rb_node *node, struct rb_node *stop);void (*copy)(struct rb_node *old, struct rb_node *new);void (*rotate)(struct rb_node *old, struct rb_node *new);};

靜態定義擴充紅/黑樹狀結構的回呼函數可以調用下面宏,它根據使用者提供的參數定義對應的回呼函數名和函數體。

<linux/rbtree_augmented.h>

#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield,\     rbtype, rbaugmented, rbcompute)\static inline void\rbname ## _propagate(struct rb_node *rb, struct rb_node *stop)\{\while (rb != stop) {\rbstruct *node = rb_entry(rb, rbstruct, rbfield);\rbtype augmented = rbcompute(node);\if (node->rbaugmented == augmented)\break;\node->rbaugmented = augmented;\rb = rb_parent(&node->rbfield);\}\}\static inline void\rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new)\{\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);\new->rbaugmented = old->rbaugmented;\}\static void\rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new)\{\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);\new->rbaugmented = old->rbaugmented;\old->rbaugmented = rbcompute(old);\}\rbstatic const struct rb_augment_callbacks rbname = {\rbname ## _propagate, rbname ## _copy, rbname ## _rotate\};

理解宏的最好方式就是用以個執行個體套進去,推演一遍。下面這個代碼是<rbtree.txt>中的執行個體

static void augment_propagate(struct rb_node *rb, struct rb_node *stop){while (rb != stop) {struct interval_tree_node *node =rb_entry(rb, struct interval_tree_node, rb);unsigned long subtree_last = compute_subtree_last(node);if (node->__subtree_last == subtree_last)break;node->__subtree_last = subtree_last;rb = rb_parent(&node->rb);}}static void augment_copy(struct rb_node *rb_old, struct rb_node *rb_new){struct interval_tree_node *old =rb_entry(rb_old, struct interval_tree_node, rb);struct interval_tree_node *new =rb_entry(rb_new, struct interval_tree_node, rb);new->__subtree_last = old->__subtree_last;}static void augment_rotate(struct rb_node *rb_old, struct rb_node *rb_new){struct interval_tree_node *old =rb_entry(rb_old, struct interval_tree_node, rb);struct interval_tree_node *new =rb_entry(rb_new, struct interval_tree_node, rb);new->__subtree_last = old->__subtree_last;old->__subtree_last = compute_subtree_last(old);}
static const struct rb_augment_callbacks augment_callbacks = {augment_propagate, augment_copy, augment_rotate};


對於紅/黑樹狀結構的操作,下篇博文給予詳細解析。

聲明:

>>     知識要傳播,勞動要尊重! 受益於開源,回饋於社會! 大家共參與,服務全人類!     

>>     本博文由my_live_123原創(http://blog.csdn.net/cwcmcw),轉載請註明出處!   ^_^

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.