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


    线段树是一颗二叉树,他的每个节点都是一个区间,此题为线段树的入门题目,只是学习笔记。例题:给定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
    ", 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("
    =================================================
    ");
    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 次
    ", point[i], tmp);
    143         tmp = 0;
    144     }
    145     return 0;
    146 }

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

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

    结果如图:

  • 相关阅读:
    [LeetCode] Majority Element II
    [Nginx] 事件模型
    [Nginx] 进程模型
    [C++] 函数中的字符串指针与数组
    [LeetCode] Shortest Distance to a Character
    [LeetCode] Number of Lines To Write String
    Ubuntu 16.04中安装谷歌Chrome浏览器
    Python、机器学习、计算机视觉、深度学习入门
    Sublime安装与配置
    [20160807][系统设计的三次迭代]
  • 原文地址:https://www.cnblogs.com/Howe-Young/p/4055023.html
Copyright © 2020-2023  润新知