【问题标题】:An algorithm to find L th min/max from length K subarray [duplicate]一种从长度为 K 的子数组中找到 L th min/max 的算法
【发布时间】:2020-03-19 18:24:09
【问题描述】:

考虑一个值数组{3,2,8,4,7,6,1}。我需要一个算法来找到每个长度为 K 子数组中的第 L 个最大/最小。

例如,对于上面的数组,设K = 5,L = 3。我需要找到每个长度为K的子数组的第3个最小值。

预期输出:

Subarray:
[3,2,8,4,7] = Third minimun is 4.
[2,8,4,7,6] = Third minimum is 6.
[8,4,7,6,1] = Third mininum is 6.

请帮我找到算法。

【问题讨论】:

  • 我最初认为是自平衡 BST,但在它的中序遍历中找到 Lth min/max 时会是 O(L)。因此,对于大小为 k 的窗口,这可能是最小堆和最大堆的组合,我们在最小堆中保留 L-1 个元素,在最大堆中保留 L-K 个元素。因此,获得 Lth min/max 将是 O(1)。
  • @vivek_23 通过使用以该节点为根的子树的大小来扩充每个节点,将 BST 转变为顺序统计树。然后,在 O(log k) 中找到订单统计信息
  • @Dave 是的,有道理。
  • @vivek_23 何时使用最小堆和最大堆,何时向堆中添加新元素以及何时从堆中提取最小值和最大值。你能把伪代码分享给我吗?
  • @Raj 在线查找如何找到整数流的运行中位数。您的问题看起来与此类似。

标签: algorithm data-structures


【解决方案1】:

一种方法是:

  1. 将输入集合分解为所需长度的子集合序列。
  2. 从每个子集合中提取独特的元素
  3. 对独特元素的集合进行排序
  4. 从排序后的集合中取出第三个元素

在 Clojure(Lisp 变体)中,我们可以这样做:

(defn nth-least-by-l [c n l]
  (map #(list (nth (sort (set %)) (dec n)) %) (partition l 1 c)))

为您的测试集合调用它:

(nth-least-by-l [3 2 8 4 7 6 1] 3 5)

返回

((4 (3 2 8 4 7)) (6 (2 8 4 7 6)) (6 (8 4 7 6 1)))

这有点难以阅读。使用以下内容进行漂亮打印

(do
  (println "Third minimums:")
  (doseq [[nval col] (nth-least-by-l [3 2 8 4 7 6 1] 3 5)]
    (println col " => " nval)))

产生以下输出:

(3 2 8 4 7)  =>  4
(2 8 4 7 6)  =>  6
(8 4 7 6 1)  =>  6

这是正确的。

【讨论】:

    【解决方案2】:

    这是一个仅使用 C++ 多集的更便携的解决方案。这个想法是维护两个多重集,第一个将包含最小的 L-1 数字,而另一个将包含其余的。复杂度为 O(N log K)。前 K 个插入和初始平衡在 O(K log K) 中工作。每个后续的平衡操作都将花费 O(log K) 时间,因为集合的不平衡绝对值最多为 1。

    #include <iostream>
    #include <vector>
    #include <set>
    using namespace std;
    
    int main() {
        int n, k, l;
        cin >> n >> k >> l;
        vector<int> a(n);
        for (int& x : a)
            cin >> x;
        multiset<int> small, large;
    
        auto balance = [&]() {
            while ((int)small.size() >= l) {
                large.insert(*--small.end());
                small.erase(--small.end());
            }
    
            while ((int)small.size() < l-1) {
                small.insert(*large.begin());
                large.erase(large.begin());
            }
        };
    
        auto add = [&](int x) {
            small.insert(x);
        };
    
        auto rem = [&](int x) {
            auto it = small.find(x);
            if (it != small.end())
                small.erase(it);
            else {
                it = large.find(x);
                large.erase(it);
            }
        };
    
        for (int i=0; i<k; i++)
            small.insert(a[i]);
        balance();
        for (int i=k; i<=n; i++) {
            cout << *large.begin() << '\n';
            if (i < n) {
                add(a[i]);
                rem(a[i-k]);
                balance();
            }
        }
    }
    
    

    【讨论】:

      【解决方案3】:

      你可以在 O(N log K) 时间内解决这个问题,其中 N 是数组的大小。

      首先请注意,如果我们解决 min 的问题,max 的解决方案是相同的,只需将原始数组元素乘以 -1。

      因此,为了解决这个问题,我们需要一个支持:添加、删除、在集合中查找第 k 个项目的数据结构,然后我们可以使用滑动窗口循环所有需要的间隔,在需要时添加和删除项目。

      实际上有很多结构可以在 O(log N) 时间内支持所有这些操作,您可以使用段树、最小/最大堆、顺序统计树(一种 B-树)等等。

      顺序统计树是一种二叉搜索树,它支持插入、查找和删除之外的两个附加操作:

      Select(i) — 找到存储在树中的第 i 个最小元素。

      Rank(x) – 找到元素 x 在树中的排名,即它在树的元素排序列表中的索引。

      当使用自平衡树作为基本数据结构时,两者都是 O(log n)。

      示例代码(C++ 和 g++ 编译器,幸运的是库中有顺序统计树)

      #include <bits/stdc++.h>
      using namespace std;
      
      #include <ext/pb_ds/assoc_container.hpp>
      #include <ext/pb_ds/tree_policy.hpp>
      using namespace __gnu_pbds;
      typedef tree<pair<int,int>,null_type,less<pair<int,int>>,rb_tree_tag,tree_order_statistics_node_update>ordered_set;
      
      int main()
      {
          int n,k,L;
          cin>>n>>k>>L;
          vector<int>A(n+1);
          for(int i=0; i<n; i++)cin>>A[i];
          ordered_set S;
          for(int i=0; i<k; i++)
              S.insert({A[i],i});
      
          for(int i=k; i<=n; i++)
          {
              cout<<S.find_by_order(L-1)->first<<" ";
              S.erase(S.find({A[i-k],i-k}));
              S.insert({A[i],i});
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-06-27
        • 2021-09-21
        • 2011-08-25
        • 2017-06-30
        • 1970-01-01
        • 2020-08-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多