1. 程式人生 > >伸展樹的節點的size域的應用

伸展樹的節點的size域的應用

伸展樹不是平衡二叉樹,但是它的操作均攤是O(logN)的,只需要將增、刪、查、改的節點都加以伸展即可。因此可以用來解決相關問題。

在伸展樹的節點上附加一個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序列構造抽象語法樹的過程,抽象語法樹是一種用來描述程序代碼語法 結構的樹形表示方式,這種結構化的表示方式將為後面語義分析、代碼生成階段提供極大