【问题标题】:Extending std::unordered_set<> for use with std::stack<>扩展 std::unordered_set<> 以与 std::stack<> 一起使用
【发布时间】:2023-03-23 00:52:01
【问题描述】:

我的问题:我想使用std::stack&lt;std::pair&lt;int,int&gt;, std::unordered_set&lt;std::pair&lt;int,int&gt;&gt;

或简称std::stack&lt;coords, std::unordered_set&lt;coords&gt;&gt;

我想知道是否可以扩展std::unordered_set&lt;&gt; 使其在std::stack&lt;&gt; 内部正常运行而不会出现问题。 除此之外,我想知道它是否值得付出努力,而不透露实际的最终用途。我的意思是为了使用 unordered_set 的好处,使用必要的方法设计我自己的模板会比扩展现有的 unordered_set 来满足要求更有效吗?

编辑:通常我会删除一个否定的问题,但我觉得这可能最终对其他误解 std::unordered_set 的可怜人有所帮助。再说一次,我还没有死,所以我很可能会删除它。

编辑 2:下面 AndreyT 的回答有助于弄清楚为什么 std::stack&lt;std::pair&lt;int,int&gt;, std::unordered_set&lt;std::pair&lt;int,int&gt;&gt; 不能成为一件事。我切换到 jxh 的答案,因为它基本上实现了一个并且接近我最终使用的。

【问题讨论】:

  • 我认为应该是 unordered_set 而不是 unordered_set<:pair>>
  • 您希望它如何工作? std::stack 是一个按插入时间排序的容器。 std::unordered_set 是一个由散列函数排序的容器。您能否描述一下您真正想要实现的目标(在性能或示例方面)?
  • 不,unordered_set 中的元素不会保持有序。该顺序纯粹是任意的,并且可以在任何触发重新散列的插入时更改,例如如果桶数为 10,元素 A 的哈希码为 45,元素 B 的哈希码为 675,那么它们都将在同一个桶中(45%10 == 675%10),但如果桶数变为 11,它们将被移动到不同的桶 (45%11 != 675%11)
  • unordered_xxx 容器的全部意义在于 O(1) 查找容器中任意位置的任意成员和 O(1) 插入容器中的任意位置。 std::stack 的全部意义在于您只能在一端访问它。您的问题就像问您是否可以将架子改装为传送带。如果你足够努力,也许你可以,但这没有意义。
  • 我还以为你是因为模仿“Love Shack”而换了 :)

标签: c++ c++11 stl stack unordered-set


【解决方案1】:

正如在另一个答案中已经解释的那样,无序集不是堆栈接口的合适容器,因为提供的容器应该提供排序语义。如其名称所示,无序集不提供排序语义。

您似乎想要的是一个 LIFO 数据结构,但您希望能够在 O(1) 中找到该数据结构中的特定元素,并对其进行操作(我猜是找到并删除它)。

您可以做的最好的可能是您自己的数据结构,该结构具有使用列表的堆栈,以及指向该列表的迭代器的无序坐标映射。

class MyFunkyStack {
    std::list<coord> list_;
    std::stack<coord, std::list<coord>> stack_;
    std::unordered_map<coord, std::list<coord>::iterator> map_;
    //...

    MyFunkyStack () : list_(), stack_(list_), map_() {}

    coord top () const { return stack_.top(); }

    void push (coord c) {
        stack_.push(c);
        map_[c] = list_.end() - 1;
    }

    void pop () { erase(top()); }

    void erase (coord c) {
        std::unordered_map<coord, std::list<coord>::iterator>::iterator i;
        if ((i = map_.find(c)) == map_.end()) return;
        list_.erase(i->second);
        map_.erase(i);
    }

    //...

};

