以数制转换问题讲解栈数据结构的基本概念及其在计算机中的应用
一、栈的概念介绍
在我们的生活中,总有这么一些例子,①食堂在堆放餐盘的时候,总是从下往上,在取餐盘的时候,又是从上往下;②最先放入厢式货车的货物,最后才能取出;③普通手枪的子弹夹,先装进弹夹的子弹,最后才会被打出来。类似于这样的场景还有很多,这样的存取顺序,我们称之为先进后出(LIFO)。这种存取方式在解决某些计算机问题的时候非常高效,因此,在计算机科学中抽象出了一种数据结构,专门用于解决后进先出这类科学问题,在计算机中,这种后进先出的数据结构,我们称之为 栈。栈这种数据结构被抽想出来以后,就被广泛的应用于计算机软硬件系统中,在编译系统、操作系统等系统软件和各类应用软件中经常使用栈来高效的完成特定的算法设计。在许多程序语言设计中,函数的调用,就会使用栈来保存形参以及函数的返回值。在硬件层面,有专门的栈寄存器来配合栈数据结构的高效访问。(更多内容,可参阅程序员在旅途)
栈的逻辑结构是一种线性结构,和线性表相同,元素之间是一对一的关系,由于需要保证元素的后进先出规则,因此需要对线性表的运算操作进行一定的限制,即只允许在线性表的一端进行插入和删除来满足栈的要求。因此可以认为栈是限制在表的一端进行插入和删除的线性表。在线性表中允许插入、删除的这一端称为栈顶,栈顶的位置是动态变化的;不允许操作的这一端称为栈底,栈底是固定不变的。当表中没有元素时称为空栈。
对于栈而言,常用的操作有:①栈的初始化; ②判断栈是否为空;③入栈;④出栈;⑤取栈顶元素;⑥销毁栈。这些基本的操作是组成复杂程序的基础。栈的示意图如下:
栈的物理实现有两种方式,分别为顺序存储方式 和 链式存储方式。采用顺序存储的栈我们称之为顺序栈,采用链式存储结构的栈称之为链式栈。
二、顺序栈的操作实现
和顺序表的定义实现一样,顺序栈的实现,要分配一块连续的存储空间存放栈中的内容,可以用一维数组来实现,同时定义一个top变量指明当前栈顶的位置。类型描述如下:
#define MAXSIZE 50
typedef struct{
ElementType data[MAXSIZE];
int top;
}SeqStack,*PSeqStack;
定义一个指向栈的指针:
PSeqStack p = (PSeqStack)malloc(sizeof(SeqStack));
由于顺序栈的数据域是静态分配的存储空间,而栈的操作又是一个动态的过程,因此,在入栈的时候,可能出现栈中元素的个数超过栈的最大空间大小,这时候就产生栈的溢出现象,这称之为上溢。在出栈的时候,可能全部元素都出去了,再也没有元素可以出栈了,这也是栈的溢出现象,称之为下溢。在栈的操作实现过程中,要注意溢出的检测。
2.1初始化顺序栈
顺序栈的初始化,就是构造一个空栈,然后返回一个指向顺序表的指针。做法是使用malloc()分配栈这种结构体的存储空间,然后,将栈中top置为-1,标识空栈。代码如下:
PSeqStack init_seqstack(){
//给栈分配内存空间
PSeqStack p = (PSeqStack)malloc(sizeof(SeqStack));
if(p){
p->top = -1; //top=-1代表空栈;
}
return p;
}
2.2判断栈是否为空
判断栈中是否有元素,只要判断top是否等于-1即可。(要注意判断p是为非NULL)
int empty_seqstack(PSeqStack p){
if(p){
if(p->top == -1){
return 1; //1代表空栈
}else{
return 0; //0代