【问题标题】:Keeping sorted sequence without a less-than predicate在没有小于谓词的情况下保持排序序列
【发布时间】:2014-09-20 17:50:18
【问题描述】:

我想保留一个排序的项目队列,我希望能够弹出具有最低值的项目(有点像std::priority_queue)。项目值是连续的并且不断增加。但是,这些项目没有定义小于比较,它们只有is-previousis-next 谓词(其中A is-previous B == B is-next A),它们还支持++--

我想知道是否有任何算法可以(部分)基于这些谓词对值进行排序?或者,是否有更好的方法来跟踪这样的队列?

在模算术中工作时会出现此问题 - 项目由整数指定,但由于模,小于和大于失去了意义,并且不能再用于排序。

选择的语言是 C++,但我想我可以移植大多数合理的语言。

编辑

向所有试图回答的人致以歉意,在编写示例代码时,我意识到原始问题的格式不正确。这真的很尴尬,但我想强调的是,你的时间并没有浪费,因为我为此流汗了一个小时,然后在 10 分钟内我意识到这是错误的。无论如何,这是我想要的行为:

template <class T>
class OrderedQueue {
    std::vector<T> storage;
    T next;

public:
    OrderedQueue(T lowest_value = T())
        :next(lowest_value)
    {}

    void Push(T t)
    {
        storage.push_back(t);
    }

    T Pop()
    {
        std::vector<T>::iterator it = std::find(storage.begin(), storage.end(), next);
        if(it == storage.end())
            throw std::runtime_error("no such element");
        T value = *it;
        storage.erase(it);
        ++ next;
        return value;
    }
};

我的问题是Pop() 需要O(N)。有没有办法让它更快,使用 is-prev 和 is-next 谓词?

【问题讨论】:

  • 为什么需要对它们进行排序?你不能在中间添加任何东西!
  • @Basilevs 我想对它们进行排序,因为保持排序后的数组通常比未排序的数组快得多。无论如何我都无法使用std::priority_queue,因为它需要一个小于谓词。我会使用std::vector 来存储值,您可以轻松地在那里vector.insert(where, 1, what)
  • 包装双端队列,并禁止除 start 和 end 之外的任何插入。
  • @Basilevs 这没什么用。如果具有最低值的项目最终在中间怎么办?我永远也达不到。
  • 如果您正在编写一个库来维护合同的一端,请告诉我们合同。否则,请告诉我们实际问题的具体实例,不要过早地尝试概括,因为阅读您实际上并不关心的破碎形式是浪费时间。

标签: c++ algorithm sorting data-structures


【解决方案1】:

我的问题是 Pop() 需要 O(N)。有没有办法让它更快,使用 is-prev 和 is-next 谓词?

您需要提供一种方法来获取密钥的实际模整数表示,例如Integer(x)。那么PushPop都可以在恒定时间内执行。

如果您有整数和模数 M,请使用双向链表数组 (std::list&lt;T&gt; queues[M]),将 Push(x) 实现为 queues[Integer(x)].push_back(x)Pop 作为

size_t i = Integer(next);
// check for existence, raise if necessary
T r = queues[i].front();
queues[i].pop_front();
return r;

如果M 很大并且您预计大多数队列大部分时间都是空的,那么您可以使用std::unordered_map&lt;size_t, std::list&lt;T&gt;&gt; 来获得相同的复杂性但节省空间。

也可以使用std::deque;它只为两种操作提供摊销的常数时间,但在实践中可能会快很多。

【讨论】:

  • 不是每个队列总是最多存储一个整数(假设它们不重复,而是稳步增加——对于您的实现来说,这是最坏的情况序列)?我同意这会起作用,但效率会非常低!如果M = 2^32 怎么办?如果我能得到整数表示,我就可以使用std::setstd::map 并将Push()Pop() 都设为对数时间。
  • @theswine 如果 M 很大,请使用 unordered_map&lt;size_t, list&lt;T&gt;&gt; 来获得恒定的时间推送和弹出。查看更新的答案。
  • 这确实是最快的解决方案,虽然幅度不大。将 100 k 的元素插入问题中描述的 OrderedQueue 仅比使用 std::unordered_map 慢约 100 毫秒。这可以通过更好的编译器或更好的 STL 实现来改善。
  • @theswine 这实际上并不令人惊讶,因为这种方法中的常量非常非常大。你试过std::deque而不是std::list吗?
  • 我只用每个 bin 一个项目来实现它(假设没有重复项目),但我希望 listdeque 稍慢。
【解决方案2】:

在 C++ 中,您的类型不需要定义 operator&lt; 以进行排序或在关联容器中使用。容器和算法可以采用执行比较的谓词。例如,使用is_pred作为谓词对向量进行排序(假设它已经是函数对象或普通函数):

std::sort(v.begin(), v.end(), is_pred);

如果没有,您可以编写自己的函子适配器或 lambda:

std::sort(v.begin(), v.end(), [](T const& lhs, T const& rhs) {
             return apply_is_pred_predicate(lhs,rhs)
          });

这里的要求是关系is-prev必须是严格的弱序。

【讨论】:

  • 是的,这不起作用,因为所描述的谓词不是strict weak ordering。不过谢谢。
  • 是的,is_previous 显然没有定义严格的弱顺序。
猜你喜欢
  • 1970-01-01
  • 2014-08-28
  • 1970-01-01
  • 1970-01-01
  • 2012-10-15
  • 2023-03-20
  • 2012-11-22
  • 1970-01-01
  • 2016-01-01
相关资源
最近更新 更多