Search Tree
程度★ 難度★★★
Binary Search Tree
置放大量數字並進行排序的資料結構。原理是Divide and Conquer,把所有數字分成一個與兩半,樹根在中間,左子樹較小,右子樹較大,然後遞迴分割下去。
插入、刪除、搜尋的時間複雜度等同於二元搜尋樹的高度。資料可以動態增加和減少,二元搜尋樹的高度亦會變動,因此時間複雜度最差為O(N),最佳為O(logN)。所有節點連成一線的時候是最差的,所有節點形成full tree是最佳的。
空間複雜度等同於節點數目,空間複雜度是O(N)。
尋找極小值、極大值,從樹根開始往左小孩走到底、往右小孩走到底就可以了。時間複雜度等同於二元搜尋樹的高度。
樹葉可以額外建立線索(Thread),如此就能依照大小順序走訪元素。建立線索不影響時間複雜度與空間複雜度。
最佳二元搜尋樹(Optimal Binary Search Tree)
如果二元搜尋樹的資料不會變動,則可以依照每個節點被搜尋到的頻率,使用Dynamic Programming求得結構最佳的二元搜尋樹,藉此加快搜尋速度。建立時間為O(N^2)。
UVa 10304
排名(Ranking)
二元搜尋樹雖然有排序的功效,但是卻沒有排名的功效。如果想知道資料的排名,那麼就要在每個節點新增一個變數,記錄其子樹的節點個數,如此便可以用Divide and Conquer的方式求得每筆資料的排名。不影響時間複雜度與空間複雜度。
UVa 10909
以陣列表示二元搜尋樹
利用陣列索引值定位到樹的節點。優點是實作輕鬆,結構簡單,效率高。缺點是當二元搜尋樹沒有形成full tree的時候,會有很多陣列空間沒有使用到,浪費記憶體,甚至記憶體會不敷使用。
AVL Tree
平衡二元搜尋樹。令每個葉子的高度落差不會太多,讓各項操作的運行速度很穩定均勻,不會出現忽快忽慢的極端現象。限制樹上所有節點,其左右兩子樹的高度差不會超過一,此舉造成整棵樹的高度為O(logN)。每當插入、刪除,高度差超過一,就馬上在高度落差太大的子樹上面,對子樹的根使用右旋轉或左旋轉,把左右子樹的高度差維持在一以下。
插入、刪除、搜尋的時間複雜度為O(logN)。旋轉的時間複雜度是O(1),最多旋轉兩次就會平衡。至於空間複雜度仍是O(N)。
Red-Black Tree
紅黑樹的功效等同平衡二元搜尋樹,但是效率更勝一籌。
http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf
可以直接使用STL的set、map。
Skip Lists
置放大量數字並進行排序的資料結構。不用樹狀結構,而改用高度不同的Linked List來連接資料。資料結構在概念上可以表示成Left Child-Right Sibling Binary Tree的模式。時間複雜度與空間複雜度與Binary Search Tree皆相同,但是實際運作效率比Binary Search Tree還要好。
Splay Tree
splay是反覆旋轉兩次,把當前節點旋轉至根。插入、刪除後就立即splay;儘管樹沒有完全平衡,插入、刪除的均攤時間複雜度仍是O(logN)。實際運作效率比AVL Tree稍快。
運用splay就很容易拆接子樹,拆接子樹的時間複雜度為O(logN)。
Heap
程度★ 難度★★★
Binary Heap
常簡稱Heap,置放大量數字,並且可以隨時求最大(小)值的資料結構。插入、刪除、求極值的時間複雜度為O(logN),資料可以動態增加和減少。
可以直接使用STL的priority_queue。
UVa 501 10587 11997
Binomial Heap
置放大量數字,並且可以隨時求最大(小)值的資料結構。遞迴結合多個高度不同的Binomial Tree,所構成的一個資料結構,以抽象化的角度來看像是一個二進位系統。兩個Binomial Heap在結合的時候,原理就像是在做二進位加法一樣,因而得此名。
Fibonacci Heap
置放大量數字,並且可以隨時求最大(小)值的資料結構。一個規則極其詭異的結構,重點在於他有一個特殊功能叫做decrease key,可以搜尋數字,並且還可以減少該數字,時間複雜度均攤之後僅有O(1)。另外,插入的時間複雜度均攤之後僅有O(1)。
Quake Heap
置放大量數字,並且可以隨時求最大(小)值的資料結構。與Fibonacci Heap功能相同,但是據說簡單很多。
http://www.cs.uwaterloo.ca/~tmchan/pub.html
Binary Search Tree
BST也可以當作Heap使用。
Heap::push = BST::insert Heap::pop = BST::get_extremum + BST::delete Heap::peek = BST::get_extremum Heap::merge = BST::merge
Heap的每一項操作,都可以用BST兜出來,時間複雜度完全一樣,唯一的例外是:Heap可以預先偷看將要彈出來的元素,僅需O(1)時間;BST要偷看,就等同於需要花費O(logN)時間搜尋最小值或最大值。
Treap
Binary Search Tree與Binary Heap進行合體術變成的東西。
令數字有額外的優先次序,同時具有「排序優先次序」的功能與「求最大(小)值」的功能。
Cartesian Tree
令數字具有額外的索引值。Cartesian Tree是二元樹,根的數值大(小)於左右子樹,根的索引值區隔左右子樹。
Cartesian Tree可以看作是一種Treap。
Cartesian Tree是以online方式建立,由左到右讀取陣列元素,每讀取一個元素就建立一個節點,整棵樹剛好會來回遍歷一次。可使用stack來實作,確保stack的元素是照順序排好的。
van Emde Boas Tree(vEB Tree)
置放大量正整數(與零)的資料結構,並且可以作為Double Ended Priority Queue。利用Divide and Conquer,將0到K-1的整數分為sqrt(K)個區塊,每個區塊的範圍大小為sqrt(K),接著各區塊各自遞迴下去。
以另一個角度來看,就是把一個整數從中間切開,成為高位數字部份和低位數字部份,把高位數字抽取出來,作為索引值,找出對應的陣列格子,並遞迴下去以儲存低位數字。跟Trie有點像。
每次搜尋、插入、刪除為O(loglogK),總時間複雜度為O(NloglogK),其中N為正整數個數,K為這些正整數的最大值。
速度是快了那麼一點,然而缺點是記憶體用很兇,空間複雜度為O(K)。【待補分析】
其實我們也可以使用Counting Sort來紀錄正整數,速度還比vEB Tree快,只不過Counting Sort不能動態排序罷了。
若要作為Double Ended Priority Queue,則在每個節點加上兩個變數,紀錄該子樹目前擁有的數字的大小範圍。