【老鼠看不懂的數據結構】FHQTreap 初識

来源:https://www.cnblogs.com/o2mouse/p/18206986
-Advertisement-
Play Games

title: Django與前端框架協作開發實戰:高效構建現代Web應用 date: 2024/5/22 20:07:47 updated: 2024/5/22 20:07:47 categories: 後端開發 tags: DjangoREST 前端框架 SSR渲染 SPA路由 SEO優化 組件庫 ...


Treap 弱平衡的隨機性很強的老鼠看不懂的平衡樹

Q:為什麼叫 Treap?
A:看看二叉搜索樹(BST)和堆(Heap),組合起來就是 Treap

其中,二叉搜索樹的性質是:
左子節點的值 (val) 比父節點小
右子節點的值 (val) 比父節點大

如果這些節點的值都一樣,這棵樹就會退化成一顆(?)鏈。

對, 我知道你在想什麼——並查集。
雖然都會被傻老鼠亂搞退化成鏈,但優化方式大有不同。

優化 - Priority - 玄學抽卡

笨蛋老鼠可能還有疑惑,為什麼鏈是naive的而樹是超棒的呢?
從OIwiki偷兩張圖來解釋:
看,這是一顆正常的樹,基本上可以看作滿二叉樹,一次查詢只需要 \(O(\log_2{n})\)的時間複雜度
正常的樹
這是一顆(?)被老鼠亂搞以後形成的鏈,一次查詢的時間複雜度劣化到了 \(O(n)\)
鏈
那麼,既然屑老鼠已經說了這棵樹有堆的性質,所以就要給每一個結點上一個隨機的優先順序\(Priority\)
讓它同時成為一個堆,在雙重特征下保持完全二叉樹的形狀

PS:傻老鼠腦子糊塗了,首先樹得滿足BST的性質,然後空下來時維護Heap的性質

大功告成,現在該知道節點裡面應該放什麼了。

node節點(為什麼不用類封裝?因為老鼠不會)
struct node{
	node *child[2];
	int val,ranf,cnt,siz;
	node(int __val) : val(__val), cnt(1), siz(1){
		child[0] = child[1] = nullptr;//左右兒子初始化 
		ranf = rand();//玄學抽卡 
	} 
	node(node *__node){
		val = __node->val, ranf = __node->ranf, cnt = __node->cnt, siz = __node->siz;
	}
	void ud_siz(){
		siz = cnt;
		if(child[0] != nullptr)siz += child[0]->siz;
		if(child[1] != nullptr)siz += child[1]->siz; 
	}
};

Treap的重要操作 Split & Merge

分裂過程接受兩個參數:根指針 \(\textit{cur}\)、關鍵值 \(\textit{key}\)。結果為將根指針指向的 treap 分裂為兩個 treap,第一個 treap 所有結點的值\(\textit{val}\)小於等於 \(\textit{key}\),第二個 treap 所有結點的值大於 \(\textit{key}\)
該過程首先判斷 \(\textit{key}\) 是否小於 \(\textit{cur}\) 的值,若小於,則說明 \(\textit{cur}\) 及其右子樹全部大於 \(\textit{key}\),屬於第二個 treap。當然,也可能有一部分的左子樹的值大於 \(\textit{key}\),所以還需要繼續向左子樹遞歸地分裂。對於大於 \(\textit{key}\) 的那部分左子樹,我們把它作為 \(\textit{cur}\) 的左子樹,這樣,整個 \(\textit{cur}\) 上的節點都是大於 \(\textit{key}\) 的。
相應的,如果 \(\textit{key}\) 大於等於 \(\textit{cur}\) 的值,說明 \(\textit{cur}\) 的整個左子樹以及其自身都小於 \(\textit{key}\),屬於分裂後的第一個 treap。並且,\(\textit{cur}\) 的部分右子樹也可能有部分小於 \(\textit{key}\),因此我們需要繼續遞歸地分裂右子樹。把小於 \(\textit{key}\) 的那部分作為 \(\textit{cur}\) 的右子樹,這樣,整個 \(\textit{cur}\) 上的節點都小於 \(\textit{key}\)
下圖展示了 \(\textit{cur}\) 的值小於等於 \(\textit{key}\) 時按值分裂的情況. ——OIWIKI

老鼠才不會解釋,因為老鼠沒腦子。

Split
pair<node *, node *> split(node *rt,int key){
	if(rt == nullptr)return {nullptr, nullptr};
	if(rt->val <= key){
	auto temp = split(rt->child[1],key);
		rt->child[1] = temp.first;
		rt->ud_siz();
		return {rt,temp.second};
	}
	else{
		auto temp = split(rt->child[0],key);
		rt->child[0] = temp.second;
		rt->ud_siz();
		return {temp.first,rt};
	}
	}

