根据维基百科,一个堆栈:
是后进先出(LIFO)抽象数据类型和线性数据结构。
而数组:
是由一组元素(值或变量)组成的数据结构,每个元素均由至少一个数组索引或键标识。
据我了解,它们相当相似。那么,主要区别是什么?如果它们不相同,那么数组不能做什么,而栈不能做什么呢?
根据维基百科,一个堆栈:
是后进先出(LIFO)抽象数据类型和线性数据结构。
而数组:
是由一组元素(值或变量)组成的数据结构,每个元素均由至少一个数组索引或键标识。
据我了解,它们相当相似。那么,主要区别是什么?如果它们不相同,那么数组不能做什么,而栈不能做什么呢?
Answers:
好吧,您当然可以用数组实现堆栈。区别在于访问。在数组中,您具有一个元素列表,并且可以随时访问其中的任何元素。(想想一堆木块,它们都是连续布置的。)
但是在堆栈中,没有随机访问操作。只有Push
,Peek
并且Pop
,所有这些都与堆栈顶部的元素专门处理。(想想现在垂直堆叠的木块。您不能触摸塔顶以下的任何东西,否则它会掉落。)
在一个纯粹的堆栈,唯一允许的操作Push
,Pop
以及Peek
但是在实际应用中,这是不完全正确。或者更确切地说,该Peek
操作通常允许您查看堆栈上的任何位置,但要注意的是它相对于堆栈的一端。
因此,正如其他人所说的,数组是随机访问,所有内容都引用到数组的开头。
在堆栈中,您只能在堆栈的工作端添加/删除,但是您仍然可以读取随机访问权限,但它是引用到工作端的。那是根本的区别。
例如,当您将堆栈上的参数传递给函数时,被调用方不必弹出参数即可查看它们。它只是将局部变量压入堆栈,并根据与堆栈指针的偏移量引用所有局部变量和参数。如果仅使用数组,那么被调用方将如何知道在哪里查找其参数?完成被调用方后,它将弹出其局部变量,推送一个返回值,将控制权返回给调用方,然后调用方弹出该返回值(如果有),然后将参数从堆栈中弹出。这样做的好处是,无论您嵌套在函数调用中有多远(假设您没有用完堆栈空间),它都可以工作。
那是一种特殊的用法/实现,但是它说明了区别:数组总是从头开始引用,而堆栈总是从某个工作结束位置引用。
堆栈的一种可能的实现方式是数组加一个索引,以记住工作端的位置。
我认为这里最大的困惑是实现与基本数据结构。
在大多数(更多基本语言)中,数组表示固定长度的元素集,您可以在给定时间访问任何元素。实际上,您有一堆这样的元素并没有告诉您应该如何使用它(坦率地说,只要您不违反用法,计算机就不会知道/不在乎您如何使用它)。
堆栈是用于表示应该以某种方式处理的数据的抽象。这是一个抽象的概念,因为它只是说它必须具有一些可以添加到顶部或从顶部删除的子例程/方法/函数,而顶部以下的数据不会受到影响。纯粹选择以这种方式使用数组。
您可以从许多不同类型的数据结构中堆叠:数组(具有最大大小),动态数组(空间不足时可能会增长)或链接列表。我个人认为链表最好地代表了堆栈的限制,因为您需要付出一些努力才能看到超出第一个元素的内容,并且很容易添加到前面并从前面删除。
因此,您可以使用数组制作堆栈,但它们并不等效
我不会说它们“非常相似”。
数组用于保存以后将顺序或通过索引访问的内容。数据结构并不意味着任何访问方法(FIFO,LIFO,FILO等),但是如果您愿意,可以使用这种方法。
堆栈是跟踪事物生成的方式。根据创建的堆栈的类型,隐式/必需访问方法。框架堆栈将是LIFO的示例。免责声明-我可能在这里混用我的数据结构分类法,而堆栈可能确实只允许使用LIFO。否则,它将是另一种类型的队列。
因此,我可以将数组用作堆栈(尽管我不愿意),但不能将堆栈用作数组(除非我非常努力地工作)。
数组中的数据可以通过键或索引访问。从堆栈顶部弹出时,可以访问堆栈中的数据。这意味着,如果您知道数组的索引,则从数组访问数据很容易。从堆栈访问数据意味着您必须一直弹出元素,直到找到所需的元素为止,这意味着数据访问可能需要更长的时间。
从程序员的角度来看,数组是固定的,其位置和大小固定,您知道它在其中的位置以及整个对象的位置。您可以访问所有内容。
使用堆栈时,您只能坐在堆栈的一端,却不知道堆栈的大小或可以安全走多远。您对它的访问仅限于您已分配的数量,如果您只是猛击到堆或程序空间中,那么您甚至在分配所需数量时甚至都不知道是否要分配它。您对堆栈的看法是一个小的数组,您已为其分配了自己的大小,想要的大小,可以控制并可以看到。您的部分与数组没有什么不同。区别在于,出于论证和术语的考虑,您的数组固定在另一个数组的一端,您无法看到它们,也不知道大小是多少,并且不能触摸它而不会造成伤害。无论如何,除非是全局数组,否则数组通常都是在堆栈上实现的,因此在该函数运行期间,数组和堆栈共享相同的空间。
如果您想进入硬件方面,那当然是处理器特定的,但是通常该数组基于已知的起点/地址,大小由编译器/编程器知道,并且地址被计算在内,有时使用寄存器偏移量寻址(从该基本寄存器值加上该偏移量寄存器值定义的地址中加载一个值,同样,在编译时,它可能是立即偏移量,不一定是基于寄存器的,当然取决于处理器),这在汇编中非常重要类似于以高级代码访问数组。同样,对于堆栈,如果可用,您也可以使用寄存器或立即偏移量寻址,尽管它通常使用特殊的寄存器,无论是堆栈指针本身还是编译器/编程器保留的用于访问该堆栈帧的寄存器功能。对于某些处理器,使用和/或要求使用特殊的堆栈访问功能来访问堆栈。您有推送和弹出说明,但是它们的使用频率不如您想像的那样,并且并不真正适用于此问题。对于某些处理器,push和pop是可与任何寄存器一起使用的别名,可以在任何地方使用,而不仅仅是堆栈上的堆栈指针,从而进一步消除了与该问题相关的push和pop。