一:概述
一些工程應用只會使用教科書式的標準資料結構,但是也會有些應用需要對現有的資料結構進行少許的創新和改造,只有很少的情況會創造全新的資料結構。
二:動態順序統計
順序統計:n個元素中第i個順序統計量,就是具有第i小關鍵字的元素。對於乙個無序的集合,可以在
o(n)
的時間內得到任意的順序統計量。
利用紅黑樹,可以在
o(lg n)
的時間內確定任意順序統計量,同時也能在
o(lg n)
時間內確定元素的秩,紅黑樹中,元素的秩代表中序遍歷時輸出的位置。
支援快速順序統計操作的資料結構,是在紅黑樹的基礎上進行了少許的改造,形成了順序統計樹。順序統計樹就是紅黑樹,只不過樹種每個節點,除了具有紅黑樹的key, color, left, right, p等五個性之外,還附加了另外的性質:
size
,它表示以
x為根的子樹(包括
x本身)的內(不計哨兵)節點數。並且定義哨兵的
size為0
,所以有下面的性質:
x.size = x.left.size + x.right.size + 1
下圖就是一顆順序統計樹,每個節點中,上邊是key,下邊是size:
1:確定給定秩的元素
利用size資訊,可以在o(lg n)時間內確定給定秩的元素:os-select。它返回以x為根的子樹中,指向第i小的關鍵字元素的指標,**如下:
os-select(x, i)
r= x.left.size+1
if i == r
return x
else if i < r
return os-select(x.left, i)
else
return os-select(x.right, i-r)
該演算法中,r表示以x為根的子樹中,節點x的秩。如果i小於r,則需要在左子樹中找第i小的元素,如果i大於r,則需要在右子樹中找第i-r小的元素。
os-select的總時間與樹的高度成正比,所以該演算法的時間複雜度為o(lg n)。
2:確定乙個元素的秩
給定元素x的指標,可以用os-rank返回元素x在樹中的秩,**如下:
os-rank(x)
r = x.left.size+1
y = x
while y != t.root
if y = y.p.right
r= r+y.p.left.size+1
y= y.p
returnr
在該演算法中,r表示以y為根的子樹中,x的秩。如果y為父節點的左孩子,則x在y為根的子樹中的秩也是在y.p為根的子樹中的秩。如果
y為父節點的右孩子,則x在
y.p為根的子樹中的秩,還需要加上
y.p為根的子樹中,左分支的節點數和
y.p本身所佔的位置,才是x在
y.p為根的子樹中秩。所以,最後y==root時,r就是在整個樹中的秩。
該演算法的時間複雜度為o(lgn)。
3:對子樹規模的維護
利用size屬性,可以很快的計算出順序統計量,但是,如果在統計數的改變過程中,不能在合理的時間內維護size屬性,那麼新增size屬性將是無意義的,比如,在插入或者刪除的過程中,如果維護元素
size
屬性的時間超過了
o(lg n)
,則size
變得無意義。下面就說明,插入和刪除操作,會在o(lg n)時間內完成size的維護。
插入操作,包括兩個階段,第一階段從根節點開始沿樹下降,將新節點插入到葉節點中。第二階段是沿樹上公升,做一些變色或者旋轉操作維護紅黑樹的性質。在第一階段中,為了維護size的屬性,可以將從根節點到新插入的葉子節點的路徑上的每個節點的size加1即可。這個操作的時間複雜度為o(lg n)。在第二階段,只有旋轉操作才會導致size的屬性變化,而紅黑樹的插入操作的旋轉次數最多為2次。旋轉操作是一種區域性操作,只會使得2個節點的size屬性失效,比如左旋**中,增加下面兩行就能維護size屬性:
y.size = x.size
x.size = x.left.size + x.right.size +1
左旋操作如下圖:
右旋操作也類似,所以對於插入操作來說,增加size屬性後,插入的時間複雜度還是o(lg n)。
刪除操作也是分兩個階段,第一階段找到y,要麼將y刪除,要麼將y上移,所以遍歷一條從節點y所在位置到根節點的路徑,將每個節點的size減1即可。第二階段是為了維護紅黑樹性質的變色和旋轉操作,因刪除操作最多需要3次旋轉,因而,同插入操作一樣,刪除操作的時間複雜度也是o(lg n)。
三:如何擴張資料結構
對基本的資料結構進行擴張,以支援一些附加功能,在演算法設計中是很常見的,下面是擴張一種資料結構的基本步驟:
a:選擇一種基本資料結構;
b:確定資料結構中需要維護的附加資訊;
c:檢驗基礎資料結構上的基本修改操作是否能維護附加資訊;
d:設計新的操作。
比如在利用紅黑樹進行順序統計的工作中,a:選擇了紅黑樹作為基本資料結構。b:在紅黑樹節點中新增了size屬性。c:保證插入和刪除操作仍能在o(lg n)時間內維護size屬性。d:設計順序統計的操作。
當紅黑樹作為基本資料結構時,可以證明,某些型別的附加資訊總是可以用插入和刪除操作進行有效的維護:如果
f是紅黑樹元素的附加屬性,假設對於任意節點x,
f的值僅僅依賴於
x, x.left, x.right
的資訊,還可能包括
x.left.f
和x.right.f
。那麼在插入和刪除操作中,可以在
o(lg n)
時間內維護
f的屬性。這是因為
f屬性的變動,只會影響到
x的祖先,所以修改
x.f,只需要更新
x.p.f, x.p.p.f……
。如此沿樹向上即可。
四:區間樹
區間[a,b]便於表示占用一連續時間的事件,例如查詢由時間區間構成的資料庫,找出給定時間內發生了什麼事。
可以用物件i表示區間[a,b], i.low = a;i.high = b。對於兩個區間i和j來說,如果:i.low <= j.high並且j.low <= i.high,則表示兩個區間i和j重疊。
區間樹是一種紅黑樹,樹中的元素表示乙個區間,該樹支援的操作有:插入,刪除,查詢,其中,查詢操作是返回乙個指向x的指標,x與給定的區間i重疊。如果x不存在,則返回t.nil。
下圖為一些列的區間:
為了支援查詢重疊區間的操作,:
a:選擇紅黑樹作為基本資料結構,每個節點表示乙個區間int,節點的關鍵字為區間的低點,也就是x.int.low。所以該資料結構中,中序遍歷就能按照low的次序列出各區間。
b:附加資訊為max,max表示了以x為根的子樹中,所有區間的端點high的最大值。
c:x的max值:x.max =max(x.int.high, x.left.max, x.right.max), 所以符合上面的定理,因而插入和刪除操作能在o(lgn)時間內完成。
d:插入和刪除操作與紅黑樹的一樣,這裡只需要設計search操作。它可以找出樹中,與給定區間i重疊的節點x,如果x不存在,返回t.nil。**如下:
interval-search(t, i)
x = t.root
while x != t.nil and i does not overlap x.int
if x.left != t.nil and x.left.max >= i.low
x= x.left
else x = x.right
return x
該演算法能保證,如果樹中存在與i重疊的區間,該區間一定能被找到。該演算法在迴圈中,判斷x.left != t.nil and x.left.max >= i.low,如果不滿足x.left.max >= i.low,則說明左子樹中的節點的high值,都小於i的low值,所以左子樹中不存在與i重疊的區間,因而在右子樹中。
如果滿足了x.left.max >= i.low屬性,則有可能左右子樹中都會包含與i重疊的區間。首先在左子樹中找,如果左子樹中沒有的話,右子樹中一定也不存在,因為樹的關鍵字為low值,左子樹的low值肯定比右子樹小。
資料結構的擴張 演算法導論第14章(194)
偽 解釋 為明白os select是如何操作的,在上圖所示的順序統計圖上查詢第17小元素的查詢過程。以x為根開始,其關鍵字為26。i 17.因為在26的左子樹大小為12,故他的秩為13,因此,秩為17的節點是26的右子樹第17 13 4小得瑟元素。遞迴呼叫後,x為關鍵字41的節點,i 4,因為41的...
《演算法導論》第14章 資料結構的擴張 2
在上一節中,我們為樹結點新增size域表示每顆子樹的大小,即包含的結點個數,擴張了 二叉查詢樹為其增加順序統計量的查詢功能。更為自然的想法是直接新增順序統計量rank域 到每個樹結點上。這一節我們就來看下在這樣的設計下,如何擴張來完成上一節相同的功能。當我們插入乙個結點到二叉樹中,假設它的順序統計量...
《演算法導論》讀書筆記之第14章 資料結構的擴張
前言 通常我們會遇到一些問題,採用一些標準的資料結構,如雙鏈表 雜湊表或二叉查詢數時,不能夠滿足操作要求,需要對這些資料結構進行擴張,新增一些額外的資訊使得能夠完成新的操作。附加的資訊需要對資料結構的某些操作進行調整,這個是非常關鍵的步驟,決定著資料結構擴張是否能夠實現。本章主要討論了紅黑樹結構的擴...