【问题标题】:Why would data structures like queues and stacks be used if arrays are easier to use and more powerful?如果数组更易于使用且功能更强大,为什么还要使用队列和堆栈等数据结构?
【发布时间】:2016-04-07 09:37:03
【问题描述】:

这可能是一个愚蠢的问题,但它一直困扰着我一段时间。大多数编程语言都有数组(例如 Java、C/C++、C#...好吧 Python 有列表!)但在我看到的大部分文献中,一些数据结构(例如堆栈和队列)被视为更基本的数组。但是既然这么多语言都对数组有如此强大的支持,为什么会有人使用堆栈或队列呢?我意识到,从概念上讲,数组以外的数据结构可能更适合模型,但考虑到您可能必须实现自己的堆栈或队列,考虑到基本数组的功能,工作量要大得多。

我想到的一个例子来自 Gale Shapley 上的 these notes 算法Maintain a list of free men (in a stack or queue). 只使用一个数组并相信自己只看它的前端/后端不是更容易吗?

我想我在问,既然大多数都是使用数组实现的,而且数组更强大,为什么还有人会费心使用堆栈或队列之类的东西?

【问题讨论】:

  • 哪里说栈和队列比数组更基本?不在您的引文中。
  • 期望的行为、封装和 DRY。

标签: java data-structures language-agnostic


【解决方案1】:

有几个原因。首先,如果您的实现与算法更接近,这将很有帮助。也就是说,如果您的算法使用堆栈,那么您可以说,例如,stack.push(thing) 而不是写:

if (stack_index >= stack_array.length)
{
    // have to extend the stack
    ....
}
stack_array[stack_index++] = thing

而且你必须在每一个地方编写代码,你将某些东西压入堆栈。而且每次想要从堆栈中弹出时,您都必须编写多行代码。

我不了解你,但我发现在编写这样的代码时很容易出错。

将该功能封装到Stack 对象中似乎更容易、更清晰、更可靠,然后您可以按预期使用:调用pushpop 方法。

另一个好处是,当您发现自己必须做一个快速的线程安全堆栈时,您可以修改您的 Stack 类以锁定任何更改内部结构(即数组)的代码,以及自动调用任何调用者有一个线程安全的堆栈。如果您要直接在代码中寻址数组,那么您必须去访问底层数组的每个地方并添加您的锁定语句。如果你在某个地方犯了错误,我会给出比偶数更高的概率,然后你就会有一个有趣的时间来追踪间歇性故障。

数组本身并不是特别强大。他们灵活,但他们没有智慧。我们将行为包装在数组周围以限制它们可以做的事情,这样我们就不必“相信自己”不会做愚蠢的事情,也可以更轻松地做正确的事情。如果我正在实现一个使用队列的算法,那么我将考虑具有 Enqueue 和 Dequeue 操作的队列,而不是链表或具有头尾索引的数组必须管理等等。

最后,如果您编写堆栈或队列或类似的数据结构实现,对其进行测试并证明它是正确的,您可以在多个项目中反复使用它,而无需再次查看代码。 它只是工作。这与使用数组滚动你自己相反,因此你不仅必须在使用堆栈的每个项目中调试它,而且你有可能搞砸每一次推送或弹出。

总而言之,我们创建数据结构而不是使用原始数组:

  1. 因为考虑数据结构操作而不是使用数组的机制更容易。
  2. 代码重用:编写一次,调试,然后在多个项目中使用。
  3. 简化代码(stack.push(item) 而不是多行数组索引)。
  4. 减少出错的可能性。
  5. 更容易让下一个人过来了解您所做的事情。 “哦,他正在将物品推到堆栈上。”

【讨论】:

    【解决方案2】:

    在内部,我想说这些类中的大多数都是在数组的帮助下实现的。但是将数组用作堆栈或队列会很乏味。数组是固定长度的东西,你不能在任意位置插入东西。您将不得不对数组元素进行大量复制,放大和缩小数组,或者记住您的头部和尾部位置等。Stack 和 Queue 类为您完成所有这些,您可以使用更方便的推送, pop 等方法。

    【讨论】:

    • 树、链表...不仅仅是数组。
    • @Kayaman 但在某些情况下它们具有有用的属性:二叉树易于搜索,链表易于添加和删除表单(这些是数组无法做到的事情)。
    • @Celeritas 我知道。我指的是“这些类中的大多数都是用数组实现的”,当时有很多数据结构不依赖于数组。
    • @Kayaman 我们谈到了堆栈和队列。但是,是的,当然可以有其他实现。我们不必在意,这是好事。 :-)
    • @Vampire 我认为他所说的“数据结构队列和堆栈”并不意味着队列和堆栈。此外,链表对这两者都非常有用,树也可用于优先级队列。事实上,查看 Java 集合,您会发现数组在内部实现中没有那么多 使用。当然ArrayListArrayBlockingQueue 和其他显而易见的,HashMap 的内部哈希表是一个数组...
    【解决方案3】:

    数组只是另一种类型的数据结构。它们有特定的用例,就像其他任何用例一样。

    所有数据结构都有特定的属性,例如

    • 固定大小与可变大小;
    • 有序与无序;
    • 允许重复 vs 禁止重复
    • 协变与否
    • 可以包含或不包含原语
    • 插入/删除/检索操作的特定时间复杂度
    • 迭代顺序
    • ...

    您选择使用数组还是任何其他数据结构取决于您要尝试做什么,以及该数据结构是否具有您需要的属性。

    最好拥有一个能做好一件事的简单数据结构,而不是试图拥有一个能做所有事情的超级数据结构。

    【讨论】:

      【解决方案4】:

      您可以比数组的大小更容易地更改堆栈和队列的大小 那是非常困难的。
      如果您知道数组应该有多大,请使用数组。但如果你不知道堆栈和队列是更好的选择。

      【讨论】:

      • 我没想过,但是用数组实现时,“你可以比数组的大小更容易改变堆栈和队列的大小”是怎么回事?
      • 它们不一定使用数组来实现。但即使它们是,您也可以只添加一个对象,并且类的内部会关心扩大数据存储。基本上,您的问题意味着“如果我都可以自己做,为什么要使用现有代码?”。您可以使用许多库,但您也可以在自定义代码中完成它们所做的所有事情。只是没有多大意义。
      【解决方案5】:

      令人钦佩的是,您如此信任自己,但是当您与其他几个开发人员(甚至您自己)一起创建项目时,您不能依赖信任。

      不同的数据结构使代码在做什么更明显,它可以防止你做错事(无论你多么信任自己),它们可以提供性能、并发性或内容保证以及其他许多简单数组的东西做不到或只是不适合。

      当背包被发明时,为什么会有口袋?

      【讨论】:

        【解决方案6】:

        我同意@Vampire。如果您有索引,则数组提供对其任何元素的即时访问。而堆栈和队列为您提供 LIFO 和 FIFO 排序的便利,使您可以更轻松地实现许多算法。此外,使用堆栈和队列更容易添加或删除元素,因为使用数组,您始终拥有有限的内存,并且您的变量位于连续的内存块中,您必须移动大量数据并相应地分配/取消分配内存。请查看此link 了解更多信息。

        【讨论】:

          猜你喜欢
          • 2011-08-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-22
          • 1970-01-01
          • 1970-01-01
          • 2021-11-03
          • 1970-01-01
          相关资源
          最近更新 更多