堆栈相对于我们编程语言的初学者而言是十分常见的,甚至在我们今后的学习中也是非常普遍的一种数据存储方式,因为函数的参数(形参)就是存储在堆栈中的,这么看来,堆栈的知识非常重要,那么,本人就在这篇博文中来为大家讲解一下堆栈的知识
堆栈有一点对于初学者而言很容易出错的知识点:
堆栈可以被称之为“栈”,但是不能被称作“堆”,堆栈和堆是有区别的,那么,在这里本人来讲解一下堆和堆栈的区别:
堆栈:
1.堆栈又名栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底;
2.堆栈就是一个桶,遵循先进后出的原则。
堆:
1.堆通常是一个可以被看做一棵“树”的数组对象。堆总是满足下列性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
根节点最大的堆被称为最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
2.堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别(即malloc()函数和calloc()函数所运用的,就是堆空间)。
总而言之,像我们初学者,所运用最多的还是形参存储在堆栈(栈)中,并且数据都是先入后出的,而malloc()函数和calloc()函数,则是在堆中申请空间。
那么,我们来用C语言方式实现堆栈的存储方式:
首先则是头文件"mec.h":
#ifndef _MEC_H_
#define _MEC_H_
typedef unsigned char boolean;
#define TRUE 1
#define FALSE 0
#endif
那么,接下来是定义一个存储数据的结构体:
typedef struct MEC_STACK {
void **stack; //这个成员用于存储数据
int capacity; //这个成员用于存储数据的容量,便于初始化
int top; //这个成员用于记录栈顶指针的位置
}MEC_STACK;
结构体构建好了,接下来我们要做的是初始化堆栈:
至于返回值,因为初始化可能会失败,所一返回值是boolean类型,而参数就是堆栈(结构体数组)的指针和容量
boolean initStack(MEC_STACK **stack, int capacity) {
MEC_STACK *res;
if (NULL == stack || NULL != *stack || capacity <= 0) {
return FALSE;
}
res = (MEC_STACK *) calloc(sizeof(MEC_STACK), 1);
res->stack = (void **) calloc(sizeof(void *), capacity);
res->capacity = capacity;
res->top = 0;
*stack = res;
return TRUE;
}
和以前一样,初始化完成后一定要记得编写销毁函数:
void destoryStack(MEC_STACK **stack) {
if (NULL == stack || NULL == *stack) {
return;
}
free((*stack)->stack);
free(*stack);
*stack = NULL;
}
因为数据存储主要是录入和取出操作,所以,我们要先编写判空,判满函数:
判空函数:
boolean isStackEmpty(const MEC_STACK *stack) {
return stack != NULL && stack->top <= 0;
}
判满函数:
boolean isStackFull(const MEC_STACK *stack) {
return stack != NULL && stack->top >= stack->capacity;
}
那么,接下来就是录入函数:
boolean push(MEC_STACK *stack, void *data) {
if (NULL == stack || isStackFull(stack)) {
return FALSE;
}
stack->stack[stack->top++] = data;
return TRUE;
}
还有取出函数:
void *pop(MEC_STACK *stack) {
if (NULL == stack || isStackEmpty(stack)) {
return NULL;
}
return stack->stack[--stack->top];
}
我们如果想对于堆栈内的数据进行操作,和队列不同的是栈顶指针会根据我们录入数据和取出数据而移动,而我们的操作都要用到栈顶指针,所以,我们在来编写一个读取栈顶指针的函数:
void *readTop(const MEC_STACK *stack) {
if (NULL == stack || isStackEmpty(stack)) {
return NULL;
}
return stack->stack[stack->top - 1];
}
其他所有的操作都是在这几个函数的基础上构建的,所以我们要清楚地掌握以上的几种函数。
关于和堆栈(先入后出)处理数据方式相反的队列(先入先出)的异同点,将在本人的另一篇博文《队列的实现》中进行详解