1. 准備acm的同學應該如何閱讀《演算法導論》,還有對於課後的習題和思考題應該如何對待怎麼處理好在線評測系
不得不說你問題問的很大,很大。。。
其實你可以去看看劉汝佳的黑書,那本對IO,ACM等都說了可也看到什麼檔次,然後知道自己哪方面不會了,有針對的分塊去看演算法,比如圖論的最短路,就幾本書聯合著看,邊看邊A題。《演算法導論》是一本很好的演算法樹,但不是一本ACM資料書,這個你要分清楚,其實前期你看那些高級的數據結構比如紅黑樹,B樹,很少用到的,看了也是白看,不如先從簡單的模塊入手,如數論,DP,搜索等等,一點點進步,等需要的時候在去看那些數據結構。
對於OJ,先去HDU吧,那個題還不算BT,要是你沒一點基礎的話建議你先從2000開始A,哪裡都是簡單的題,那一頁全A掉差不多你就有編程基礎了。其他你可以去POJ,ZOJ,等等。
給你個連接。
http://blog.csdn.net/bestluoliwei/archive/2010/07/20/5748964.aspx
ACM很強大,好好珍惜!
2. 關於演算法導論
概念:
紅黑樹是一種自平衡二叉查找樹,是在計算機科學中用到的一種數據結構,典型的用途是實現關聯數組。它是在1972年由Rudolf Bayer發明的,他稱之為"對稱二叉B樹",它現代的名字是在 Leo J. Guibas 和 Robert Sedgewick 於1978年寫的一篇論文中獲得的。它是復雜的,但它的操作有著良好的最壞情況運行時間,並且在實踐中是高效的: 它可以在O(log n)時間內做查找,插入和刪除,這里的n 是樹中元素的數目。
紅黑樹是一種很有意思的平衡檢索樹。它的統計性能要好於平衡二叉樹(有些書籍根據作者姓名,Adelson-Velskii和Landis,將其稱為AVL-樹),因此,紅黑樹在很多地方都有應用。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)應用了紅黑樹的變體(SGI STL中的紅黑樹有一些變化,這些修改提供了更好的性能,以及對set操作的支持)。
背景和術語:
紅黑樹是一種特定類型的二叉樹,它是在計算機科學中用來組織數據比如數字的塊的一種結構。所有數據塊都存儲在節點中。這些節點中的某一個節點總是擔當啟始位置的功能,它不是任何節點的兒子;我們稱之為根節點或根。它有最多兩個"兒子",都是它連接到的其他節點。所有這些兒子都可以有自己的兒子,以此類推。這樣根節點就有了把它連接到在樹中任何其他節點的路徑。
如果一個節點沒有兒子,我們稱之為葉子節點,因為在直覺上它是在樹的邊緣上。子樹是從特定節點可以延伸到的樹的某一部分,其自身被當作一個樹。在紅黑樹中,葉子被假定為 null 或空。
由於紅黑樹也是二叉查找樹,它們當中每一個節點都的比較值都必須大於或等於在它的左子樹中的所有節點,並且小於或等於在它的右子樹中的所有節點。這確保紅黑樹運作時能夠快速的在樹中查找給定的值。
用途和好處:
紅黑樹和AVL樹一樣都對插入時間、刪除時間和查找時間提供了最好可能的最壞情況擔保。這不只是使它們在時間敏感的應用如即時應用(real time application)中有價值,而且使它們有在提供最壞情況擔保的其他數據結構中作為建造板塊的價值;例如,在計算幾何中使用的很多數據結構都可以基於紅黑樹。
紅黑樹在函數式編程中也特別有用,在這里它們是最常用的持久數據結構之一,它們用來構造關聯數組和集合,在突變之後它們能保持為以前的版本。除了O(log n)的時間之外,紅黑樹的持久版本對每次插入或刪除需要O(log n)的空間。
紅黑樹是 2-3-4樹的一種等同。換句話說,對於每個 2-3-4 樹,都存在至少一個數據元素是同樣次序的紅黑樹。在 2-3-4 樹上的插入和刪除操作也等同於在紅黑樹中顏色翻轉和旋轉。這使得 2-3-4 樹成為理解紅黑樹背後的邏輯的重要工具,這也是很多介紹演算法的教科書在紅黑樹之前介紹 2-3-4 樹的原因,盡管 2-3-4 樹在實踐中不經常使用。
屬性:
紅黑樹是每個節點都有顏色特性的二叉查找樹,顏色的值是紅色或黑色之一。除了二叉查找樹帶有的一般要求,我們對任何有效的紅黑樹加以如下增補要求:
1.節點是紅色或黑色。
2.根是黑色。
3.每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
4.從每個葉子到根的所有路徑都包含相同數目的黑色節點。
這些約束強制了紅黑樹的關鍵屬性: 從根到葉子的最長的可能路徑不大於最短的可能路徑的兩倍長。結果是這個樹大致上是平衡的。因為操作比如插入、刪除和查找某個值都要求與樹的高度成比例的最壞情況時間,這個在高度上的理論上限允許紅黑樹在最壞情況下都是高效的,而不同於普通的二叉查找樹。
在很多樹數據結構的表示中,一個節點有可能只有一個兒子,而葉子節點包含數據。用這種範例表示紅黑樹是可能的,但是這會改變一些屬性並使演算法復雜。為此,本文中我們使用 "null 葉子" 或"空(null)葉子",如上圖所示,它不包含數據而只充當樹在此結束的指示。這些節點在繪圖中經常被省略,導致了這些樹好像同上述原則相矛盾,而實際上不是這樣。與此有關的結論是所有節點都有兩個兒子,盡管其中的一個或兩個可能是空葉子。
操作:
在紅黑樹上只讀操作不需要對用於二叉查找樹的操作做出修改,因為它也二叉查找樹。但是,在插入和刪除之後,紅黑屬性可能變得違規。恢復紅黑屬性需要少量(O(log n))的顏色變更(這在實踐中是非常快速的)並且不超過三次樹旋轉(對於插入是兩次)。這允許插入和刪除保持為 O(log n) 次,但是它導致了非常復雜的操作。
3. B樹演算法難么
感覺挺難。
B樹的定義
假設B樹的度為t(t>=2),則B樹滿足如下要求:(參考演算法導論)
(1) 每個非根節點至少包含t-1個關鍵字,t個指向子節點的指針;至多包含2t-1個關鍵字,2t個指向子女的指針(葉子節點的子女為空)。
(2) 節點的所有key按非降序存放,假設節點的關鍵字分別為K[1], K[2] … K[n], 指向子女的指針分別為P[1], P[2]…P[n+1],其中n為節點關鍵字的個數。則有:
P[1] <= K[1] <= P[2] <= K[2] …..<= K[n] <=P[n+1] // 這里P[n]也指其指向的關鍵字
(3) 若根節點非空,則根節點至少包含兩個子女;
(4) 所有的葉子節點都在同一層。
B樹的搜索,search(root, target)
從root出發,對每個節點,找到大於或等於target關鍵字中最小的K[i],如果K[i]與target相等,則查找成功;否則在P[i]中遞歸搜索target,直到到達葉子節點,如仍未找到則說明關鍵字不在B樹中,查找失敗。
B樹的插入,insert(root,target)
B樹的插入需要沿著搜索的路徑從root一直到葉節點,根據B樹的規則,每個節點的關鍵字個數在[t-1, 2t-1]之間,故當target要加入到某個葉子時,如果該葉子節點已經有2t-1個關鍵字,則再加入target就違反了B樹的定義,這時就需要對該葉子節點進行分裂,將葉子以中間節點為界,分成兩個包含t-1個關鍵字的子節點,同時把中間節點提升到該葉子的父節點中,如果這樣使得父節點的關鍵字個數超過2t-1,則要繼續向上分裂,直到根節點,根節點的分裂會使得樹加高一層。
上面的過程需要回溯,那麼能否從根下降到葉節點後不回溯就能完成節點的插入呢?答案是肯定的,核心思想就是未雨綢繆,在下降的過程中,一旦遇到已滿的節點(關鍵字個數為2t-1),就就對該節點進行分裂,這樣就保證在葉子節點需要分裂時,其父節點一定是非滿的,從而不需要再向上回溯。
B樹的刪除,delete(root,target)
在刪除B樹節點時,為了避免回溯,當遇到需要合並的節點時就立即執行合並,B樹的刪除演算法如下:從root向葉子節點按照search規律遍歷:
(1) 如果target在葉節點x中,則直接從x中刪除target,情況(2)和(3)會保證當再葉子節點找到target時,肯定能借節點或合並成功而不會引起父節點的關鍵字個數少於t-1。
(2) 如果target在分支節點x中:
(a) 如果x的左分支節點y至少包含t個關鍵字,則找出y的最右的關鍵字prev,並替換target,並在y中遞歸刪除prev。
(b) 如果x的右分支節點z至少包含t個關鍵字,則找出z的最左的關鍵字next,並替換target,並在z中遞歸刪除next。
(c) 否則,如果y和z都只有t-1個關鍵字,則將targe與z合並到y中,使得y有2t-1個關鍵字,再從y中遞歸刪除target。
(3) 如果關鍵字不在分支節點x中,則必然在x的某個分支節點p[i]中,如果p[i]節點只有t-1個關鍵字。
(a) 如果p[i-1]擁有至少t個關鍵字,則將x的某個關鍵字降至p[i]中,將p[i-1]的最大節點上升至x中。
(b) 如果p[i+1]擁有至少t個關鍵字,則將x個某個關鍵字降至p[i]中,將p[i+1]的最小關鍵字上升至x個。
(c) 如果p[i-1]與p[i+1]都擁有t-1個關鍵字,則將p[i]與其中一個兄弟合並,將x的一個關鍵字降至合並的節點中,成為中間關鍵字。
4. B樹演算法難么
感覺挺難。
B樹的定義
假設B樹的度為t(t>=2),則B樹滿足如下要求:(參考演算法導論)
(1)
每個非根節點至少包含t-1個關鍵字,t個指向子節點的指針;至多包含2t-1個關鍵字,2t個指向子女的指針(葉子節點的子女為空)。
(2)
節點的所有key按非降序存放,假設節點的關鍵字分別為K[1],
K[2]
…
K[n],
指向子女的指針分別為P[1],
P[2]…P[n+1],其中n為節點關鍵字的個數。則有:
P[1]
<=
K[1]
<=
P[2]
<=
K[2]
…..<=
K[n]
<=P[n+1]
//
這里P[n]也指其指向的關鍵字
(3)
若根節點非空,則根節點至少包含兩個子女;
(4)
所有的葉子節點都在同一層。
B樹的搜索,search(root,
target)
從root出發,對每個節點,找到大於或等於target關鍵字中最小的K[i],如果K[i]與target相等,則查找成功;否則在P[i]中遞歸搜索target,直到到達葉子節點,如仍未找到則說明關鍵字不在B樹中,查找失敗。
B樹的插入,insert(root,target)
B樹的插入需要沿著搜索的路徑從root一直到葉節點,根據B樹的規則,每個節點的關鍵字個數在[t-1,
2t-1]之間,故當target要加入到某個葉子時,如果該葉子節點已經有2t-1個關鍵字,則再加入target就違反了B樹的定義,這時就需要對該葉子節點進行分裂,將葉子以中間節點為界,分成兩個包含t-1個關鍵字的子節點,同時把中間節點提升到該葉子的父節點中,如果這樣使得父節點的關鍵字個數超過2t-1,則要繼續向上分裂,直到根節點,根節點的分裂會使得樹加高一層。
上面的過程需要回溯,那麼能否從根下降到葉節點後不回溯就能完成節點的插入呢?答案是肯定的,核心思想就是未雨綢繆,在下降的過程中,一旦遇到已滿的節點(關鍵字個數為2t-1),就就對該節點進行分裂,這樣就保證在葉子節點需要分裂時,其父節點一定是非滿的,從而不需要再向上回溯。
B樹的刪除,delete(root,target)
在刪除B樹節點時,為了避免回溯,當遇到需要合並的節點時就立即執行合並,B樹的刪除演算法如下:從root向葉子節點按照search規律遍歷:
(1)
如果target在葉節點x中,則直接從x中刪除target,情況(2)和(3)會保證當再葉子節點找到target時,肯定能借節點或合並成功而不會引起父節點的關鍵字個數少於t-1。
(2)
如果target在分支節點x中:
(a)
如果x的左分支節點y至少包含t個關鍵字,則找出y的最右的關鍵字prev,並替換target,並在y中遞歸刪除prev。
(b)
如果x的右分支節點z至少包含t個關鍵字,則找出z的最左的關鍵字next,並替換target,並在z中遞歸刪除next。
(c)
否則,如果y和z都只有t-1個關鍵字,則將targe與z合並到y中,使得y有2t-1個關鍵字,再從y中遞歸刪除target。
(3)
如果關鍵字不在分支節點x中,則必然在x的某個分支節點p[i]中,如果p[i]節點只有t-1個關鍵字。
(a)
如果p[i-1]擁有至少t個關鍵字,則將x的某個關鍵字降至p[i]中,將p[i-1]的最大節點上升至x中。
(b)
如果p[i+1]擁有至少t個關鍵字,則將x個某個關鍵字降至p[i]中,將p[i+1]的最小關鍵字上升至x個。
(c)
如果p[i-1]與p[i+1]都擁有t-1個關鍵字,則將p[i]與其中一個兄弟合並,將x的一個關鍵字降至合並的節點中,成為中間關鍵字。