合併過程接受兩個參數:左 treap 的根指針 \(\textit{u}\)、右 treap 的根指針 \(\textit{v}\)。必須滿足 \(\textit{u}\) 中所有結點的值小於等於 \(\textit{v}\) 中所有結點的值。一般來說,我們合併的兩個 treap 都是原來從一個 treap 中分裂出去的,所以不難滿足 \(\textit{u}\) 中所有節點的值都小於 \(\textit{v}\)
在旋轉 treap 中,我們藉助旋轉操作來維護 \(\textit{priority}\) 符合堆的性質,同時旋轉時還不能改變樹的性質。在無旋 treap 中,我們用合併達到相同的效果。
因為兩個 treap 已經有序,所以我們在合併的時候只需要考慮把哪個樹「放在上面」,把哪個「放在下麵」,也就是是需要判斷將哪個一個樹作為子樹。顯然,根據堆的性質,我們需要把 \(\textit{priority}\) 小的放在上面(這裡採用小根堆)。
同時,我們還需要滿足搜索樹的性質,所以若 \(\textit{u}\) 的根結點的 \(\textit{priority}\) 小於 \(\textit{v}\) 的,那麼 \(\textit{u}\) 即為新根結點,並且 \(\textit{v}\) 因為值比 \(\textit{u}\) 更大,應與 \(\textit{u}\) 的右子樹合併;反之,則 \(\textit{v}\) 作為新根結點,然後因為 u 的值比 \(\textit{v}\) 小,與 v 的左子樹合併。——OIWIKI
老鼠懶,自己看。

Merge
node *merge(node *u,node *v){
		if(u == nullptr && v == nullptr)return nullptr;
		if(u != nullptr && v == nullptr)return u;
		if(u == nullptr && v != nullptr)return v;
		if(u->ranf < v->ranf){
			u->child[1] = merge(u->child[1],v);
			u->ud_siz();
			return u;
		}
		else{
			v->child[0] = merge(u, v->child[0]);
			v->ud_siz();
			return v;
		}
	}

不好用,才不用——⑨老鼠

作為笨蛋老鼠,能看懂這個東西怎麼用才奇怪吧,只能分裂和合併的數據結構,鬼才用嘞!
笨蛋老鼠!這個東西可以整很多活的

查詢小於等於val的數的個數:考察根節點的值和val,如果val小於根節點,遞歸進左子樹,否則遞歸進右子樹。
插入val:先查詢Rank(val),然後按照rank把整個TreapSplit成兩個,把val做成一個新節點,Merge到裡面。
刪除val:先查詢Rank(val),然後按照rank把整個TreapSplit成三個,刪除需要的點,最後Merge剩下兩個。
查詢第K個值:把整個TreapSplit成三個,輸出需要的值,最後合併起來。

好啊,你的區間呢?

