上篇博文:【 C 】经典抽象数据类型(ADT)之堆栈(用静态数组实现堆栈)讲了堆栈的基础知识以及如何用静态数组实现堆栈。
这篇博文简单记录下用动态数组实现堆栈!
整体的实现过程和用静态数组实现堆栈相似,但是也有不同之处。
首先就是我们需要在接口中定义两个新函数:
//create_stack
//创建堆栈,参数指定堆栈可以保存多少元素
//注意:这个函数并不用于静态数组版本的堆栈
void create_stack( size_t size );
//destroy_stack
//销毁堆栈。它释放堆栈所使用的内存。
//注意:这个函数也不用于静态数组版本的堆栈
void destroy_stack( void );
第1个函数用于创建堆栈,用户像它传递一个参数,用于指定数组的长度。第2个函数用于删除堆栈,为了避免内存泄露,这个函数是必须的。
这些声明可以添加到stack.h中,尽管使用静态数组实现堆栈时,并没有这两个函数。注意,用户在使用静态数组类型的堆栈时并不存在错误地调用这两个函数的危险,因为它们在那个模块中并不存在。
//一个用动态分配数组实现的堆栈
//堆栈的长度在创建堆栈的函数被调用时给出,该函数必须在任何其他操作堆栈的函数被调用之前调用
#include < stdio.h >
#include < stdlib.h >
#include < malloc.h >
#include < assert.h >
#include " stack.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 == 0 ); //stack是指向内存空间的指针,一开始为零也就是空指针
stack_size = size;
stack = malloc( stack_size * sizeof( STACK_TYPE ) );
assert( stack != NULL ); //动态内存分配成功继续,否则退出
}
//destroy_stack
void destroy_stack( void )
{
assert( stack_size > 0 ); //在释放内存之前确保内存已经分配成功了,也就是堆栈的大小大于0
stack_size = 0; //堆栈大小重置为0
free( stack ); //释放内存
}
//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; //堆栈为空,则返回TRUE,否则返回FALSE
}
//is_full
int is_full( void )
{
assert( stack_size > 0 );
return top_element == stack_size - 1; //堆栈已满,返回TRUE,否则返回FALSE
}
上述代码为堆栈的实现,且是用动态数组实现的堆栈。
简单的解释下上述代码:
使用动态分配数组在实现上与静态数组实现堆栈改动得不多,仅仅多了两个函数,一个是创建堆栈,一个是销毁堆栈。数组由一个指针代替,该指针指向分配的内存的首地址。程序引用stack_size变量保持堆栈的长度。它们在缺省情况下都初始化为零。
create_stack函数首先检查堆栈是否已经创建。然后分配所需数量的内存并检查分配是否成功。
destroy_stack函数在释放内存之前把长度和指针变量重新设置为零,这样它们可以用于创建另一个堆栈。
模块剩余部分唯一改变的是is_full函数的返回值语句中 top_element 与 stack_size -1 进行比较,而不是与#define定义的 STACK_SIZE进行比较(静态数组中的),并且在 is_full和is_empty函数中都增加了一条assert宏。assert可以防止任何堆栈函数在堆栈被创建前就被调用。其余的堆栈函数并不需要这条断言,因为它们都调用了这两个函数之一。