算法、数据结构、与设计模式等在游戏开发中的运用 (二):栈(Stack)
作者:Compasslg 李涵威
(如果你已经了解什么是栈并且如何实现,可以直接跳到第3部分)
1. 什么是栈(Stack)
通常来说,我们认为栈(Stack)是一种抽象的数据类型(Abstract Data Type),或者说抽象的数据结构(Abstract Data Structure)。之所以说是抽象,我个人的见解是因为这种数据结构并非根据他的内部组成或者实现方式定义的,而是根据其调用方式。举一个简单的例子,链表(LinkedList)是一种由一个一个不同的节点(Node)通过指针连接而成的线性数据结构。他的使用方式和一般的ArrayList没有什么区别,但是由于他们的实现方式不同,导致我们会在不同的情况下使用它们,而他们也被视为不同的数据结构。因此,这些数据结构都是由他们的实现方式定义的。
相比上述提到的数据结构,Stack这个概念本身不会限制开发者如何实现它,而只是抽象的告诉使用者,这个结构会将数据一层一层的“堆叠”起来。你可以通过如图1所示的方法在将新的元素加入(Push)到栈的 顶端(Top),或者获取当前顶端的元素。这样每次从中获得或者移除的数据就是你最后一次加入的数据,这也就是 先进后出(First In, Last Out) 的概念。至于具体如何实现,开发者可以自行决定。很多人甚至会直接把其他的线性数据结构例如数组和链表当作Stack来使用——事实上,Stack通常也只是这两种数据结构之一的外面包一层外壳而已。
图 1:Stack中元素的加入,查看,和移除
相对于 “栈” 这个名词,我个人更喜欢港台地区对Stack的翻译 —— “堆叠”。在我学习编程之初,“堆栈”的用法对我造成了很大的困扰。因为Stack这个词本身的意思就和“堆”差不多,我在用英文学习的时候也是把它视作一种 “将东西堆起来” 的数据结构,因此我常常先入为主的把中文的“堆”(Heap)和“栈”(Stack)的含义弄反。直到很长一段时间以后才从一些中文的资料中发现我之前闹的乌龙……
在内存中,堆和栈代表着不同的意思。由于这篇文章主要讨论的是Stack作为一种抽象数据结构在游戏开发中的运用,在这里我不会讨论他们的其他含义。在之后博客中我也会谈到堆(Heap)作为一种数据结构在游戏开发中的应用,此处就不多提了。
2. 如何实现和使用 Stack (Java)
在这一部分,我将用利用Java中的数组(Array)来实现Stack。之所以使用数组而不是前面提到的链表,是因为这样可以尽量保证Stack使用的是连续的内存空间。虽说在游戏开发中用到Stack的大多数情况下这点优化都几乎可以忽略不计,但养成一个好的开发习惯总归是没错的,更何况你永远不知道什么时候这些小问题会在将来造成更大的影响。
以下便是一个利用java中的数组实现的简单的栈。要注意的是,因为使用的是最普通的数组,我们难免会要面对Stack的容量限制问题。这一点上,我们可以选择接受Stack有容量限制的事实,在超出容量时报错;也可以选择类似java中ArrayList的处理方式,在Stack满了以后扩大容量 (建一个新的数组,两倍于原来的大小,将原数组中的所有元素转移到新数组)。下面的代码选择的是第一种处理方式。
public class Stack<T>{
private T arr[];
private int top;
// 构造器 1: 使用参数容量
public Stack(int size){
arr = new T[size]