【高階資料結構】K-D Tree
阿新 • • 發佈:2018-12-17
【高階資料結構】K-D Tree
$K-D Tree$ 是用來解決K維空間中數點問題強有力的資料結構,可以在 $(NlogN)$ ——$(N\sqrt{N})$ 的時間複雜度內完成查詢和修改。
一、K-D Tree的做法
$K-D Tree$ 當K等於 $1$ 時,就是一顆替罪羊樹樹(平衡樹的一種)。我們把 $K-D Tree$ 擴充套件到 $K$ 維空間。
$K-D Tree$ 是一顆權值樹,我們對於每個樹的結點用一個結構體來存
1 struct K_D_Tree{ 2 int l,r,sum,val,size,Min[K],Max[K],d[K]; 3 }tr[MAXN];
$l$ $r$ 分別是該結點的左孩子和右孩子
$sum$ 為以該點為根的子樹區間中點權之和
$val$ 為該點所存的點的點權
$size$ 為該樹為根的數中的點數
$Min[i]$ 表示在第 $i$ 維上區間的下界
$Max[i]$ 表示在第 $i$ 維上區間的上界
$d[i]$ 表示該位置表示的點的第 $i$ 維的座標
插入操作
我們對於深度為 $i$ 的位置,我們按照 第 $i%k$ 維的座標來排序、
然後就是普通平衡樹的插入即可
我們設定一個平衡因子,一般設定為 $0.6~0.9$ 之間
1 const double alpha=0.75;
當在插入是發現要插入的子樹的 $size$ 比整棵樹的 $size$ 的 $alpha$ 倍要大,即發現樹不平衡,那麼我們直接暴力重建該子樹。
查詢操作
直接平衡樹的查詢操作即可
刪除操作
我們直接對要刪除的點打刪除標記,在插入時再刪除,在查詢或刪除時時如果發現某顆子樹的的刪除標記個數大於一個定值,我們對於該子樹暴力重構即可。
二、K-D Tree的幾何性質
可以看上面一個以 $k$ 為 $2$ 為例的圖
$K-D Tree$ 相當於每個結點對於其管轄的區間平行於座標軸分割成一半,最後整個圖被分成若干個區間,但這些區間的大小是不一致的,所以 $K-D Tree$ 是很容易被卡的。
# 模板題:
三維偏序:P4148 簡單題
1 #include<bits/stdc++.h> 2 #defineMAXN 100010 3 using namespace std; 4 inline int read () 5 { 6 int s=0,w=1; 7 char ch=getchar (); 8 while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();} 9 while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar (); 10 return s*w; 11 } 12 const double alpha=0.75; 13 const int K=2; 14 struct K_D_Tree{ 15 int l,r,sum,val,size,Min[K],Max[K],d[K]; 16 }tr[MAXN]; 17 int n,ans,root,len; 18 int p[K],q[K][2],A; 19 int D,num,h[MAXN]; 20 bool cmp (const int &a,const int &b) 21 { 22 return tr[a].d[D]<tr[b].d[D]; 23 } 24 inline void update (int x) 25 { 26 int l=tr[x].l,r=tr[x].r; 27 tr[x].size=tr[l].size+tr[r].size+1; 28 tr[x].sum=tr[l].sum+tr[r].sum+tr[x].val; 29 for (int i=0;i<K;i++) 30 { 31 if (l) tr[x].Max[i]=max (tr[l].Max[i],tr[x].Max[i]),tr[x].Min[i]=min (tr[l].Min[i],tr[x].Min[i]); 32 if (r) tr[x].Max[i]=max (tr[r].Max[i],tr[x].Max[i]),tr[x].Min[i]=min (tr[r].Min[i],tr[x].Min[i]); 33 } 34 } 35 inline void build (int &x,int l,int r,int k) 36 { 37 if (l>r) return; 38 int mid=(l+r)>>1;D=k; 39 nth_element (h+l,h+mid+1,h+r+1,cmp); 40 x=h[mid]; 41 tr[x].sum=tr[x].val; 42 for (int i=0;i<K;i++) tr[x].Max[i]=tr[x].Min[i]=tr[x].d[i]; 43 build (tr[x].l,l,mid-1,(k+1)%K); 44 build (tr[x].r,mid+1,r,(k+1)%K); 45 update (x); 46 } 47 inline void erase (int &x) 48 { 49 if (!x) return; 50 h[++num]=x; 51 erase (tr[x].l),erase (tr[x].r); 52 x=0; 53 } 54 inline void rebuild (int &x,int k) 55 { 56 h[num=1]=++len; 57 tr[len].size=1; 58 for (int i=0;i<K;i++) tr[len].d[i]=p[i]; 59 tr[len].val=tr[len].sum=A; 60 erase (x),build (x,1,num,k); 61 } 62 inline void insert (int &x,int k) 63 { 64 if (!x) 65 { 66 tr[x=++len].size=1,tr[x].val=tr[x].sum=A; 67 for (int i=0;i<K;i++) tr[x].Max[i]=tr[x].Min[i]=tr[x].d[i]=p[i]; 68 return; 69 } 70 if (p[k]<tr[x].d[k]) 71 { 72 if (tr[tr[x].l].size>tr[x].size*alpha) rebuild (x,k); 73 else insert (tr[x].l,(k+1)%K); 74 } 75 else 76 { 77 if (tr[tr[x].r].size>tr[x].size*alpha) rebuild (x,k); 78 else insert (tr[x].r,(k+1)%K); 79 } 80 update (x); 81 } 82 inline bool check_range (int x) 83 { 84 if (!x) return 0; 85 for (int i=0;i<K;i++) 86 if (q[i][0]>tr[x].Min[i]||q[i][1]<tr[x].Max[i]) return 0; 87 return 1; 88 } 89 inline bool check_point (int x) 90 { 91 if (!x) return 0; 92 for (int i=0;i<K;i++) 93 if (tr[x].d[i]<q[i][0]||tr[x].d[i]>q[i][1]) return 0; 94 return 1; 95 } 96 inline bool check (int x) 97 { 98 if (!x) return 0; 99 for (int i=0;i<K;i++) 100 if (q[i][1]<tr[x].Min[i]||q[i][0]>tr[x].Max[i]) return 0; 101 return 1; 102 } 103 inline void query (int x) 104 { 105 if (check_range (x)) 106 { 107 ans+=tr[x].sum; 108 return; 109 } 110 if (check_point (x)) ans+=tr[x].val; 111 if (check (tr[x].l)) query (tr[x].l); 112 if (check (tr[x].r)) query (tr[x].r); 113 } 114 int main() 115 { 116 n=read (); 117 while (1) 118 { 119 int opt=read (); 120 if (opt==1) 121 { 122 for (int i=0;i<K;i++) p[i]=read ()^ans; 123 A=read ()^ans; 124 insert (root,0); 125 } 126 if (opt==2) 127 { 128 for (int i=0;i<=1;i++) 129 for (int j=0;j<K;j++) q[j][i]=read ()^ans; 130 ans=0;query (root); 131 printf ("%d\n",ans); 132 } 133 if (opt==3) break; 134 } 135 return 0; 136 }