堆栈是一种后进先出(LIFO)的数据结构。
1、堆栈接口
基本的堆栈操作通常是进栈push和出栈pop。push是把一个新值压入到堆栈的顶部,pop就是把堆栈顶部的值移出堆栈并返回这个值。另外一种堆栈接口提供三个基本操作:push、pop和top。push操作和前面描述的一样,pop只是把顶部元素从堆栈中移除,并不返回值,而top操作返回栈顶的元素,但不将其移除。第一种pop操作具有移除元素的副作用,而top操作没有副作用,第二种堆栈接口的功能分得更细,每种功能都不带副作用,所以第二种接口定义要更好一些。
堆栈的实现方式可以有三种:静态数组、动态数组和链表。如下的定义接口的头文件可用于所有的三种实现方式。
堆栈接口头文件stack.h:
/*
** 一个堆栈模块的接口
*/
#define STACK_TYPE int /*堆栈所存储的值的类型*/
/*
** push
** 压入堆栈
*/
void push(STACK_TYPE value);
/*
** pop
** 弹出堆栈
*/
void pop(void);
/*
** top
** 返回堆栈顶部元素
*/
STACK_TYPE top(void);
/*
** is_empty
** 判断堆栈是否为空
*/
int is_empty(void);
/*
** is_full
** 判断堆栈是否已满
*/
int is_full(void);
/*
** create_stack
** 创建堆栈,参数指定堆栈长度
** 这个函数不用于静态数组版本的堆栈
*/
void create_stack(size_t size);
/*
** destroy_stack
** 销毁堆栈,释放所使用的内存
** 这个函数不用于静态数组版本的堆栈
*/
void destroy_stack(void);
在这个接口中,用户可以根据自己的需要修改堆栈中值的类型STACK_TYPE的定义。
2、静态数组实现堆栈
/*
** 用一个静态数组实现堆栈
*/
#include "stack.h"
#include <assert.h>
#define STACK_SIZE 100 /*堆栈长度*/
/*
** 存储堆栈中数据的数组和指向栈顶的指针
*/
static STACK_TYPE stack[STACK_SIZE];
static int top_element = -1;
/*
** push
*/
void push(STACK_TYPE value)
{
assert(!is_full());
top_element += 1;
stack[top_element] = value;
}
/*
** pop
*/
void pop(void)
{
assert(!is_empty());
top_element -= 1;
}
/*
** top
*/
STACK_TYPE top(void)
{
assert(!is_empty());
return stack[top_element];
}
/*
** is_empty
*/
int is_empty(void)
{
return top_element == -1;
}
/*
** is_full
*/
int is_full(void)
{
return top_element == STACK_SIZE - 1;
}
在这个实现中,所有不属于外部接口的内容都被声明为static,这是为了防止用户使用接口之外的其他方式改变堆栈中的值。
静态数组实现方式中,堆栈的长度是在编译之前就确定好的。
3、动态数组实现堆栈
/*
** 动态数组实现堆栈
** 堆栈的长度在创建堆栈的函数被调用时给出
*/
#include "stack.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>
#include <stddef.h>
/*
** 用于存储堆栈元素的数组和栈顶指针
*/
static STACK_TYPE *stack;
static size_t stack_size;
static int top_element = -1;
/*
** create_stack
*/
void create_stack(size_t size)
{
assert(stack_size == 0);
stack_size = size;
stack = (STACK_TYPE *)malloc(stack_size * sizeof(STACK_TYPE));
assert(stack != NULL);
}
/*
** destroy_stack
*/
void destroy_stack(void)
{
assert(stack_size > 0);
stack_size = 0;
free(stack);
stack = NULL;
}
/*
** push
*/
void push(STACK_TYPE value)
{
assert(!is_full());
top_element += 1;
stack[top_element] = value;
}
/*
** pop
*/
void pop(void)
{
assert(!is_empty());
top_element -= 1;
}
/*
** top
*/
STACK_TYPE top(void)
{
assert(!is_empty());
return stack[top_element];
}
/*
** is_empty
*/
int is_empty(void)
{
assert(stack_size > 0);
return top_element == -1;
}
/*
** is_full
*/
int is_full(void)
{
assert(stack_size > 0);
return top_element == stack_size - 1;
}
在进行堆栈操作之前必须先调用void create_stack(size_t size)函数创建堆栈,同时指明堆栈长度。堆栈操作结束后需要调用void destroy_stack(void)函数释放内存。
4、链表实现堆栈
/*
** 用单相链表实现堆栈,这个堆栈没有长度限制
*/
#include "stack.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>
#define FALSE 0
/*
** 定义一个结构存储堆栈元素
*/
typedef struct STACK_NODE{
STACK_TYPE value;
struct STACK_NODE *next;
}StackNode;
/*
** 指向堆栈中第一个节点的指针
*/
static StackNode *stack;
/*
** create_stack
*/
void create_stack(size_t size)
{
}
/*
** destroy_stack
*/
void destroy_stack(void)
{
while(!is_empty())
pop();
}
/*
** push
*/
void push(STACK_TYPE value)
{
StackNode *new_node;
new_node = malloc(sizeof(StackNode));
assert(new_node != NULL);
new_node->value = value;
new_node->next = stack;
stack = new_node;
}
/*
** pop
*/
void pop(void)
{
StackNode *first_node;
first_node = stack;
stack = first_node->next;
free(first_node);
}
/*
** top
*/
STACK_TYPE top(void)
{
assert(!is_empty());
return stack->value;
}
/*
** is_empty
*/
int is_empty(void)
{
return stack == NULL;
}
/*
** is_full
*/
int is_full(void)
{
return FALSE;
}
create_stack是个空函数,因为不需要事先申请一定的内存,内存是随时要随时生成。链表实现的堆栈没有长度限制,所以int is_full(void)函数永远返回FALSE。
destroy_stack函数连续从堆栈中弹出元素,直到堆栈为空。这里的弹出元素与上面数组方式不一样,这里是真正的删除元素,连着该元素的内存一起释放。
参考文献
[1] Kenneth A. Reek. C和指针(第二版)[M]. 人民邮电出版社.