线段树入门---给定多个线段求点的出现个数

线段树是一颗二叉树,他的每个节点都是一个区间,此题为线段树的入门题目,只是学习笔记。例题:给定N个线段,给定M个点,求点在多少个线段中出现过,此时如果用传统的方法来求,时间复杂度太高,但是,线段树的时间复杂度还可以接受。

步骤为:

1. 首先找一个区间,能覆盖给定的所有区间, 然后把此区间建立线段树,建立线段树的方式是二分法建立,即它的左孩子是他的左半个区间,右孩子是它的右边那个区间。一个图足以说明清楚

2. 将所有的区间映射到此树上, 从根节点开始遍历, 每遍历一个节点考虑四种情况:

1) 当前节点的区间正好和正在遍历的区间相等,这时, 将它的cnt++, 然后return

2) 当区间在当前节点的左半个区间时,继续遍历当前节点的左孩子

3)  当区间在当前节点的右半个区间时,继续遍历当前节点的右孩子

4) 除了上面的之外, 区间的左端点在区间中点的左边,右端点在区间中点的右边,这时分别遍历左右孩子

3. 此时的树已经是映射好的,所以只需要查找点就行了,也是用递归的形式,只要加它的cnt就行了

下面是代码实现:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <algorithm>
  4
  5 using namespace std;
  6
  7 const int MAX = 999999999;
  8 const int MIN = 0;
  9 //left,right表示区间的左右端点,cnt表示这样的线段有多少条
 10 typedef struct Node{
 11     int left, right;
 12     int cnt;
 13     struct Node *lchild, *rchild;
 14 }Node, *PNode;
 15 //创建线段树函数
 16 void CreateTree(PNode *root)//*root已经被初始化
 17 {
 18     int left = (*root)->left;
 19     int right = (*root)->right;
 20     if(left < right)
 21     {
 22         int mid = (left + right) / 2;
 23         //它的左孩子,并初始化它的左孩子
 24         PNode lnode = (PNode)malloc(sizeof(Node));
 25         lnode->cnt = 0;
 26         lnode->left = left;
 27         lnode->right = mid;
 28         lnode->lchild = lnode->rchild = NULL;
 29         //它的右孩子,并初始化它的右孩子
 30         PNode rnode = (PNode)malloc(sizeof(Node));
 31         rnode->cnt = 0;
 32         rnode->left = mid + 1;
 33         rnode->right = right;
 34         rnode->rchild = rnode->lchild = NULL;
 35         //扯上关系
 36         (*root)->lchild = lnode;
 37         (*root)->rchild = rnode;
 38         //递归建树
 39         CreateTree(&(*root)->lchild);
 40         CreateTree(&(*root)->rchild);
 41     }
 42 }
 43 //将区间映射到线段树上
 44 void Map_Interval(PNode root, int left, int right)
 45 {
 46     //如果恰好当前区间等于线段树当前节点的区间,将该条线段+1,就返回
 47     if(root->left == left && root->right == right)
 48     {
 49         root->cnt++;
 50         return;
 51     }
 52     int mid = (root->left + root->right) / 2;
 53     //如果在当前节点的区间左半个区间内, 就继续找他的左半个区间,也就是左儿子
 54     if(right <= mid)
 55     {
 56         Map_Interval(root->lchild, left, right);
 57     }
 58     //如果在当前节点的区间右半个区间内, 就继续找他的右半个区间,也就是右儿子
 59     else if(left > mid)
 60     {
 61         Map_Interval(root->rchild, left, right);
 62     }
 63     //如果左端点在当前节点的左半部份,右端点在当前区间的右半部份,分别找
 64     else
 65     {
 66         Map_Interval(root->lchild, left, mid);
 67         Map_Interval(root->rchild, mid + 1, right);
 68     }
 69 }
 70 //找到这个点在线段上一共出现的次数, sum用来得到具体的数目,  point为点的号
 71 void search_point(PNode root, int point, int *sum)
 72 {
 73     if(point == root->left && point == root->right)
 74     {
 75         *sum += root->cnt;
 76         return;
 77     }
 78     else
 79     {
 80         int mid = (root->left + root->right) / 2;
 81         if(point > mid)
 82         {
 83             *sum += root->cnt;
 84             search_point(root->rchild, point, sum);
 85         }
 86         else
 87         {
 88             *sum += root->cnt;
 89             search_point(root->lchild, point, sum);
 90         }
 91     }
 92 }
 93 //前序遍历,辅助看建树的情况
 94 void preOrder(PNode root)
 95 {
 96     if(root != NULL)
 97     {
 98         printf("[%d, %d]   count --> %d\n", root->left, root->right, root->cnt);
 99         preOrder(root->lchild);
100         preOrder(root->rchild);
101     }
102 }
103
104 int main()
105 {
106     freopen("1.in", "r", stdin);
107     //初始化root
108     PNode root = (PNode)malloc(sizeof(Node));
109     root->cnt = 0;
110     root->lchild = root->rchild = NULL;
111
112     int left[1000], right[1000], point[1000];
113     int n; //区间个数
114     scanf("%d", &n);
115     int min_num = MAX;//保存最小的区间端点
116     int max_num = MIN;//保存最大的区间端点
117     for(int i = 0; i < n; i++)
118     {
119         scanf("%d %d", &left[i], &right[i]);
120         min_num = min(left[i], min_num);
121         max_num = max(right[i], max_num);
122     }
123     int m;//带查询点的个数
124     scanf("%d", &m);
125     for(int i = 0; i < m; i++)
126         scanf("%d", &point[i]);
127     root->left = min_num;
128     root->right = max_num;
129     CreateTree(&root);//建树
130     preOrder(root);//打印出来,看建树的情况
131     for(int i = 0; i < n; i++)
132     {
133         Map_Interval(root, left[i], right[i]);
134     }
135     //将区间映射到树上之后,再看树的情况
136     printf("\n=================================================\n");
137     preOrder(root);
138     int tmp = 0;
139     for(int i = 0; i < m; i++)
140     {
141         search_point(root, point[i], &tmp);
142         printf("%d 在给定线段中出现 %d 次\n", point[i], tmp);
143         tmp = 0;
144     }
145     return 0;
146 }

