樹——笛卡爾樹

樹簡介

笛卡爾樹是平衡二叉樹的一種,他和我們之前學習的AVL樹一樣通過旋轉來調整蓖康,使平衡樹達到平衡態(tài)蓉坎。不過,不同的是:笛卡爾樹除了用于決定節(jié)點向左/右分布的key外唠粥,還有個value疏魏,用來決定節(jié)點的層級先后

笛卡爾樹的分布存在以下特點:

  • key:分布遵循BST的規(guī)律晤愧,即左子樹key<根key<右子樹key
  • value:分布遵循堆的規(guī)律大莫,即父value < 子value/子value < 父value
    • 父value < 子value: 最小堆
    • 子value < 父value:最大堆

樹操作算法概述

笛卡爾樹我們只介紹建樹的過程。忽略樹的修改操作官份。

建樹

笛卡爾樹的key遵循二叉搜索樹的特點只厘。value遵循堆的特點。所以我們先把數據按照key從小到大進行排序舅巷⌒赴迹【以此為例,如果你硬要從大到小也是一樣的道理】

接下來我們只需要從第一個節(jié)點開始串下去就行了悄谐,后面的肯定比前面的大介评,所以在前面節(jié)點的右子樹或者父節(jié)點,而是右子樹還是父節(jié)點,通過value判定们陆。

一個二叉樹大概長下面這樣【網上盜的圖寒瓦,不是搜索樹,大概看個結構就行】:

1.png

既然不是當右兒子就是當爸爸坪仇,我們在構建過程中直接把根節(jié)點到最右子樹存下來杂腰,方便為新來的節(jié)點找到進入的位置

存儲的東西如下:

2.png

每新來一個節(jié)點椅文,我們拿著value一個一個比較喂很,因為父value要比子value大。而且有一個特點皆刺,新插入一個點后少辣,原來他右下位置的點就變成他的左子樹了,右鏈砍掉了一部分羡蛾。所以有兩種思路:

  1. 每次從根往右遍歷
    • 和當前的節(jié)點比較漓帅,當前節(jié)點比插入值大就繼續(xù)向后遍歷,直到遇到一個比他小的痴怨,然后插到他的前面忙干,他后面的節(jié)點做插入點的左子樹
  2. 每次從右下角遍歷
    • 和當前的節(jié)點比較,當前節(jié)點比插入值小就過浪藻,繼續(xù)向前遍歷捐迫,找到對應位置,插進去

兩種方法其實都是一個思路:遍歷爱葵,直到找到合適位置施戴,然后插入。

在網上找到的很多源碼都提到了O(n)時間復雜度的笛卡爾樹構造方法钧惧,說是借助棧暇韧,他們一般用的是我介紹的第二種思路:和棧頂比較勾习,不行就扔掉浓瞪,插進去后入站,然后新的右鏈就又在棧中了巧婶。

如果你思路清晰了乾颁,完全可以不借助棧,直接從根節(jié)點右子樹右子樹的遍歷就行艺栈,都不用維護棧了英岭。

源碼

直接放項目的地址了,我是用的java寫的增刪操作湿右,沒有用C诅妹。還有就是沒有專門為這個建git,源碼在com.gateway.learn.tree下面。github地址:https://github.com/LiPengcheng1995/gateway-parent.git

樹應用場景

官方話:

笛卡爾樹是一種特定的二叉樹數據結構吭狡,可由數列構造尖殃,在范圍最值查詢、范圍top k查詢(range top k queries)等問題上有廣泛應用划煮。它具有堆的有序性送丰,中序遍歷可以輸出原數列。

(用數組下標當key)

自己的話:

笛卡爾樹我認為其實最重要的并不是key弛秋,而是value

key存在的意義是為了讓數列中相鄰的節(jié)點繼續(xù)相鄰器躏,比較形象一點的形容就是只看key的話可以把key當作數列的下標,然后從中間把這個數列拎起來蟹略,就形成了樹結構登失。

value存在的意義你可以看作數組中存的值,你給整個數列做了一個排序后的存儲科乎,只不過你把排序結果放在了樹的上下結構中壁畸,節(jié)點下標沒變。

問題一

這個樣一個問題:求[a,b]范圍中的最大值茅茂,你只需要找到a,b節(jié)點的LCA(Lowest Common Ancestor)捏萍,即最近公共祖先。

思路分析

  1. 構建笛卡爾樹
  2. 依靠二叉排序樹的特點快速定位LCA
  3. 返回查到的LCA的value

本文算法時間復雜度分析

  1. 構建笛卡爾樹 O(n)空闲。(因為以下標為key令杈,不需要重新排序了,用棧來輔助理解就是每個元素只會有一次進棧)
  2. 依靠二叉排序樹特點快速定位LCA O(log2(n))【平衡二叉樹樹深碴倾,雖然笛卡爾樹的平衡不一定有VAL樹那么平逗噩,但是大概還是平的】
    • this.key < a-->右邊走
    • this.key > b --->左邊走
    • 遇到的第一個a<this.key<bthis.value就是結果
  3. return this.value

