① 關於演算法導論紅黑樹旋轉的問題。
這句的意思是把 y節點 的 左節點 的 父節點 設為 x節點.
建議看著下面那副圖13-3理解,第一步是把y設為x的右兒子,第二步是把x的右兒子設為y的左兒子,這樣完成了x節點的旋轉,然後接下來處理y.首先判斷y的左兒子是否存在,如果存在,由於y的左兒子的父節點已經改變了(第二步改變的),所以要改變y的左兒子的父指針,設為x.
② 為什麼像map,set都用紅黑樹來實現
STL中List,Vector,Map,Set的理解2009年07月11日 星期六 21:27List封裝了鏈表,Vector封裝了數組, list和vector得最主要的區別在於vector使用連續內存存儲的,他支持[]運算符,而list是以鏈表形式實現的,不支持[]。Vector對於隨機訪問的速度很快,但是對於插入尤其是在頭部插入元素速度很慢,在尾部插入速度很快。List對於隨機訪問速度慢得多,因為可能要遍歷整個鏈表才能做到,但是對於插入就快的多了,不需要拷貝和移動數據,只需要改變指針的指向就可以了。另外對於新添加的元素,Vector有一套演算法,而List可以任意加入。Map,Set屬於標准關聯容器,使用了非常高效的平衡檢索二叉樹:紅黑樹,他的插入刪除效率比其他序列容器高是因為不需要做內存拷貝和內存移動,而直接替換指向節點的指針即可。Set和Vector的區別在於Set不包含重復的數據。Set和Map的區別在於Set只含有Key,而Map有一個Key和Key所對應的Value兩個元素。Map和Hash_Map的區別是Hash_Map使用了Hash演算法來加快查找過程,但是需要更多的內存來存放這些Hash桶元素,因此可以算得上是採用空間來換取時間策略。
③ 紅黑樹在linux內核什麼地方
紅黑樹是平衡二叉樹的一種,它有很好的性質,樹中的結點都是有序的,而且因為它本身就是平衡的,所以查找也不會出現非常惡劣的情況,基於二叉樹的操作的時間復雜度是O(log(N))。Linux內核在管理vm_area_struct時就是採用了紅黑樹來維護內存塊的。
先到include/linux/rbtree.h中看一下紅黑樹的一些定義,如下:
struct rb_node
{
unsigned long rb_parent_color;
#define RB_RED 0
#define RB_BLACK 1
struct rb_node *rb_right;
struct rb_node *rb_left;
} __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。
我們把重點集中在lib/rbtree.c上,看看一些和紅黑樹相關的重要演算法。開始之前我們一起回憶一下紅黑樹的規則:
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)
{
struct rb_node * n = inode->i_rb_page_cache.rb_node;
struct page * page;
while (n)
{
page = rb_entry(n, struct page, rb_page_cache);
if (offset < page->offset)
n = n->rb_left;
else if (offset > page->offset)
n = n->rb_right;
else
return page;
}
return NULL;
}
static inline struct page * __rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
struct rb_node * parent = NULL;
struct page * page;
while (*p)
{
parent = *p;
page = rb_entry(parent, struct page, rb_page_cache);
if (offset < page->offset)
p = &(*p)->rb_left;
else if (offset > page->offset)
p = &(*p)->rb_right;
else
return page;
}
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)
{
struct page * ret;
if ((ret = __rb_insert_page_cache(inode, offset, node)))
goto out;
rb_insert_color(node, &inode->i_rb_page_cache);
out:
return ret;
}
因為紅黑樹的這些良好性質和實現中介面的簡易性,它被廣泛應用到內核編程中,大大提高了內核的效率。
④ c++中紅黑樹的問題,我想要書面式回答
這個應該隨便網路一下,或者隨便找一本數據結構的書都會提到的
關於紅黑樹的具體,我能說的只有:
這個數的節點被分成紅色和黑色兩種,枚舉值是編程語言方面的問題,用來標記一個節點是什麼顏色,你也可以用0和1來區分顏色
⑤ 紅黑樹的紅色內結點個數的最小值和最大值
剛寫過紅黑樹的應用。但卻是沒想過紅節點的數量。
感覺紅節點可能最少完全一個都沒有的0個,
到最多一半節點數量5000/2
對於n個節點中的紅色節點數,最小值0,最大值「n整除2」
⑥ 演算法中紅黑樹的刪除方法問題.
了無適俗韻真可與晤語
⑦ 紅黑樹和平衡二叉樹 區別
紅黑樹和平衡二叉樹的主要區別如下:
平衡二叉樹(AVL)樹是嚴格的平衡二叉樹,平衡條件必須滿足(所有節點的左右子樹高度差不超過1)。不管我們是執行插入還是刪除操作,只要不滿足上面的條件,就要通過旋轉來保持平衡,而的英文旋轉非常耗時的。所以平衡二叉樹(AVL)適合用於插入與刪除次數比較少,但查找多的情況。
紅黑樹在二叉查找樹的基礎上增加了著色和相關的性質使得紅黑樹相對平衡,從而保證了紅黑樹的查找、插入、刪除的時間復雜度最壞為O(log
n)。所以紅黑樹適用於搜索,插入,刪除操作較多的情況。
(7)紅黑樹演算法擴展閱讀:
平衡二叉樹在Windows
NT內核中廣泛存在。
紅黑樹的應用:
1、在C
++的STL中,地圖和集都是用紅黑樹實現的;
2、著名的Linux的的進程調度完全公平調度程序,用紅黑樹管理進程式控制制塊,進程的虛擬內存區域都存儲在一顆紅黑樹上,每個虛擬地址區域都對應紅黑樹的一個節點,左指針指向相鄰的地址虛擬存儲區域,右指針指向相鄰的高地址虛擬地址空間;
3、IO多路復用的epoll的的的實現採用紅黑樹組織管理的sockfd,以支持快速的增刪改查;
4、Nginx的的的中用紅黑樹管理定時器,因為紅黑樹是有序的,可以很快的得到距離當前最小的定時器;
5、Java中的TreeMap中的實現。
參考資料:
網路-平衡二叉樹
網路-紅黑樹
⑧ 只有左樹節點是紅樹的紅黑樹與演算法里介紹的紅黑樹有何區別
只有左樹節點是紅樹的紅黑樹與演算法里介紹的紅黑樹有何區別
因為插入之前所有根至外部節點的路徑上黑色節點數目都相同,所以如果插入的節點是黑色肯定錯誤(黑色節點數目不相同),而相對的插入紅節點可能會也可能不會違反「沒有連續兩個節點是紅色」這一條件,所以插入的節點為紅色,如果違反條件再調整
紅黑樹的操作時間跟二叉查找樹的時間復雜度是一樣的,執行查找、插入、刪除等操作的時間復雜度為O(logn)。
紅黑樹是特殊的AVL樹,遵循紅定理和黑定理 紅定理:不能有兩個相連的紅節點 黑定理:根節點必須是黑節點,而且所有節點通向NULL的路徑上,所經過的黑節點的個數必須相等
⑨ 以後想去當程序員,找工作面試時會不會考紅黑樹操作這樣惡心的問題
或許會有,紅黑樹也屬於演算法之一吧,看招聘者對應聘者的要求而定