其中利用文件重定向打开文件,文件中的内容为

3
2 5
4 6
0 7
4
2 4 6 7

结果如图:

时间: 2024-10-08 21:36:33

线段树入门---给定多个线段求点的出现个数的相关文章

线段树入门(Billboard)

Billboard Time Limit:8000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description At the entrance to the university, there is a huge rectangular billboard of size h*w (h is its height and w is its width). The board is the place where

线段树入门理解

在复习算法至分治法时,书本上主要介绍了合并排序和快速排序,较为简单.特拓展简单学习一个应用了分治法的算法结构--线段树. acm刷题时遇到许多连续区间的动态查询问题,例如求取某一区间上元素之和.求取某一区间上元素的最大值,此时如果使用一般的方法求解会使得时间超出要求.此时需要使用到线段树,其主要用于高效解决连续区间的动态查询问题. 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN),从而大大减少耗时

线段树入门小结

QUE:线段树? 称谓: 从刘汝佳的书中得知,"这种数据结构在学术界没有统一的术语,但线段树是最常见的叫法.其他叫法包括区间树(interval tree).范围树(range tree)等,但这些属于在特定的场合(如计算几何)中有着特殊的意义".怎么叫看读者的心情,以下统一用线段树称呼. 先来作一些了解: 线段树是一棵二叉树,它的左右儿子也都是一棵线段树.(定义) 线段树也叫区间树,为什么叫它区间树呢?因为线段树是一种基于区间的数据结构. 线段树的每个节点代表一个区间 [L,R],其

线段树入门(I Hate It)

I Hate It Time Limit:3000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input 本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,

《数据结构》线段树入门(二)

今天继续介绍——线段树之延迟标记 接上期<数据结构>线段树入门(一):http://www.cnblogs.com/shadowland/p/5870339.html 在上期介绍了线段树的最基本内容(线段树单点修改,区间查询),这次将介绍:区间修改,区间查询. Question: 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述: 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一数是1,后接3个正

hdu1166敌兵布阵&amp;&amp;hdu1754I Hate It(线段树入门)

单点更新是最最基础的线段树,只更新叶子节点,然后把信息用pushup这个函数更新上来. http://acm.hdu.edu.cn/showproblem.php?pid=1166 update单点更新,query区域求和. #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #define N 200001 using namespace std; s

hdu 1754 I Hate It(线段树入门)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 41510    Accepted Submission(s): 16458 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师

线段树入门(更新单个节点)

很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目. 学生ID编号分别从1编到N. 第二行包含N个整数,代表这N个学生的初始成绩,

线段树入门之单点更新

作者:zifeiy 标签:线段树 单点更新 :最最基础的线段树,只更新叶子节点,然后把信息用 push_up(int rt) 这个函数更新上来 HDU1166 敌兵布阵 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 线段树功能: update:单点增减: query:区间求和 #include <bits/stdc++.h> using namespace std; #define lson l, m, rt<<1 #def