所以時間復雜度為O(n)跌榔。當然异雁,如果多次查詢只需要一次建樹,這就很爽了僧须,時間復雜度為O(log2(n))纲刀。

傳統(tǒng)算法時間復雜度分析

順序遍歷一遍,記一下最大就行担平,時間復雜度為O(n)示绊。

比較

本文算法優(yōu)點:

  1. 多次查詢只需一次建樹
  2. 查詢效率穩(wěn)定,(已經構建笛卡爾樹后暂论,區(qū)間范圍越大查詢效果往往越快)

本文算法缺點:

  1. 存儲樹結構有一一定開銷
  2. 不穩(wěn)定面褐,存在極度變態(tài)數列情況下整棵樹被拉成一個鏈的可能

問題二

這樣一個問題:已知一個無序數組,我給你其中一個下標取胎,你要告訴我這個下標對應的值是多大范圍內的最大/小值

思路分析

  1. 構建笛卡爾樹展哭,如果求最大值,就按照最大堆構建【后面都按照最大來講,最小也是同理】
  2. 笛卡爾樹看下標是樹匪傍,看值是堆坝咐,找到對應的點,它下面的值都比它小析恢,找到他的子樹中的最左邊點墨坚、最右邊點即可

本文算法時間復雜度

  1. 構建樹,需要O(n)
  2. 找節(jié)點對應子樹的最大最小映挂,即是兩個范圍點【找最左/最右子樹即可】泽篮,復雜度為樹深度,都是O(log_2(n))

綜上柑船,復雜度為O(n)

問題三

這個樣一個問題:求[a,b]范圍中的第n大的值帽撑。

思路分析

  1. 構建笛卡爾樹
  2. 依靠二叉排序樹的特點快速定位LCA
  3. 看LCA下面一層夠不夠數
    • 夠就找到第n-1大的
    • 不夠就從再下一層找,設本層有m個點
      • 這一層夠就找第n-m-1大的
      • 不夠再去下一層鞍时。亏拉。。逆巍。及塘。。

時間復雜度分析

  1. 構建笛卡爾樹 O(n)
  2. 找LCA O(log2(n))
  3. 找第n大的锐极,由于樹每靠上一層笙僚,本曾的節(jié)點數以指數倍減少,所以我們可以近似看成常數(可以灵再。肋层。。吧翎迁,總之需要比較的很少了)栋猖、

所以時間復雜度為O(n)。當然汪榔,如果多次查詢只需要一次建樹蒲拉,這就很爽了,時間復雜度為O(log2(n))揍异。

傳統(tǒng)算法時間復雜度分析

得對這一段區(qū)間進行排序全陨,時間復雜度為O(nlog2(n))

比較

如果區(qū)間隨便給爆班,這么查的話衷掷,還是本文的算法好。缺點和上一個問題一樣就不提了柿菩。

還有一個明顯的缺點戚嗅,就是如果對同一個區(qū)間你經常查top k 。直接用傳統(tǒng)方法排個序反而比較快∨嘲看自己需要什么了替久。

問題四

給出n個非負的整數,表示直方圖中每個條的高度躏尉,且每個條的寬度均為1蚯根,找出這些條覆蓋的最大面積的矩形。

3.png

思路分析

  1. 構建笛卡爾樹胀糜,(最小堆)
  2. 后序遍歷颅拦,將每個節(jié)點的所有子節(jié)點數目存在這個節(jié)點里,并求出子節(jié)點數目和此節(jié)點value的乘積教藻。找到乘積最大的那個結果

幫助理解:

  • 子節(jié)點數目 = 這個節(jié)點的value是多少個節(jié)點中最小的 = 涂色長方形的底邊寬是多少
  • 此節(jié)點value = 這n個節(jié)點的高度最小值 = 涂色長方形的高

注意:

  • 因為是按照下標為key進行排的距帅,原來相鄰的長條現在還是相鄰,所以才能這么等效

時間復雜度分析

  1. 構建笛卡爾樹 O(n)

  2. 后續(xù)遍歷括堤、計算碌秸、存儲,因為只遍歷一遍悄窃,所以時間復雜度是 O(n)

思路分析(基礎)

上面的雖然說著通順讥电,但是正常人的思路并不是這樣的。你可以這樣想轧抗,底和高都變化允趟,我們需要先找兩者的關聯,先構建出一個合適的計算公式:

從底入手

我們需要遍歷底邊的所有匹配段情況鸦致,并算出區(qū)域內的最低點【數組一端區(qū)間內的最小值】潮剪,這樣需要構建笛卡爾樹,因為匹配的不確定性分唾,我們需要枚舉底邊的(1+2+...+n)種情況抗碰,復雜度是O(n^2)。當然绽乔,如果你硬要把每次確定最小值的操作算到求高的操作中去的話弧蝇,就是 O(n^2log2(n))