向 B-52 道歉:
如果您在归档的 syslog 中看到一条旧日志行,上面写着:/ “哈希堆栈中的 15 MB”/ 哈希堆栈,是的,是的 // 我正在下载一个开源项目。 / 寻找哈希-ay-泰! / 今天编译哈希表。 // 我给了我一个 tar-ball,它对于电子邮件来说太大了。 / 我们正在为我的哈希堆栈下载它。 // 我给了我一个编译器,它优化了很多,/ 所以快点准备好你的构建盒吧! // 哈希堆栈是我们一起制作的一个有点奇怪的容器。 / 哈希堆栈,宝贝(哈希堆栈宝贝!)// 哈希堆栈,宝贝,哈希堆栈! / 哈希堆栈,宝贝,哈希堆栈! // (哈希,宝贝,这就是它的所在。)/ (哈希,宝贝,这就是它的所在。)// 许可证(哦!)是开源的家伙! / 导致哈希堆栈的共享很酷。 // 这只是一个小班,只有几个领域。 / 这是一个奇怪的时髦堆栈,有点像黑客。 // 不需要析构函数。 / 不需要赋值。 / 不需要移动或复制。 / 不需要赋值。 // 哈希堆栈是我们一起制作的一个有点奇怪的容器。 / 哈希堆栈,宝贝(哈希堆栈宝贝!)// (哈希,宝贝,这就是它的所在。)/ (哈希,宝贝,这就是它的所在。)// 打字和鼠标,/ 思考和编码,/ 几乎什么都没穿,/ 因为我在家工作(是的)。 // 哈希堆栈编译! / 哈希堆栈编译! // 哈希堆栈编译,/ 而其他堆栈仍然只是 / 数组,数组,数组,数组! // 其他堆栈只是推,其他堆栈只是弹出,宝贝! / 最后排队的人最先出来。 // 其他堆栈只是推,其他堆栈只是弹出,宝贝! / 一个奇怪的时髦堆栈! / 一个奇怪的时髦堆栈! // 当我编译(依赖是陈旧的)时,我发送了一封电子邮件! // 我推出了一个新的“品牌”,价格为 -20,/ 所以快点,准备好你的构建箱! // 哈希堆栈是我们一起制作的一个有点奇怪的容器。 / 哈希堆栈,宝贝(哈希堆栈宝贝!)// 哈希堆栈,宝贝,哈希堆栈! / 哈希堆栈,宝贝,哈希堆栈! // (哈希,宝贝,这就是它所在的地方,是的。)/ (哈希,宝贝,这就是它的所在。)// 测试,测试,测试,仍然没有核心,宝贝。 / 多试几个案例,宝贝/ 测试,测试,测试,仍然没有核心,宝贝。 / 你的测试计划是什么? // 测试,测试,测试,仍然没有核心,宝贝。 / 多试几盒,糖/ 测试,测试,测试,仍然没有核心,宝贝。 / 你的测试计划是什么? // 测试,测试,测试,仍然没有核心,宝贝。 / (多尝试几个案例。)/ 测试,测试,测试,仍然没有核心,宝贝。 // 测试一下! (有一个核心,宝贝!)/ 测试一下! (有一个核心!)/ 测试一下! (有一个核心,宝贝!)/ 测试一下! // 你做了什么? / 转载,拦路虎! // 哈希堆栈,宝贝,哈希堆栈! / (哈希,宝贝,这就是它所在的地方,是的。)/ (哈希,宝贝,这就是它的所在。)// 哈希,宝贝,哈希堆栈! / 打字和鼠标,/ 思考和编码,/ 在哈希堆栈上。

【讨论】:

  • 类似,有点,但不完全是我想要的。不过,我肯定会考虑一下,它可能会提供我需要的东西。
  • 你的 funkystack 中的堆栈不应该是一个迭代器堆栈吗?
  • 这个想法是您可以编写一个例程来通过搜索map_ 来删除坐标,然后删除地图条目和列表条目。这也会使其从堆栈中消失。
  • 哦,我错过了堆栈构造函数,我的错。
【解决方案2】:

std::stack is by intent 是按照 LIFO 原则排序的数据结构。在该意图内,它将数据以特定顺序放置到底层容器中,并期望该容器保持该顺序。不可能将任何会自行更改排序的容器用作底层容器。消除了任何无序数据结构或按照其他原则排序的数据结构。

但是,该逻辑仅适用于您想要保持std::stack 的上述意图,即使用它来实现堆栈,字面意思。

如果您不在乎最终得到什么(在最极端的情况下,如果您只想“编译”您的代码),那么您可以将std::stack 视为一个“瘦”抽象接口适配器,只不过是调整了一些底层类的接口而已。从这个角度来看,您绝对可以将任何东西用作底层“容器”,只要它满足所需的接口规范。据我记得,这包括emptysizebackpush_backpop_back 方法的可用性,可能还有一些 typedef。

unordered_set,您可能已经注意到,不满足所需的接口规范。你可以正式地“扩展”它只是“让它编译”,但我不明白你怎么可能让它为一个真正的堆栈工作。

【讨论】:

  • 所以 std::stack 无法知道最后一个项目是什么,除非该项目专门保存在预期的底层容器的任何一端?此外,无序集不知道何时将一个项目添加到它相对于它存储的另一个项目??
  • std::stack 完全保持无状态。它所做的只是调整现有容器的界面,隐藏它的一些成员(并重命名其他成员),因此您只能在容器的末尾插入和擦除。没有“结束”的底层容器不可能std::stack 一起使用,因为它甚至不提供push_backpop_back 成员函数。
  • @JonathanWakely 您的回答似乎暗示了我对第二个问题的回答。自从收到有关此问题的意见以来,我一直怀疑这一点。 std::unordered_set 不维护有关何时添加某些内容的任何形式的信息?最初,我认为迭代器将按照添加内容的顺序进行保存。不过,我想明确地知道我怀疑是真还是假。
  • 查看我对上述问题的评论。有一个命令,但它是任意的,可以随时更改。线索就在“无序”这个名字中
猜你喜欢
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
  • 2021-05-30
  • 2015-06-21
  • 2014-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多