始终保留n个最佳元素的数据结构

问题描述 投票:0回答:9

我需要一个始终保存迄今为止插入的最大项目的数据结构(无特定顺序)。


因此,如果

n

为 3,我们可以进行以下会话,其中我插入一些数字并且容器的内容会发生变化:


n

你明白了。数据结构的名称是什么?实现这一点的最佳方法是什么?或者这个在某个图书馆里?

我正在考虑使用一个其元素(委托)具有

[] // now insert 1 [1] // now insert 0 [1,0] // now insert 4 [1,0,4] // now insert 3 [1,4,3] // now insert 0 [1,4,3] // now insert 3 [4,3,3]

的容器,它使用反向比较,因此

priority_queue
将删除最小的元素。因此
pop
函数首先检查要插入的新元素是否大于最小元素。如果是这样,我们就扔掉最小的元素并推送新元素。

(我心里有一个

insert

实现,但问题仍然与语言无关。)

    

language-agnostic data-structures priority-queue
9个回答
14
投票

这个想法是将数组(称之为 A)视为深度为 n 的完全二叉树:

忽略A[0];将A[1]视为根节点

对于每个节点 A[k],子节点是 A[2*k] 和 A[2*k+1]
  • 节点 A[N/2..N-1] 是叶子
  • 要将树维护为“堆”,您需要确保每个节点都小于(或等于)其子节点。这称为“堆条件”:
A[k]

A[k]
  • <= A[2*k]
  • 使用堆来维护最大的N个元素:
  • <= A[2*k+1]
请注意,根 A[1] 是堆中最小的元素。

将每个新元素 (x) 与根进行比较:如果较小(x
  • 否则,则将新元素插入到堆中,如下所示:
  • 从堆中移除根(A[1],最小元素),并拒绝它
  • 将其替换为新元素 (A[1]:= x)
    • 现在,恢复堆状态:
    • 如果 x 小于或等于它的两个子元素,则完成
    • 否则,将 x 与最小的孩子交换
      • 在每个新位置重复测试和交换,直到满足堆条件
    • 基本上,这将导致任何替换元素“过滤”树,直到到达其自然位置。这最多需要 n=log2(N) 步,这是你能得到的最好的结果。此外,树的隐式形式允许非常快速的实现;现有的有界优先级队列库很可能会使用隐式堆。

在 Java 中,你可以使用 SortedSet 实现,例如通过 TreeSet。每次插入后检查集合是否太大,如果是则删除最后一个元素。


6
投票

priority_queue 是 C++ 中与 STL 最接近的东西。您可以将其包装在另一个类中以创建您自己的自动修剪大小的实现。


4
投票

插入数据

排序
  1. 删除第n个元素之后的所有内容
  2. std::priority_queue 会为你执行第 2 步。

A

有界优先级队列

2
投票
编辑

:它被称为C++。我不确定 C++ STL 是否包含类似的东西。


是否可以只从已排序的集合中取出前 n 个元素?


1
投票

在 Pyhton 中,使用 heapq。在它周围创建一个小包装,如下所示:


1
投票

...


是的,您可以保持 N 号的最小头部 然后在每次插入时将新项目与根项目进行比较 如果根项“大于”根项,则弹出根项并插入该项 最后你得到了 N 个最大的物品


0
投票

这是 C++ 11 中的一个简单实现。 可能不是最好的,但易于使用且简洁。


0
投票

输出:

#include <iostream>
#include <map>

/// @brief Maintains a set of N elements with the best keys. (duplicate keys are allowed)
template<class Key, class T, class Compare = std::less<Key> >
class KeepNBests{
public:
    KeepNBests(size_t N=0):N(N), is_less(Compare()){}

    bool add(const Key& key, const T& t){
        // If there are enough elements,
        if(bests.size()>N // and the candidate key is worse than the worst stored key...
            && is_less(key, bests.cbegin()->first)){
            return false; // nothing to do
        }
        // else insert the new candidate
        bests.insert({key, t});
        // keeps max N elts
        while(bests.size()>N){
            bests.erase(bests.cbegin());
        }
        return true;
    }
    const std::multimap<Key, T, Compare>& get()const{
        return bests;
    }
public:
    const size_t N;
private:
    Compare is_less;
    std::multimap<Key, T, Compare> bests;
};


int main() {
    
  KeepNBests<double, std::string> best(3);
  best.add(0., "bad");
  best.add(0., "bad");
  best.add(300., "the best");
  best.add(10., "ok");
  best.add(-1., "bad");
  best.add(-7., "very bad");
  best.add(-1., "bad");
  best.add(100., "good");
  best.add(2., "bof");
  best.add(200., "the second");

  for(const auto& b : best.get()){
    std::cout << b.first << " " << b.second << std::endl;
  }
  return 0;
}

创建一个最小堆,同时存储一个计数器。

-1
投票

您可以通过以下方式执行此操作:O(1) insert、get-min 和 O(log log n) extract-min。

100 good 200 the second 300 the best

或者,您可以使用 O(log n) insert 和 O(1) 执行另一个操作提到的操作。

[1]


[2]

M. Thorup,“常数时间内密钥递减的整数优先级队列和单源最短路径问题”,第 35 届 ACM 计算理论年度研讨会论文集,美国纽约,2003 年,第 149–158 页。

[1]
G. S. Brodal、G. Lagogiannis、C. Makris、A. Tsakalidis 和 K. Tsichlas,“指针机中的最佳手指搜索树”,J. Comput。系统。科学,卷。 67,没有。 2,第 381–418 页,2003 年 9 月。


© www.soinside.com 2019 - 2024. All rights reserved.