基本的堆栈操作通常被称为push和pop,push就是把一个新值压入堆栈的顶部,pop就是把堆栈顶部的值移出堆栈并返回这个值,堆栈只提供对它顶部值的访问。还有另一种堆栈接口提供top操作,top操作返回顶部元素的值,但它并不把顶部元素从堆栈中移除。因此使用top操作可以反复取得栈顶元素而不需要经该值保存在一个局部变量中。因为如果堆栈为空我们不能实现pop和top操作,因此也需要一个接口判断堆栈是否为空,如果堆栈满了我们就不能是进行push操作,避免溢出,因此还需要一个接口判断堆栈是否满。
综上所述我们可以定义出堆栈操作的接口如下所示(stack.h文件):
定义完接口以后就是具体的实现了,任何一个ADT首先必须先确认的就是如何获取内存来存储值。有三种方案:静态数组、动态分配的数组和动态分配的链式结构。这三种方式各有优缺点。
静态数组:要求结构的长度固定,长度必须在编译时确定,但是这个方案最简单最不容易出错。
动态数组:可以在运行时决定数组的长度,如果需要的话可以分配一个新的更长的数组,把原来的元素复制到新数组中,然后删除原先的数组,打到改变数组元素长度的目的。但是这样做增加的复杂性。
链式结构:提供了最大程度的灵活性,每个元素都在需要时才单独分配内存空间,激活对元素的数量没有什么限制,但是链式内存消耗一定的内存来存储链接字段,并且链式结构访问一个特定元素的效率不如数组(当然在堆栈中不存在这个缺点,因为堆栈始终访问的都是栈顶元素)。
下面是堆栈的静态数组实现方式:
接下来是动态数组的实现方式:
动态数组实际和静态数组的算法差不多只是多了一个创建堆栈和销毁堆栈的接口(在静态数组中这个任务是有系统完成的所以不存在这两个接口),多出了接口如下所示(stack.h文件):
具体的实现如下:
接下来是链式结构的实现: