伸展樹的節點的size域的應用
伸展樹不是平衡二叉樹,但是它的操作均攤是O(
在伸展樹的節點上附加一個size域,用來儲存以該節點為根的子樹的節點總數。這個size域可以用來解決很多問題。例如可以解決區間問題,例如SBT就是用這個size域來計算平衡條件。size域能夠解決的最簡單、最直接的問題就是kth問題——求第k小的數。
在伸展樹上加上size域用來解決POJ2371。
二叉樹節點的結構體定義如下:
int const SIZE = 1111111;
int const LEFT = 0;
int const RIGHT = 1 ;
struct node_t{
int parent; //父節點
int child[2]; //子節點
int sn; //指示本節點是其父親左兒子還是右兒子
key_t key; //鍵
value_t value;//值,這道題裡實際上不需要
int size; //size域
}Node[SIZE]; //Node[0]不使用,用來充當NULL
int toUsed = 0;
這道題中用來充當鍵的就是一個整數,而且存在可能相等的情況。因此,將其出現的先後順序考慮進去,從而確保任意兩個節點具有不同的鍵。因此,鍵不是一個整數,而是一個結構體。同時為這個結構體過載相應的關係運算符。
struct key_t{
int key; //真正的鍵
int order; //出現的順序
key_t(int a=0,int b=0):key(a),order(b){}
};
bool operator < (key_t const&l,key_t const&r){
return l.key < r.key || ( l.key == r.key && l.order < r.order );
}
bool operator == (key_t const&l,key_t const&r){
return l.key == r.key && l.order == r.order;
}
bool operator != (key_t const&l,key_t const&r){
return !( l == r );
}
計算size域所用的函式
inline void _pushUp(int t){
Node[t].size = 1;
int son = Node[t].child[LEFT];
if (son) Node[t].size += Node[son].size;
son = Node[t].child[RIGHT];
if (son) Node[t].size += Node[son].size;
}
伸展操作所用到的函式,在《伸展樹的旋轉和伸展操作》中有介紹。這個地方需要注意的是:要在合適的地方呼叫_pushUp(int)函式來維持size域。
//設定p、t的父子關係,這裡不呼叫_pushUp(int)
inline void _link(int p,int sn,int t){
Node[p].child[sn] = t;
Node[t].parent = p;
Node[t].sn = sn;
}
//旋轉就是重新確定三對父子關係,這裡只維持節點p的size
inline void _rotate(int t){
int p = Node[t].parent;
int sn = Node[t].sn;
int osn = sn ^ 1;
_link(p,sn,Node[t].child[osn]);
_link(Node[p].parent,Node[p].sn,t);
_link(t,osn,p);
_pushUp(p);
}
//伸展,只需在伸展的最後維持節點t的size
void _splay(int t,int p,int&root){
while( Node[t].parent != p ){
int pp = Node[t].parent;
if ( Node[pp].parent != p ){
Node[pp].sn == Node[t].sn ?
_rotate(pp) : _rotate(t);
}
_rotate(t);
}
_pushUp(t);
if ( 0 == p ) root = t;
return;
}
再為伸展樹提供3個輔助函式。
//獲取一個新的可用節點
inline int _newNode(){
++toUsed;
memset(Node+toUsed,0,sizeof(node_t));
return toUsed;
}
//在root樹上查詢鍵為key的節點,parent為其父節點
int _advance(int root,int&parent,key_t key){
if ( 0 == root ) return parent = 0;
int t = root;
parent = Node[t].parent;
while( t && key != Node[t].key ){
parent = t;
t = key < Node[t].key ? Node[t].child[LEFT] : Node[t].child[RIGHT];
}
return t;
}
//在root樹上遞迴查詢第kth個數,編號從1開始。這是size應用的關鍵,但其實很好理解。
int _kth(int root,int kth){
int son = Node[root].child[LEFT];
int sz = son ? Node[son].size + 1 : 1;
if ( kth < sz ) return _kth(son,kth);
if ( sz < kth ) return _kth(Node[root].child[RIGHT],kth-sz);
return root;
}
接下來為伸展數提供對外介面,根據這道題的要求,只需提供3個函式即可。
//初始化
inline void init(){
toUsed = 0;
memset(Node,0,sizeof(node_t));
}
//在root樹上插入一個節點
void insert(int&root,key_t key,value_t value=0){
int t = _newNode();
Node[t].key = key;
Node[t].value = value;
Node[t].size = 1;
if ( 0 == root ){
root = t;
return;
}
int p;
_advance(root,p,key);
int sn = key < Node[p].key ? LEFT : RIGHT;
_link(p,sn,t);
_splay(t,0,root);
}
//在root樹上查詢第kth數,編號從1開始
int select(int& root,int kth){
int t = _kth(root,kth);
_splay(t,0,root);
return t;
}
接下來只要完成主函式即可。完整的程式碼如下。
#include <cstdio>
#include <cstring>
using namespace std;
int const SIZE = 1111111;
int const LEFT = 0;
int const RIGHT = 1;
struct key_t{
int key;
int order;
key_t(int a=0,int b=0):key(a),order(b){}
};
bool operator < (key_t const&l,key_t const&r){
return l.key < r.key || (l.key == r.key && l.order < r.order);
}
bool operator == (key_t const&l,key_t const&r){
return l.key == r.key && l.order == r.order;
}
bool operator != (key_t const&l,key_t const&r){
return !(l == r);
}
typedef int value_t;
struct node_t{
int parent;
int child[2];
int sn;
key_t key;
value_t value;
int size;
}Node[SIZE];
int toUsed = 0;
inline void _pushUp(int t){
Node[t].size = 1;
int son = Node[t].child[LEFT];
if (son) Node[t].size += Node[son].size;
son = Node[t].child[RIGHT];
if (son) Node[t].size += Node[son].size;
}
inline void _link(int p,int sn,int t){
Node[p].child[sn] = t;
Node[t].parent = p;
Node[t].sn = sn;
}
inline void _rotate(int t){
int p = Node[t].parent;
int sn = Node[t].sn;
int osn = sn ^ 1;
_link(p,sn,Node[t].child[osn]);
_link(Node[p].parent,Node[p].sn,t);
_link(t,osn,p);
_pushUp(p);
}
void _splay(int t,int p,int&root){
while( Node[t].parent != p ){
int pp = Node[t].parent;
if ( Node[pp].parent != p ){
Node[pp].sn == Node[t].sn ?
_rotate(pp) : _rotate(t);
}
_rotate(t);
}
_pushUp(t);
if ( 0 == p ) root = t;
return;
}
inline void init(){
toUsed = 0;
memset(Node,0,sizeof(node_t));
}
inline int _newNode(){
++toUsed;
memset(Node+toUsed,0,sizeof(node_t));
return toUsed;
}
int _advance(int root,int&parent,key_t key){
if ( 0 == root ) return parent = 0;
int t = root;
parent = Node[t].parent;
while( t && key != Node[t].key ){
parent = t;
t = key < Node[t].key ? Node[t].child[LEFT] : Node[t].child[RIGHT];
}
return t;
}
void insert(int&root,key_t key,value_t value=0){
int t = _newNode();
Node[t].key = key;
Node[t].value = value;
Node[t].size = 1;
if ( 0 == root ){
root = t;
return;
}
int p;
_advance(root,p,key);
int sn = key < Node[p].key ? LEFT : RIGHT;
_link(p,sn,t);
_splay(t,0,root);
}
int _kth(int root,int kth){
int son = Node[root].child[LEFT];
int sz = son ? Node[son].size + 1 : 1;
if ( kth < sz ) return _kth(son,kth);
if ( sz < kth ) return _kth(Node[root].child[RIGHT],kth-sz);
return root;
}
int select(int& root,int kth){
int t = _kth(root,kth);
_splay(t,0,root);
return t;
}
int main(){
init();
int root = 0;
int n;
scanf("%d",&n);
for(int i=0;i<n;++i){
int x;
scanf("%d",&x);
insert(root,key_t(x,i));
}
char tmp[10];
scanf("%s%d",tmp,&n);
for(int i=0;i<n;++i){
int k;
scanf("%d",&k);
int t = select(root,k);
printf("%d\n",Node[t].key.key);
}
return 0;
}
相關推薦
伸展樹的節點的size域的應用
伸展樹不是平衡二叉樹,但是它的操作均攤是O(logN)的,只需要將增、刪、查、改的節點都加以伸展即可。因此可以用來解決相關問題。 在伸展樹的節點上附加一個size域,用來儲存以該節點為根的子樹的節點總數。這個size域可以用來解決很多問題。例如可以解決區間問題
伸展樹的基本操作與應用
【總結】 由上面的分析介紹,我們可以發現伸展樹有以下幾個優點: (1)時間複雜度低,伸展樹的各種基本操作的平攤複雜度都是 O(log n)的。在樹狀資料結構中,無疑是非常優秀的。 (2)空間要求不高。與紅黑樹需
weblogic建域、建服務節點、部署應用
一、新建weblogic域 開始選單 Oracle WebLogic -> WebLogic Server 10gR3 -> Tools -> Configuration Wizard 預設下一步 預設下一步 輸入weblogic使用
二叉樹節點個數,葉子個數,第K層個數,最低公共節點
fun ret tco left right amp 最小公共 last turn 1. 節點個數 function getNodeNum(root){ if(root == null){ return 0; } //+1為root
伸展樹基本概念基本題目
name names algorithm rto 維護 每次 等於 http 移動 http://blog.csdn.net/discreeter/article/details/51524210 //基本概念詳見這裏 例題HDU4453 代碼來源http://bl
求最值背景下動態刪除線段樹節點
int scan 輸入 包含 mes else 兩個 let del 動態最值(minmax.Cpp/c/Java)(空間限制128M)有一個包含n個元素的數組,要求實現以下操作:DELETE k:刪除位置k上的數。右邊的數往左移一個位置。QUERY i j:查詢位置i~j
Ext 4.2樹節點搜索功能
pan 比較 嘗試 true query spa otn tro 使用 註,如果拿到的節點Type是treeNode這樣的類型,你的Ext版本和筆者的Ext版本並不相同,據網上說treeNode是比較老的Ext版本。筆者使用的類型是Ext.data.NodeInterfac
填充樹節點
per datatable eno 節點 tables dataset tar style node 1 Endv.DataHelper myHelper = new Endv.DataHelper(); 2 DataSet
數據結構-伸展樹
ati 三種 操作 而不是 文章 沒有 通過 blog com 本文為轉載文章 作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝! 我們討論過,樹的搜索效率與樹的深度有關。二叉搜索樹的深度可能為n,這種
BZOJ 3391 [Usaco2004 Dec]Tree Cutting網絡破壞:dfs【無根樹 節點分枝子樹大小】
push pac namespace pre efi else line geo void 題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=3391 題意: 給你一棵無根樹,求分支size均不大於一半點數的點。
easyui tree:根據屬性格式化樹節點名稱
targe span col logs code ssi 名稱 pos eth $(‘#resourceTree‘).tree({ method : ‘post‘, animate : true, onContextMenu : function(e
二叉樹節點的插入
stdio.h insert 隊列 enum ear sem == count include 全部代碼 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <assert.
排序二叉樹節點的刪除
for 臨時 sse 刪除節點 bsp != == tro 二叉樹 全部代碼 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <assert.h> 4 5
Angular 2 樹節點的上下移動問題
flow multiple tex wid post nodes body get left 最近在做一個樹節點的上下移動然後實現排序的問題。直接看圖: 實現已選查詢條件的上下移動。結合了primeng 的picklist 組件。 下面是html代碼 <p
Splay伸展樹入門(單點操作,區間維護)
png family alt 二分 可能 區間 搜索 一個 soft ps:終於學會了伸展樹的區間操作,做一個完整的總結,總結一下自己的伸展樹的單點操作和區間維護,順便給未來的總結復習用。 splay是一種平衡樹,【平均】操作復雜度O(nlogn)。首先平衡樹先是一
Access Treeview樹節點代碼一
維護 des 基礎資料 AD int AS 觀測 date 節點 Private Sub TreeView0_Updated(Code As Integer)Dim ndeindex As NodeSet ndeindex = TreeView0.Nodes.Add(, ,
Access Treeview樹節點代碼二
The 出錯 AC self. pri lock cat windows IT Private Sub Form_Load() ‘引用C:\windows\system32\MSCOMCTL.OCX,否則提示出錯。 Dim Rec As New ADODB.Re
伸展樹Splay
png 保持 什麽 排名 求解 最終 轉化 特定 隨機 平衡樹的旋轉 一般的平衡樹通過旋轉來維持樹的動態平衡。 回顧二叉搜索樹的性質,無論什麽時候都需要保證左子節點小於根節點,右子節點大於根節點。我們需要在維護平衡的過程中保持該性質不變。 旋轉分為左旋與右旋。 總結起來,樹
DOM樹節點的相關知識
image 知識 pan img 圖片 bsp src 分享圖片 ima dom的操作其實是對節點的:增,刪,改,查 這四項基本的操作; DOM樹節點的相關知識
第四章-語法分析之認識樹節點
似的 序列 語義分析 語法 聲明 規則 mil src alt 上一章我們得到了Token序列,而語法分析就是根據Token序列構造抽象語法樹的過程,抽象語法樹是一種用來描述程序代碼語法 結構的樹形表示方式,這種結構化的表示方式將為後面語義分析、代碼生成階段提供極大