• Frequent Values-线段树求解出现最多的数


    Frequent Values(poj 3368)

    注意:以下答案为离线作答结果,并非能通过poj,若要通过poj,需要修改函数接口,因为以下程序接受半封闭区间(s,e],同时还需要修改输入数据的顺序

    求出现最多的数:
    给定n个数,已按从大到小顺序排列好,一共有q个询问,每次询问一个区间,问这个区间中出现次数最多的数是什么。
    题目数据范围:
    数的个数,1 ≤ n ≤ 100000
    询问次数,1 ≤ q ≤ 100000
    每个数的大小,-100000 ≤ ai ≤ 100000

    思路:

    使用线段树解决。因个数比较多,最多有10w个数据,又因为输入是有序的,从大到小输入。因此可以将相同数据保存到同一个结构体中,并记录一共有多少个相同的元素。这样做既可以免去合并子树求解出现最多数的麻烦,也节约了空间,提高了效率。

    题目中使用数组来模拟线段树。

      1 /**********************************************************
      2  作者:SunboyL
      3  题目3:Frequent Values(poj 3368)
      4 
      5  求出现最多的数:
      6  给定n个数,已按从大到小顺序排列好,一共有q个询问,每次询问一个区间,问这个区间中出现次数最多的数是什么。
      7  题目数据范围:
      8  数的个数,1 ≤ n ≤ 100000
      9  询问次数,1 ≤ q ≤ 100000
     10  每个数的大小,-100000 ≤ ai ≤ 100000
     11 
     12 
     13  思路:很容易想到建立线段树,并在线段树的每个结点中保存区间中出现次数最多的数。
     14  需要解决的问题,两个子结点的信息如何合并到父结点。
     15  很显然,子结点中出现次数最多的数不一定就是父结点中出现次数最多的数
     16 
     17  时间:2013.10.28
     18 **********************************************************/
     19 
     20 #include <vector>
     21 #include <algorithm>
     22 #define MAXN (100000)
     23 
     24 typedef struct _dataSet
     25 {// 一个值相同的数据集合
     26     int value,count,lastNumSeq; //value表示一个值;count表示有多少个value值,seq表示在线性表中该集合最后一个值的序号
     27     int setSeq;// 集合序号,表示第几个集合
     28 }DataSet;
     29 
     30 typedef struct _segTree
     31 {
     32     int s,e; // 表示区段[s,e)
     33     DataSet mostValue; // mostValue表示区段[s,e)中出现最多的值.
     34 }SegTree,*pSegTree;
     35 
     36 void buildSegTree(pSegTree tree,int start,int end,int node) 
     37 { // tree为树根节点,start到end区间为[start,end),node表示节点编号
     38     tree[node].s = start;
     39     tree[node].e = end;
     40     tree[node].mostValue.value = 0x80000000;//0x80000000为最小的32位整数
     41     if(start + 1 == end)
     42         return;
     43     int mid = (start + end) >> 1;
     44     buildSegTree(tree,start,mid,node*2); // 建立左子树
     45     buildSegTree(tree,mid,end,node*2+1); // 建立右子树
     46 }
     47 
     48 void insertDataSet(pSegTree segTree,const DataSet& dataSet,int node = 1)
     49 {// 将dataSet插入到segTree中,node默认指向根节点,调用时忽略此参数
     50     if(dataSet.count > segTree[node].mostValue.count)
     51         segTree[node].mostValue = dataSet;// 每个节点都记录区间中出现次数最多的数
     52     if(segTree[node].s + 1 == segTree[node].e)
     53         return;// 叶子节点
     54     int mid = (segTree[node].s + segTree[node].e) / 2;
     55     if(dataSet.setSeq < mid) // 线段树以集合的个数来划分线段,因此使用集合所在的序号判断插入到左子树或是右子树
     56         insertDataSet(segTree,dataSet,node*2);
     57     else
     58         insertDataSet(segTree,dataSet,node*2+1);
     59 }
     60 
     61 DataSet query(pSegTree tree,const std::vector<DataSet>& dataSet,int l,int r,int node = 1)
     62 {// 查询在线性列表dataSet[]中(l,r]区间出现次数最多的数,并返回该数的相同元素集合
     63     DataSet result;
     64     int mid = (tree[node].s + tree[node].e) / 2;
     65     int ds,de; // 将以集合为单位的区间转换为以单个数据值为单位的区间[ds,de)
     66     if(tree[node].s == 1)
     67         ds = 1;
     68     else
     69         ds = dataSet[tree[node].s - 2].lastNumSeq + 1;
     70     de = dataSet[tree[node].e - 2].lastNumSeq + 1; // 因为std::vector<DataSet>& dataSet数组从0开始,所以相对起始位置,应该是减2
     71 
     72     if(l == ds && r == de)
     73     {
     74         return tree[node].mostValue;
     75     }
     76     if(tree[node].s + 1 == tree[node].e)
     77     {
     78         result = tree[node].mostValue;
     79         result.count = r - l;// 区间并不完全覆盖相同元素的集合,则相同的个数为其部分覆盖的个数。
     80         return result;
     81     }
     82     DataSet a,b;
     83     a.count = b.count = 0;
     84     if(l <= dataSet[mid - 2].lastNumSeq)
     85         a = query(tree,dataSet,l,std::min(r,dataSet[mid - 2].lastNumSeq + 1),node*2);
     86     if(r > dataSet[mid - 2].lastNumSeq + 1)
     87         b = query(tree,dataSet,std::max(l,dataSet[mid - 2].lastNumSeq + 1),r,node*2+1);
     88     result = a.count>b.count?a:b;
     89     return result;
     90 }
     91 
     92 void frequentValues()
     93 {// frequentValues函数是本程序接口。负责输入。
     94     int n,i; // n:有多少个要输入的元素
     95     std::vector<DataSet> dataSet; // 本题目主要研究线段树而非线性表,这里就使用一下容器偷懒
     96     DataSet temp;
     97 
     98     std::cin >> n; // 输入一共有多少个数据
     99     if(n < 1 || n > MAXN) // 元素个数为1到100000
    100         return;
    101 
    102     std::cin >> temp.value;// 输入第一个元素的值
    103     temp.count = 1;
    104     temp.lastNumSeq = 1;
    105     temp.setSeq = 1;// 第一个集合
    106     dataSet.push_back(temp);
    107     int q = 0; // 指向dataSet数组中当前值(集合)的指针
    108 
    109     for( i = 2;i <= n;++i )
    110     {
    111         std::cin >> temp.value;
    112         if(temp.value == dataSet[q].value)
    113         {
    114             dataSet[q].count ++;
    115             dataSet[q].lastNumSeq ++;
    116         }
    117         else
    118         {
    119             temp.count = 1; // 值不相同,表示另外一个集合,充值value值有一个,序号为前一个集合的最后一个值的序号加一
    120             temp.lastNumSeq = dataSet[q].lastNumSeq + 1;
    121             temp.setSeq = dataSet[q++].setSeq + 1;
    122             dataSet.push_back(temp);
    123         }
    124     }
    125     int size = dataSet.size(); // 记录值不同的集合有多少个,并利用这个,建立1-size的线段树
    126     pSegTree segTree = new SegTree[4*size];
    127 
    128     buildSegTree(segTree,1,size+1,1); // 以segTree为根节点,集合个数size(1-size+1)为线段区间建立线段树
    129     for(i = 0;i < size;++i)
    130         insertDataSet(segTree,dataSet[i]);// 将dataSet中的数据集合插入到线段树中
    131 
    132     int ask; // 多少次询问
    133     std::cin >> ask;
    134     while(ask --)
    135     {
    136         DataSet result;
    137         int l,r;
    138         std::cin >> l >> r;// 输入要询问的区间[l,r)
    139         result = query(segTree,dataSet,l,r);
    140         int t = result.value;
    141         std::cout << t << std::endl;
    142     }
    143     delete[] segTree;
    144     
    145 }
    146 
    147 #include <iostream>
    148 #include <list>
    149 using namespace std;
    150 int main()
    151 {
    152     //MergeSortTest(); // 测试题目一,归并排序
    153     //movableWindowsTest();// 测试题目二,移动窗口,输出窗口中的最小值
    154     //MonkeyKingTest();// 测试题目三,猴王monkey king
    155     frequentValues(); // 测试题目四,Frequently Values
    156 
    157     return 0;
    158 }
    View Code

    20

    50 48 48 48 48 47 39 39 39 39 20 20 17 16 16 16 16 16 12 11
    5
    1 21
    3 12
    5 9
    7 17
    7 18

     输出结果如下:

  • 相关阅读:
    文本标记
    第一个HTML文档
    HTML入门
    bootstrap fileinput 文件上传
    DPDK rte_hash 简述
    glib学习笔记-基本知识
    linux常用网络命令
    libevent学习过程
    C语言 singleton模式
    oracle命令行导出、导入dmp文件
  • 原文地址:https://www.cnblogs.com/SunboyL/p/Frequent_Values.html
Copyright © 2020-2023  润新知