【问题标题】:How to find maximum(or minimum) in any subarray(of any size) in a given array?如何在给定数组的任何子数组(任何大小)中找到最大值(或最小值)?
【发布时间】:2018-01-03 23:37:09
【问题描述】:

我们得到一个数组和一些查询。每个查询包含两个数字ij。我们需要在给定数组中从索引i 开始到索引j 结束的子数组中找到最大(或最小)元素。

例如。

arr = [2 , 3 , 5,  8 , 4 , 9]

query 1: (2 , 4)

与此查询对应的子数组将是[5 , 8 , 4]。因此,最大值为8

注意:查询数约为 10^5,数组中约有 10^6 个元素。程序执行的时间限制也是 1s 。所以,我猜需要一个解决方案,每个查询的复杂度为 O(log n) 或更小,其中 n 是数组中的元素数。

【问题讨论】:

  • 简单方法:ij 线性迭代并找到最大值或最小值。 更好的方法: 建立一个RSQ。这一切都取决于数组中的项目总数、查询的数量和复杂性。
  • 谢谢@yeldar。数组中将有大约 10^6 个元素和大约 10^5 个查询。程序应在 1 秒内给出结果。所以我认为这种简单的方法不会被证明是有用的。如果您详细说明第二种方式,那将非常有帮助。
  • 据我记忆中的 ACM 经验,10^6 听起来与 RSQ 限制完全一样。随便找求和段树,网上应该有解决办法。您使用什么语言?
  • @yeldar 我使用 C++
  • 看看这个...RMQ topcoder tutorial

标签: c++ arrays algorithm


【解决方案1】:

详细说明 Yeldar 的 RSQ 想法,并假设您只需要找到最大值(否则,也要对最小值重复此结构): - 您已经拥有每个条目的值。现在将您的数组分成对,并存储每对的最大值。 (所以在你的例子中你会得到 3,8,9)。然后将它们分成对(4 个原始条目)并存储其中的最大值(所以 8,9;奇数的一个单独存在)。重复直到你只剩下一对,给你整个数组的最大值。因此,您有多个级别的树,每个级别对应一个子数组。

-现在,您可以使用这棵树更有效地找到每个最大值:如果您需要找到从 i 到 j 的最大值,请在树中找到完全包含从 i 到 j 的范围的最小子数组。现在您可以(从您的树中)看到该子数组的最大值,因此向下追踪(因为每个较高级别的子数组由两个较低级别的子数组组成)直到您得到完全包含在从 i 到j,或完全不相交。跟踪时,跟踪您不走的每条路径的最大值。

如果它完全被包含,您就有答案(该范围的最大值)。如果它完全不相交,那么这不是你的答案(它来自不在范围内的东西),所以从你没有走的路径中取最高最大值并重复这个过程(添加任何新的未走路径),直到你的最大值确实最终来自一个完全包含在范围内的子数组。

【讨论】:

  • 您好,感谢您的解释。你能给我一些链接,我可以在其中找到与此相关的代码,因为每当我在 Google 上搜索 RSQ 时,它都会给我一些奇怪的东西。
  • @GauravJha:您已经获得了指向great article on TopCoder 的链接。阅读“A 解决方案”部分。该部分没有代码,但解释清楚,您应该能够自己编写代码。如果不能,请使用您编写的部分代码发布问题并解释您遇到的困难。
【解决方案2】:

您可以尝试通过遍历所有元素来存储初始输入的最大值和最小值,如果 i 和 j 包含先前的输入,则可以通过使用先前的结果来节省时间。

【讨论】:

  • 在最坏的情况下能承受吗?
  • 最坏的情况是索引没有重叠。那么这个方法就相当于把所有的元素都遍历一遍,求最大值和最小值。在一般情况下,与其他方法相比,这种方法会更有效。
【解决方案3】:

可以使用 STL:max_element

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
    std::vector<int> v{2,3,5,8,4,9};
    int initial , end;
    cin>>initial>>end;
    cout <<*(std::max_element(v.begin()+initial,v.begin()+end));
    return 0;

}

其他方式仅适用于 c++17

http://coliru.stacked-crooked.com/view?id=3de476681d9e1374

#include <iostream>
#include <algorithm>
#include <experimental/array>
using namespace std;

int main()
{
    decltype(auto) v = std::experimental::make_array(2,3,5,8,4,9);
    int initial=2,end=4;
    cout <<*(std::max_element(v.begin()+initial,v.begin()+end));
    return 0;

}

【讨论】:

  • 它说函数 max_element 在比较的元素数量上具有线性复杂性,如果您会看到我的编辑,那么您会发现它将超时。顺便说一句,谢谢我学到了一个对我来说全新的功能。
  • 您能否分享示例输入或您尝试此操作的论坛
【解决方案4】:

我能想到的最简单的方法是将数组存储为平衡搜索树,在每个节点下存储最小值,这样每个索引都映射到它的值。

然后你可以除以j,除以i(平衡树维护当前节点子树的最小值),并返回根最小值。然后加入你得到的(最多)3棵树。

构造是O(n log n),查询是O(log n) [因为split & join是在O(log n)中完成的]

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
【解决方案5】:
counter = i-1
max = MIN_INTEGER
min = MAX_INTEGER
while counter < j:
  if arr[counter] > max:
    max = arr[counter]
  if arr[counter] < min:
    min = arr[counter]
  counter++
return max,min

编辑:

或者,将其插入到 maxheap 和 minheap (O(n)) 中,然后查询将全部为 O(1)。

【讨论】:

  • 我认为这个解决方案在时间限制上不会有帮助。
  • @GauravJha 没有看到您的编辑。我相应地进行了编辑。希望对您有所帮助。
  • 可能我听起来很笨,但我没有得到要插入 maxheap 或 minheap 的内容(它是完整的数组吗)。如果您正在谈论在 minheap 或 maxheap 中插入整个数组,那么我们将如何在 O(1) 中获得查询结果。顺便说一句,谢谢。
  • 嗯,实际上我确实意识到这仍然需要 O(n) 时间,除非您可以预处理数组/进程一次然后执行许多查询。此外,这意味着最坏的情况可能仍然是 O(nlogn)。假设您想找到只有 [2] 的子数组的最大值(其中 2 是整个数组中的最小值),那么您最终会弹出整个树。抱歉,我似乎无法提供更好的解决方案。
【解决方案6】:

您可以使用 C++ STL(这是对先前解决方案的修改。我们需要在末尾添加 +1。因为它需要范围 [start, end)中的索引。希望这可以帮助。尝试这个问题以获得更好的理解。 https://www.hackerrank.com/challenges/service-lane/problem

#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int> arr{2,3,5,8,4,9};
    int i; // start index
    int j; // end index
    cin >> i >> j;
    cout << *max_element(arr.begin()+i, arr.begin()+j+1);

    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-10
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多