堆栈的实现

堆栈相对于我们编程语言的初学者而言是十分常见的,甚至在我们今后的学习中也是非常普遍的一种数据存储方式,因为函数的参数(形参)就是存储在堆栈中的,这么看来,堆栈的知识非常重要,那么,本人就在这篇博文中来为大家讲解一下堆栈的知识

堆栈有一点对于初学者而言很容易出错的知识点:
堆栈可以被称之为“栈”,但是不能被称作“堆”,堆栈和堆是有区别的,那么,在这里本人来讲解一下堆和堆栈的区别:
堆栈
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];
}

其他所有的操作都是在这几个函数的基础上构建的,所以我们要清楚地掌握以上的几种函数。

关于和堆栈(先入后出)处理数据方式相反的队列(先入先出)的异同点,将在本人的另一篇博文《队列的实现》中进行详解

posted @ 2020-03-04 17:44  在下右转,有何贵干  阅读(276)  评论(0编辑  收藏  举报