Algorithm 面试问题-在时间间隔列表中,哪些数字出现次数最多
我只听说过这个问题,所以我不知道确切的限制。您将得到一个正整数列表。每两个连续值形成一个闭合区间。查找在大多数间隔中出现的数字。如果两个值出现的次数相同,请选择最小的一个 示例:Algorithm 面试问题-在时间间隔列表中,哪些数字出现次数最多,algorithm,intervals,Algorithm,Intervals,我只听说过这个问题,所以我不知道确切的限制。您将得到一个正整数列表。每两个连续值形成一个闭合区间。查找在大多数间隔中出现的数字。如果两个值出现的次数相同,请选择最小的一个 示例:[4,1,6,5]结果是[1,4],[1,6],[5,6],其中1,2,3,4,5分别出现两次。正确答案应该是1,因为它是最小的 不幸的是,我不知道如果不采用O(n^2)方法,如何实现这一点。我能想到的唯一优化方法是合并连续的降序或升序间隔,但这并不能真正起作用,因为[4,3,2]会计算两次3 编辑:有人用此链接评论(但
[4,1,6,5]
结果是[1,4],[1,6],[5,6]
,其中1,2,3,4,5
分别出现两次。正确答案应该是1,因为它是最小的
不幸的是,我不知道如果不采用O(n^2)方法,如何实现这一点。我能想到的唯一优化方法是合并连续的降序或升序间隔,但这并不能真正起作用,因为[4,3,2]
会计算两次3
编辑:有人用此链接评论(但随后删除)了一个解决方案。我觉得这是最优雅的,尽管它没有考虑到我输入的某些元素是某些间隔的开始和结束。根据它们的起始值对间隔进行排序。然后从左(全局最小值)到右(全局最大值)滑动一行。在每个集合点(间隔的开始或结束)计算与滑动线的交点数(在
O(log(n))
)。此算法的时间复杂度为O(n log(n))
(n
是间隔数)。如果范围数组中的最大数小于数组的最大大小限制,我的解决方案将以复杂度O(n)工作
1-我创建了一个新数组来处理范围并使用它来查找
在所有间隔中出现最多的数字。为了简单起见,让我们使用
你的例子。输入=[1,4]、[1,6]、[5,6]。让我们叫新的
数组进程,并将其长度设为6,然后将其初始化为0
进程=[0,0,0,0,0,0]
2-然后循环所有间隔,并用(+1)和标记开始
我的范围后面的单元格以(-1)结束
- 对于范围[1,4]进程=[1,0,0,0,-1,0]
- 对于范围[1,6]进程=[2,0,0,0,-1,0]
- 对于范围[5,6]进程=[2,0,0,0,0]
public static void main(String[] args) {
int[] arr = new int[] { 4, 1, 6, 5 };
System.out.println(solve(arr));
}
public static int solve(int[] range) {
// I assume that the max number is Integer.MAX_VALUE
int size = (int) 1e8;
int[] process = new int[size];
// fill process array
for (int i = 0; i < range.length - 1; ++i) {
int start = Math.min(range[i], range[i + 1]);
int end = Math.max(range[i], range[i + 1]);
process[start]++;
if (end + 1 < size)
process[end + 1]--;
}
// Find the number that appears in most intervals (smallest one)
int appear = process[0];
int max = appear;
int solu = 0;
for (int i = 1; i < size; ++i) {
appear += process[i];
if (appear > max){
solu = i;
max = appear;
}
}
return solu;
}
publicstaticvoidmain(字符串[]args){
int[]arr=新的int[]{4,1,6,5};
System.out.println(solve(arr));
}
公共静态int solve(int[]范围){
//我假设max number是Integer.max\u值
int size=(int)1e8;
int[]进程=新的int[size];
//填充进程数组
对于(int i=0;i最大值){
solu=i;
最大值=出现;
}
}
返回solu;
}
}将这些视为括号:
(
开始和间隔,)
结束。现在检查每对[a,b]的边界,并对每个位置的间隔开始/结束标记进行计数:较低的数字从左侧获得间隔开始;数字越大,间隔越近。对于给定的输入:
Process [4, 1]
result: [0, 1, 0, 0, 0, -1]
Process [1, 6]
result: [0, 2, 0, 0, 0, -1, 0, -1]
Process [6, 5]
result: [0, 2, 0, 0, 0, -1, 1, -2]
现在,只需对该列表进行累加;最大值的位置是您想要的答案
result: [0, 2, 0, 0, 0, -1, 1, -2]
cumsum: [0, 2, 2, 2, 2, 1, 2, 0]
请注意,最终总和必须为0,并且不能为负。最大值为2,它首先出现在位置1处。因此,1是出现最大(2)数量的最小整数
不,输入一次,数字范围一次。请注意,使用一个简单的值表,可以节省存储空间。处理表的外观如下所示:
[(1, 2)
(4, -1)
(5, 1)
(6, -2)]
如果输入的开始和停止间隔都是一个数字,那么需要首先处理开始。例如,[4,3,2]看起来像
[(2, 1)
(3, 1)
(3, -1)
(4, -1)]
注意:根据输入的大小维护排序插入列表是O(n^2)时间;之后对列表进行排序是O(n log n)。或者是O(n)空格
我的第一个建议是,在数字本身上建立索引,即O(n)时间,但O(r)输入值范围上的空间。
[主要的观察结果是,结果将是输入中的一个数字(证据留给读者作为简单的练习,yada-yada) 我的解决方案将受到@Prune解决方案的启发。重要的一步是将输入数字映射到输入中所有不同数字的顺序
我将使用C++ STD。我们可以先把所有的数字装入一个集合。然后我们可以从它创建地图,它把一个数字映射到所有数字中的顺序。< /P>
int solve(input) {
set<int> vals;
for (int n : input) {
vals.insert(n);
}
map<int, int> numberOrder;
int order = 0;
for (int n : vals) { // values in a set are ordered
numberOrder[n] = order++;
}
int求解(输入){
设置VAL;
for(int n:输入){
插入(n);
}
地图编号器;
整数阶=0;
对于(int n:vals){//集合中的值是有序的
numberOrder[n]=订单++;
}
然后我们创建进程数组(类似于@Prune的解决方案)
int进程[map.size()+1];//添加到结束元素之后
int curr=输入[0];
对于(int i=0;i int process[map.size() + 1]; // adding past-the-end element
int curr = input[0];
for (int i = 0; i < input.size(); ++i) {
last = curr;
curr = input[i];
process[numberOrder[min(last, curr)]]++;
process[numberOrder[max(last, curr)] + 1]--;
}
int appear = 0;
int maxAppear = 0;
for (int i = 0; i < process.size(); ++i) {
appear += process[i];
if (appear > maxAppear) {
maxAppear = appear;
maxOrder = i;
}
}
for (pair<int, int> a : numberOrder) {
if (a.second == maxOrder) {
return a.first;
}
}
}