- 数据结构编程实验(第3版)
- 吴永辉 王建德编著
- 987字
- 2021-08-13 17:24:05
4.3 在数组中快速查找指定元素
在1.4节中给出了一种快速查找指定元素的二分法,但使用二分法需预先对序列中的数据进行排序,并按下述方法递归查找:由序列的中间元素划分出左右子序列。每次待查找元素与中间元素比较大小,确定该元素是否为中间元素或者可能属于哪个子序列内的元素。这个过程一直进行到找到待查元素位置或子区间不存在为止,二分查找可使得计算效率提高到O(log(n))。
但是,若序列中的数据重复出现,并且要避免预先排序的额外开销,要求按照出现次数给出被查数据的位置,怎么办?
使用vector类的动态数组不需要进行排序和递归,较为简便。下面,我们就通过一个实例来详述这种方法。
【4.3.1 Easy Problem from Rujia Liu?】
给出一个数组,请找到一个整数v的第k次出现(从左至右)。为了使问题更加困难、更加有趣,请回答m个这样的查询。
输入
输入给出若干测试用例。每个测试用例的第一行给出两个整数n、m(1≤n,m≤100 000),表示数组中元素的个数和查询的数目。下一行给出不大于1 000 000的n个正整数。接下来的m行每行给出两个整数k和v(1≤k≤n,1≤v≤1 000 000)。输入以EOF结束。
输出
对于每个查询,输出出现的位置(数组起始位置为1)。如果没有这样的元素,则输出'0'。
试题来源:Rujia Liu’s Present 3:A data structure contest celebrating the 100th anniversary of Tsinghua University
在线测试:UVA 11991
试题解析
本题要求按照出现次数查找指定元素值的下标位置。最为简便的计算方法是,使用vector类的动态数组存储每个整数值的下标位置。
设动态数组v[],其中v[x]按照输入顺序依次存储整数x的下标位置,即v[x][k-1]存储第k次出现x的下标位置,v[x].size()是原数组中x的最多出现次数。
在输入数组的同时构造动态数组v[]:若输入数组的第i个元素x,则通过v[x].push_back(i)将其下标序号i送入容器v[x](1≤i≤n);v[]以原数组的元素值作为下标,数组元素为一个“容器”,依次存放该整数值的下标。
显然,每次查询可直接在这个记录表中找到结果,无须顺序查找或二分查找,时间复杂度为O(1)。若第j个查询是第k次出现x的下标位置(1≤j≤m),则分析:若v[x].size()<k,则说明x的出现次数不足k次,输出0;否则输出数组中第k次出现x的下标位置v[x][k-1]。
参考程序
#include<iostream> #include<vector> using namespace std; const int MAXX=1000050; vector<int> v[MAXX]; int main() { int n,m; while (scanf("%d%d",&n,&m)==2) //反复输入元素数和查询数 { for (int i=1;i<MAXX;i++) v[i].clear(); //动态数组初始化为空 for (int i=1;i<=n;i++) //输入原数组,构建动态数组 { int x; scanf("%d",&x); //输入第i个元素值x v[x].push_back(i); //将x的下标序号i送入容器v[x] } for (int i=1;i<=m;i++) //依次处理每个查询 { int k,x; scanf("%d%d",&k,&x); //第i个查询是第k次出现x的下标位置 int ans=0; //下标位置初始化为0 if (v[x].size()<k) ans=0; //若x的出现次数不足k次,则输出0 else ans=v[x][k-1]; //否则记下第k次出现x的下标位置 printf("%d\n",ans); //输出第k次出现x的下标位置 } } return 0; }