线段树+离散化 IP地址段检查 SEGMENT TREE

本文介绍了一种通过构建区间树来高效查找IP地址所属段的方法。首先将IP段离散化为整数区间,然后建立区间树,每个叶节点对应一个具体的IP区间。通过递归插入和搜索操作,实现快速定位特定IP地址所在的段。

Problem:

Give a series of IP segments, for example, [0.0.0.1-0.0.0.3], [123.234.232.21-123.245.21.1]...

Now there is a new IP, find which IP segment it's in ?

Solution:

First, we could map the ends of IP segments into some intervals, since the IP address could be represented by a unsigned int. This is called discretization

Second, setup the segment tree. Every leaf node is [x, x+1], it means whether an IP segment includes the part. We could use a vector<int> to record the interval index for searching. So the core code is:

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <limits.h>
#include <assert.h>
#include <stdio.h>
using namespace std;

class Interval {
 public:
  int l, r;
  Interval(int ll = 0, int rr = 0) : l(ll), r(rr) {}
};
class Node {
 public:
  int l, r;
  Node *lc, *rc;
  vector<int> IPs;
  
  Node(int ll = 0, int rr = 0) : l(ll), r(rr), lc(NULL), rc(NULL) {}
};

Node* build(int l, int r) {
  Node *root = new Node();
  root->l = l, root->r = r;
  if (l + 1 >= r)
    return root;
  int mid = (l + r) / 2;
  root->lc = build(l, mid);
  root->rc = build(mid, r);
  return root;
}
void insert(Node* root, int l, int r, int idx) {
  if (l <= root->l && r >= root->r) {
    root->IPs.push_back(idx);
    return;
  }
  int mid = (root->l + root->r) / 2;
  if (l < mid) // Take care here!
    insert(root->lc, l, mid, idx);
  if (r > mid) // Take care here!
    insert(root->rc, mid, r, idx);
}
void search(Node* root, int l, int r, vector<int>& res) {
  if (r <= root->r && l >= root->l) {
    for (int i = 0; i < root->IPs.size(); ++i)
      res.push_back(root->IPs[i]);
  }
  int mid = (root->l + root->r) / 2;
  if (r <= mid)
    search(root->lc, l, mid, res);
  if (l >= mid)
    search(root->rc, mid, r, res);
}
int main()
{
  Node* rt = build(1, 7);
  Interval inters[] = {Interval(1,3),Interval(2,4),Interval(1,5), Interval(5,7)};
  int size = sizeof(inters) / sizeof(inters[0]);
  for (int i = 0; i < size; ++i) 
    insert(rt, inters[i].l, inters[i].r, i);
  vector<int> res;
  search(rt, 2, 3, res);
  return 0;
}



### 线段树离散化实现方法与原理 #### 定义与背景 线段树是一种高效的数据结构,适用于处理区间查询和更新操作。然而,在实际应用中,数据范围可能非常大,这使得直接构建线段树变得不切实际。此时,通过离散化可以有效减少数值范围,从而优化存储空间和提高运算效率。 #### 离散化的意义 离散化是指将原始数据映射到较小范围内的一组整数的过程。这样做不仅能够节省内存消耗,还能加快算法执行速度。对于线段树而言,这意味着可以在更紧凑的空间上建立索引,进而提升性能[^1]。 #### 离散化的方法 为了实现有效的离散化,通常采用如下步骤: - **收集所有可能出现的关键点**:遍历输入序列中的每一个元素,并将其加入集合S。 - **去除重复项并对剩余元素排序**:对上述得到的集合去重后按升序排列形成有序列表L。 - **创建映射关系**:为方便后续查找,可使用哈希表或其他快速访问方式来记录每个唯一值对应的新位置编号。 ```cpp std::map<int, int> discrete_map; for (int i = 0; i < L.size(); ++i) { discrete_map[L[i]] = i + 1; // 将原坐标转换成新的连续编号 } ``` #### 结合线段树的操作 完成离散化之后,就可以基于这些经过变换后的数值来初始化并维护一棵线段树了。每当遇到一个新的询问或修改请求时,先依据之前定义好的映射规则找到对应的离散化后的下标再进行相应处理即可[^3]。 #### 示例代码展示 下面给出一简化版C++程序片用于说明如何在线段树框架内实施离散化技术: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5+7; struct SegmentTree { vector<long long> tree; void build(int node, int start, int end){ if(start == end){ tree[node] = 0; }else{ int mid = (start + end)/2; build(2*node, start, mid); build(2*node+1, mid+1, end); tree[node] = tree[2*node]+tree[2*node+1]; } } void update(int node, int start, int end, int idx, int val){ if(start == end){ tree[node] += val; }else{ int mid=(start+end)/2; if(idx<=mid) update(node*2,start,mid,idx,val); else update(node*2+1,mid+1,end,idx,val); tree[node]=tree[node*2]+tree[node*2+1]; } } long long query(int node, int start, int end, int l, int r){ if(r<start || end<l)return 0; if(l<=start && end<=r)return tree[node]; int mid=(start+end)/2; return query(node*2,start,mid,l,r)+query(node*2+1,mid+1,end,l,r); } }; // ...其他部分省略... void solve(){ map<int,int> mp; set<int> s; for(auto &x : a)s.insert(x); // 收集关键点 int cnt=0; for(auto it=s.begin();it!=s.end();++it){ mp[*it]=++cnt; // 创建映射 } segment_tree.build(1,1,cnt); // 构建线段树 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值