從高入手

前面一個思路折砸,我們解決的最大的問題是把范圍確定后的定高問題解決了看疗,但是確定底邊取值范圍開銷太大了,達到了O(n^2log2(n))級別睦授。

遇到了這么亂的思路两芳,一般有兩個可能:

  1. 問題就是挺亂的。就像我們某些不合理的需求一樣去枷,屎一樣的需求怖辆,再怎么捋代碼也是亂七八糟
  2. 我們抽象的不對

我們抽象出了底邊x和高y,然后求xy的乘積最大值是复。我們求x時即使使用普通的排序方法,不用笛卡爾樹也還有O(n)竖螃,但是找底邊時我們直接就沖著O(n^2)去了淑廊,這就給人不協調的感覺了。

所以我們進行更近一步的抽象特咆,設從x到y(tǒng)季惩,取這范圍內的長方形,設高的最小值為z腻格,這樣x,y,z的判斷就都是O(n)了蜀备,當然,直接一下荒叶,復雜度直接就是O(n^3)了碾阁。

我們需要找到三個變量之間的關系

如果我們確定一個x,后面y每個取值對應的高度都有可能變化些楣,變化根據數組來變脂凶,沒有規(guī)律,而沒有規(guī)律就意味著遍歷愁茁,意味著低效蚕钦。

如果確定y,是一樣的道理鹅很。

如果我們確定z嘶居,那就無所謂了,x和y往開的撐就行了促煮。撐的越開邮屁,面積越大。

所以我們從z入手菠齿,算出每個z的值都能對應最大多大的(y-x)佑吝,也就是z值都是多大區(qū)間內的最小值。此處回到問題二绳匀。只需要遍歷即可芋忿,所以時間復雜度是:

  1. 構建笛卡爾樹O(n)
  2. 一一求出每個高度對應的底邊最大值【對應的最大最小的范圍】(n個,每個O(log_2(n))疾棵,也就是O(n)*2O(log_2(n))=O(2nlog_2(n))
  3. n個方案戈钢,在一一計算時順手存下最小的值以及計算方案即可,無需多余的時間復雜度

所以時間復雜度降低到了O(2nlog_2(n))是尔。

文獻參考

  1. https://www.cnblogs.com/pushing-my-way/archive/2012/08/24/2653709.html
  2. https://blog.csdn.net/pipisorry/article/details/39033269
  3. https://baike.baidu.com/item/%E7%AC%9B%E5%8D%A1%E5%B0%94%E6%A0%91
  4. https://agatelee.cn/2016/07/%E6%A0%91%E7%B1%BB%E9%97%AE%E9%A2%98%E7%AC%9B%E5%8D%A1%E5%B0%94%E6%A0%91/
  5. https://zh.wikipedia.org/wiki/%E7%AC%9B%E5%8D%A1%E5%B0%94%E6%A0%91
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末殉了,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子嗜历,更是在濱河造成了極大的恐慌宣渗,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梨州,死亡現場離奇詭異痕囱,居然都是意外死亡,警方通過查閱死者的電腦和手機暴匠,發(fā)現死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門鞍恢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人每窖,你說我怎么就攤上這事帮掉。” “怎么了窒典?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵蟆炊,是天一觀的道長。 經常有香客問我瀑志,道長涩搓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任劈猪,我火速辦了婚禮昧甘,結果婚禮上,老公的妹妹穿的比我還像新娘战得。我一直安慰自己充边,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布常侦。 她就那樣靜靜地躺著浇冰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪聋亡。 梳的紋絲不亂的頭發(fā)上湖饱,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音杀捻,去河邊找鬼井厌。 笑死,一個胖子當著我的面吹牛致讥,可吹牛的內容都是我干的仅仆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼垢袱,長吁一口氣:“原來是場噩夢啊……” “哼墓拜!你這毒婦竟也來了?” 一聲冷哼從身側響起请契,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤咳榜,失蹤者是張志新(化名)和其女友劉穎夏醉,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體涌韩,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡畔柔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了臣樱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靶擦。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雇毫,靈堂內的尸體忽然破棺而出玄捕,到底是詐尸還是另有隱情,我是刑警寧澤棚放,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布枚粘,位于F島的核電站,受9級特大地震影響飘蚯,放射性物質發(fā)生泄漏赌结。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一孝冒、第九天 我趴在偏房一處隱蔽的房頂上張望柬姚。 院中可真熱鬧,春花似錦庄涡、人聲如沸量承。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撕捍。三九已至,卻和暖如春泣洞,著一層夾襖步出監(jiān)牢的瞬間忧风,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工球凰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狮腿,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓呕诉,卻偏偏與公主長得像缘厢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子甩挫,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容