誇下的海口終究會被打qwq。
發現了沒,這些個操作全是區間的,想想你的線段樹怎麼區間修改優化的?
\(LazyTag\)救我狗命對吧!
所以,直接打標記建線段樹到處亂搞,怎麼在樹鏈剖分上整的活大多數都能整!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基於rake的爬取代碼 require 'nokogiri' require 'json' require 'open-uri' namespace :spider_sbi_code_info do task table_data: :environment do options = Seleniu ...
  • 正文 中午睡得真的好香,一點都不想起床上班。我要鬧了!為什麼世界上會有上班這麼邪惡的事情。 今天開了一個一般戶。剛開始資料散成一堆,得有二十張。各種找。最後還是出了點岔子,讓客戶沒簽字就跑了。算了,無所謂了。 上午綜合各家 AI 和已有的宣傳稿,寫了一篇稿子發出去。能不能被選中投上那就不是我的事了。 ...
  • 在做商城時生成隨機一個頭像,找了一下發現用首個字元直接生成的類也不錯,和用第三方外鏈的話還是有不同的,第三方雖然圖片比較多,但是會有超時問題,所以用首字母生成方式本地搞,代碼如下: 點擊查看代碼 1、方法調用測試 letter_avatar("壹零柒") 2、生成圖片方法 function lett ...
  • Qt具有跨平臺的特性,即Qt數據結構與演算法庫本身跨平臺和編譯腳本(.pro)跨平臺。在同時具有Windows下和Linux開發的需求時,最好的建議是使用QtCreator來開發,雖然也可以使用其他的IDE配合CMake等方式,但使用QtCreator更加方便,並且操作環境完全一致。QtCreator ...
  • 使用Spring Boot開發API的時候,讀取請求參數是服務端編碼中最基本的一項操作,Spring Boot中也提供了多種機制來滿足不同的API設計要求。 接下來,就通過本文,為大家總結6種常用的請求參數讀取方式。如果你發現自己知道的不到6種,那麼趕緊來查漏補缺一下。如果你知道的不止6種,那麼告訴 ...
  • 亮數據,適合大模型數據準備的可視化高效率數據採集工具。 一、大模型訓練需要數據 大模型數據處理的流程,分為數據採集、數據清洗、數據評估和指令數據標註四個主要階段,而這些階段中最重要的就是數據採集階段,需要海量的數據才能讓大模型涌現智能。 訪問點擊: 亮數據加速數據採集。 數據採集 涉及多種數據源,包 ...
  • 相關參考 https://leejjon.medium.com/how-to-allow-cross-origin-requests-in-a-jax-rs-microservice-d2a6aa2df484 https://stackoverflow.com/questions/28065963/ ...
  • 配置 NGINX 和 NGINX Plus 作為 Web 伺服器 設置虛擬伺服器 在 NGINX Plus 配置文件中,必須包含至少一個 server 指令來定義一個虛擬伺服器。 當 NGINX Plus 處理請求時,首先選擇將服務於該請求的虛擬伺服器。 http { server { # 伺服器配 ...
一周排行
    -Advertisement-
    Play Games
  • 問題 有很多應用程式在驗證JSON數據的時候用到了JSON Schema。 在微服務架構下,有時候各個微服務由於各種歷史原因,它們所生成的數據對JSON Object屬性名的大小寫規則可能並不統一,它們需要消費的JSON數據的屬性名可能需要大小寫無關。 遺憾的是,目前的JSON Schema沒有這方 ...
  • 首先下載centos07鏡像,建議使用阿裡雲推薦的地址: https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/?spm=a2c6h.25603864.0.0.59b5f5ad5Nfr0X 其實這裡就已經出現第一個坑了 centos 07 /u ...
  • 相信很多.NETer看了標題,都會忍不住好奇,點進來看看,並且順便準備要噴作者! 這裡,首先要申明一下,作者本人也非常喜歡Linq,也在各個項目中常用Linq。 我愛Linq,Linq優雅萬歲!!!(PS:順便吐槽一下,隔壁Java從8.0版本推出的Streams API,抄了個四不像,一點都不優雅 ...
  • 在人生的重要時刻,我站在了畢業的門檻上,望著前方的道路,心中涌動著對未來的無限憧憬與些許忐忑。面前,兩條道路蜿蜒伸展:一是繼續在職場中尋求穩定,一是勇敢地走出一條屬於自己的創新之路。儘管面臨年齡和現實的挑戰,我仍舊選擇勇往直前,用技術這把鑰匙,開啟新的人生篇章。 迴首過去,我深知時間寶貴,精力有限。 ...
  • 單元測試 前言 時隔多個月,終於抽空學習了點新知識,那麼這次來記錄一下C#怎麼進行單元測試,單元測試是做什麼的。 我相信大部分剛畢業的都很疑惑單元測試是乾什麼的?在小廠實習了6個月後,我發現每天除了寫CRUD就是寫CRUD,幾乎用不到單元測試。寫完一個功能直接上手去測,當然這隻是我個人感受,僅供參考 ...
  • 一:背景 1. 講故事 最近在分析dump時,發現有程式的卡死和WeakReference有關,在以前只知道怎麼用,但不清楚底層邏輯走向是什麼樣的,藉著這個dump的契機來簡單研究下。 二:弱引用的玩法 1. 一些基礎概念 用過WeakReference的朋友都知道這裡面又可以分為弱短和弱長兩個概念 ...
  • 最近想把ET打表工具的報錯提示直接調用win系統彈窗,好讓策劃明顯的知道表格哪裡填錯數據,彈窗需要調用System.Windows.Forms庫。操作如下: 需要在 .csproj 文件中添加: <UseWindowsForms>true</UseWindowsForms> 須將目標平臺設置為 Wi ...
  • 從C#3開始,拓展方法這一特性就得到了廣泛的應用。 此功能允許你能夠使用實例方法的語法調用某個靜態方法,以下是一個獲取/創建文件的靜態方法: public static async Task<StorageFile> GetOrCreateFileAsync(this StorageFolder f ...
  • 在Windows 11下,使用WinUI2.6以上版本的ListView長這樣: 然而到了Win10上,儘管其他控制項的樣式沒有改變,但ListViewItem變成了預設樣式(初代Fluent) 最重大的問題是,Win10上的HorizontalAlignment未被設置成Stretch,可能造成嚴重 ...
  • 前言 周六在公司加班,幹完活後越顯無聊,想著下載RabbiitMQ做個小項目玩玩。然而這一下就下載了2個小時,真讓人頭痛。 簡單的講一下如何安裝吧,網上教程和踩坑文章還是很多的,我講我感覺有用的文章放在本文末尾。 安裝地址 erlang 下載 - Erlang/OTP https://www.erl ...