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

 输出结果如下:

 

posted @ 2013-10-29 19:08  SunboyL  阅读(644)  评论(0编辑  收藏  举报