紅黑樹是平衡二叉樹的一種,它有很好的性質,樹中的結點都是有序的,而且因為它本身就是平衡的,所以查詢也不會出現非常惡劣的情況,基於二叉樹的操作的時間複雜度是o(log(n))。linux核心在管理vm_area_struct時就是採用了紅黑樹來維護記憶體塊的。
先到include/linux/rbtree.h中看一下紅黑樹的一些定義,如下:
struct rb_node__attribute__((aligned(sizeof(long))));
struct
rb_root只是struct
rb_node*的乙個包裝,這樣做的好處是看起來不用傳遞二級指標了。不錯,很簡單。再看一下 下面幾個重要的巨集,細心的你一定會發現,rb_parent_color其實沒那麼簡單,andrea
arcangeli在這裡使用了乙個小的技巧,不 過非常棒。正如名字所暗示,這個成員其實包含指向parent的指標和此結點的顏色!它是怎麼做到的呢?很簡單,對齊起了作用。既然是 sizeof(long)大小的對齊,那麼在ia-32上,任何rb_node結構體的位址的低兩位肯定都是零,與其空著不用,還不如用它們表示顏色,反 正顏色就兩種,其實一位就已經夠了。
這樣,提取parent指標只要把rb_parent_color成員的低兩位清零即可:
#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
取顏色只要看最後一位即可:
#define rb_color(r) ((r)->rb_parent_color & 1)
測試顏色和設定顏色也是水到渠成的事了。需要特別指出的是下面的乙個內聯函式:
static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link);
它把parent設為node的父結點,並且讓rb_link指向node。
1. 每個結點要麼是紅色要麼是黑色;
2. 根結點必須是黑色;
3. 紅結點如果有孩子,其孩子必須都是黑色;
4. 從根結點到葉子的每條路徑必須包含相同數目的黑結點。
這四條規則可以限制一棵排序樹是平衡的。
__rb_rotate_left是把以root為根的樹中的node結點進行左旋,__rb_rotate_right是進行右旋。這兩個函式是為後面的插入和刪除服務,而不是為外部提供介面。
新插入的結點都設為葉子,染成紅色,插入後如果破壞了上述規則,通過調整顏色和旋轉可以恢復,二叉樹又重新平衡。插入操作的介面函式是
void rb_insert_color(struct rb_node *node, struct rb_root *root);
它把已確定父結點的node結點融入到以root為根的紅黑樹中,具體演算法的分析可以參考[1]中第14.3節,這裡的實現和書中的講解幾乎完全 一樣。怎麼確定node的父結點應該在呼叫rb_insert_color之前通過手工迭帶完成。值得指出的一點是,雖然插入操作需要乙個迴圈迭代,但是 總的旋轉次數不會超過兩次!所以效率還是很樂觀的。
刪除操作多多少少都有點麻煩,它要先執行像普通二叉查詢樹的「刪除」,然後根據刪除結點的顏色來判斷是否執行進一步的操作。刪除的介面是:
void rb_erase(struct rb_node *node, struct rb_root *root);
其實它並沒有真正刪除node,而只是讓它和以root為根的樹脫離關係,最後它還要判斷是否呼叫__rb_erase_color來調整。具體 演算法的講解看參考[1]中第13.3和14.4節,__rb_erase_color對應書中的rb-delete-fixup,此處的實現和書上也基本 上一致。
其餘的幾個介面就比較簡單了。
struct rb_node *rb_first(struct rb_root *root);
在以root為根的樹中找出並返回最小的那個結點,只要從根結點一直向左走就是了。
struct rb_node *rb_last(struct rb_root *root);
是找出並返回最大的那個,一直向右走。
struct rb_node *rb_next(struct rb_node *node);
返回node在樹中的後繼,這個稍微複雜一點。如果node的右孩子不為空,它只要返回node的右子樹中最小的結點即可;如果為空,它要向上查詢,找到迭帶結點是其父親的左孩子的結點,返回父結點。如果一直上述到了根結點,返回null。
struct rb_node *rb_prev(struct rb_node *node);
返回node的前驅,和rb_next中的操作對稱。
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
用new替換以root為根的樹中的victim結點。
紅黑樹介面使用的乙個典型例子如下:
static inline struct page * rb_search_page_cache(struct inode * inode,因為紅黑樹的這些良好性質和實現中介面的簡易性,它被廣泛應用到核心程式設計中,大大提高了核心的效率。unsigned long offset)
return null;
}static inline struct page * __rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
rb_link_node(node, parent, p);
return null;
}static inline struct page * rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
1. introduction
to algorithms,
thomas
h. cormen,
charles
e. leiserson,
and
ronald
l. rivest,
mit
press.
2. understanding
the
linux
kernel,
3rd
edition,
daniel
p. bovet,
marco
cesati,
o'reilly.
3. linux
kernel
2.6.19
source
code.
linux 記憶體管理 紅黑樹(未讀)
2009 12 11 10 02 佚名 51cto 字型大小 t t 很多的人都開始學習linux作業系統。當我們學習linux時,會遇到很多的問題。最近看linux記憶體管理,看到紅黑樹這一部分甚為頭大,於是了解了一下紅黑樹的基本知識。詳細講解一下linux記憶體管理。ad wot2014 使用者...
紅黑樹的管理
3.2 刪除 4 1 紅黑樹是一種自平衡二叉樹,擁有以下性質 每個節點非黑即紅 每個葉子為黑 若某節點為紅,其兩子節點均為黑 從任一節點到其葉子的所有路徑都包含相同數量的黑節點 根節點總為黑 當樹被修改 節點增 刪 時,樹會自動調整以維持上述性質的有效性。2 3紅黑樹的操作主要是插入資料和刪除資料,...
紅黑樹系列之旋轉
1 概述 二叉樹是使用非常廣泛的資料結構,但如果是常規的插入,會導致二叉樹的高度過高和出現整棵樹不平衡的情況。紅黑樹是一種平衡二叉樹,c stl中的set,map及其擴充套件容器內部的資料結構都是紅黑樹。2 左旋轉 注 parent為求父親結點的函式,root是始終指向根結點記憶體區域的指標。左旋轉...