1、栈
1.1、什么是栈
栈的结构类似于枪的弹夹,第一颗放入弹夹的子弹却是最后一颗被打出来,最后一颗放入弹夹的子弹却是第一颗被打出来,称为先进后出的数据结构。
现实中,栈的应用非常广泛和普遍,例如,使用浏览器上网时,不管什么浏览器都会有 个"后退"键,点击后可以按访问顺序的逆序,加载浏览过的网页;如office、photoshop等文档或图像的编辑软件中,都有撤销(undo)的操作,也是使用栈来实现的。
栈的定义:栈(stack)是限定仅能在表尾进行插入和删除的线性结构。
我们通常把可以进行插入和删除的一端称为栈顶,就像弹夹可以放入子弹的一端是位于弹夹顶部的,另一端则称为栈底,不含任何数据元素的栈称为空栈。
首先它是一个线性表 ,也就是说,栈元素具有线性关系,即前驱后继关系。只不过栈是一种特殊的线性表而已。栈的特殊之处就在于限制了这个线性表的插入和删除位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。
栈的插入操作,叫作进栈,也叫压栈、入栈。
栈的删除操作,叫作出栈,也叫弹栈。
这个最先进栈的元素,是不是一定就只能最后出栈呢?
答案是不一定,栈只是对线性表的插入和删除的位置进行了限制 ,并没有对元素进出的时间进行限制,也就是说,在所有元素没有完全进栈时,先进栈的元素也可以先出栈,只要保证是栈顶元素出栈即可。就像我们有五颗子弹来打枪,先放入三颗子弹,打了两枪,再放入一颗子弹,又打了两枪,再放入一颗子弹,最后把这课子弹打出去,此时最后入栈的子弹最后打出去。
1.2、栈的基本功能
InitStack:初始化操作,建立一个空栈。
DestroyStack:若栈存在,则销毁栈。
ClearStack:清空栈元素。
StackEmpty:判断栈是否为空栈。
GetTop:若栈存在且非空,返回栈顶元素。
Push:入栈,将新元素放置栈顶。
Pop :弹栈,返回栈顶元素并删除该栈顶元素。
StackLength:返回栈的元素个数。
2、栈的数组结构以及实现
栈的一切操作取决于栈顶指针,实现栈的功能更多的是对栈顶指针的操作。
2.1、栈的数组结构
const int MAXSIZE=100;
typedef int DataType; //栈的数据元素类型
typedef struct ArrayStack {
DataType data[MAXSIZE]; //栈的数组
int top; //栈顶指针
}*Stack; //不需要加指针*的别名
2.2、入栈操作
int Push(Stack s, DataType element)
{
if (s->top >= MAXSIZE-1) return 0; //如果栈满则返回错误
s->top++; //栈顶指针向上移到空位
s->data[s->top] = element; //将新元素赋给栈顶
return 1;
}
2.3、出栈操作
DataType Pop(Stack s)
{
if (s->top <= -1) return 0; //如果栈空则返回错误
DataType element = 0; //初始化一个返回值
element = s->data[s->top]; //将栈顶元素赋给返回值
s->top--; //栈顶指针向下移
return element;
}
两个最重要功能都没有涉及任何循环, 因此时间复杂度都为O(1)。
实现栈的数组结构还是非常简单的,因为它只准栈顶进出元素,所以不存在线性表插入和删除时需要移动大量元素的问题。不过栈的数组结构还是有个很大的缺陷,就是必须事先确定数组存储空间大小,万一不够用了,就需要编程手段来扩展数组的容量,非常麻烦。
3、栈的链表结构以及实现
栈的链表结构简称链栈,栈只是用栈顶来进行操作,单链表有头指针而栈顶指针也是必须,所以可以让他们合二为一:头结点的头指针为栈顶指针,头结点的数据用来记录栈元素个数。
对于链栈来说,基本不存在满栈的情况,所以无需考虑。
3.1、栈的链表结构
typedef int DataType; //栈的数据元素类型
typedef struct StackNode {//链栈节点结构
DataType data; //栈的数据
StackNode *next; //后继指针
}*LinkStackNode;//不需要加指针*的别名
typedef struct StackTop {//链栈头节点结构
int length; //栈元素个数
LinkStackNode top; //栈顶指针
}*LinkStackTop;//不需要加指针*的别名
3.2、入栈操作
int Push(LinkStackTop s, DataType element)
{
LinkStackNode node = new StackNode;
node->data = element; //新元素赋值给新节点
node->next = s->top; //新节点的后继节点为当前栈顶节点
s->top = node; //新节点成为栈顶节点
s->length++; //栈元素个数加一
return 1;
}
3.3、出栈操作
DataType Pop(LinkStackTop s)
{
if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误
DataType element = 0; //初始化一个返回值
LinkStackNode node = s->top; //得到栈顶节点
element = node->data; //将栈顶元素赋给返回值
s->top = node->next; //删除栈顶节点
s->length--; //栈元素个数减一
free(node);
return element;
}
两个最重要功能在链表结构中的时间复杂度也都为O(1),只是清空栈操作稍微麻烦一下。
4、对比两种结构的栈
对比数组栈和链栈,它们实现的出栈、入栈功能的时间复杂度是一样的,均为O(1)。时间性能一样,那么来比较空间性能,数组栈需要事先确定一个固定的长度,可能会存在内存空间浪费或不足的问题,优势是存取时定位方便,实现简单;而链栈因为每个元素都有指针域,增加了一定的空间开销,优势是栈的长度无限制,使用灵活。如果在使用栈的过程中,栈的元素个数变化在一定范围,或者一定要固定栈的长度,那么推荐使用数组栈,否则最好是链栈。
附录一: 栈的数组结构实现全部基本功能
const int MAXSIZE=100;
typedef int DataType; //栈的数据元素类型
typedef struct ArrayStack {
DataType data[MAXSIZE]; //栈的数组
int top; //栈顶指针
}*Stack; //不需要加指针*的别名
Stack InitStack()
{
Stack s = new ArrayStack;
s->top = -1;
return s;
}
int StackEmpty(Stack s)
{
if (s->top <= -1) return 0;
else return 1;
}
int Push(Stack s, DataType element)
{
if (s->top >= MAXSIZE-1) return 0; //如果栈满则返回错误
s->top++; //栈顶指针向上移到空位
s->data[s->top] = element; //将新元素赋给栈顶
return 1;
}
DataType Pop(Stack s)
{
if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误
DataType element = 0; //初始化一个返回值
element = s->data[s->top]; //将栈顶元素赋给返回值
s->top--; //栈顶指针向下移
return element;
}
DataType GetTop(Stack s)
{
if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误
DataType element = 0; //初始化一个返回值
element = s->data[s->top]; //将栈顶元素赋给返回值
//不需要删除栈顶元素,栈顶指针不用下移
return element;
}
void ClearStack(Stack s)
{
s->top=-1;
}
int StackLength(Stack s)
{
return s->top+1;
}
void DestroyStack(Stack s)
{
free(s);
}
附录二: 栈的链表结构实现全部基本功能
typedef int DataType; //栈的数据元素类型
typedef struct StackNode {
DataType data; //栈的数据
StackNode *next; //后继指针
}*LinkStackNode;//不需要加指针*的别名
typedef struct StackTop {
int length; //栈元素个数
LinkStackNode top; //栈顶指针
}*LinkStackTop;//不需要加指针*的别名
LinkStackTop InitStack()
{
LinkStackTop s = new StackTop;
s->length = 0;
s->top = NULL;
return s;
}
int StackEmpty(LinkStackTop s)
{
if (s->top == NULL) return 0;
else return 1;
}
int Push(LinkStackTop s, DataType element)
{
LinkStackNode node = new StackNode;
node->data = element; //新元素赋值给新节点
node->next = s->top; //新节点的后继节点为当前栈顶节点
s->top = node; //新节点成为栈顶节点
s->length++; //栈元素个数加一
return 1;
}
DataType Pop(LinkStackTop s)
{
if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误
DataType element = 0; //初始化一个返回值
LinkStackNode node = s->top; //得到栈顶节点
element = node->data; //将栈顶元素赋给返回值
s->top = node->next; //删除栈顶节点
s->length--; //栈元素个数减一
free(node);
return element;
}
DataType GetTop(LinkStackTop s)
{
if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误
DataType element = 0; //初始化一个返回值
LinkStackNode node = s->top; //得到栈顶节点
element = node->data; //将栈顶元素赋给返回值
return element;
}
void ClearStack(LinkStackTop s)
{
while (s->top != NULL)
{
LinkStackNode node = new StackNode;
node = s->top;
s->top=node->next;
free(node);
}
s->top = NULL;
s->length = 0;
}
int StackLength(LinkStackTop s)
{
return s->length;
}
void DestroyStack(LinkStackTop s)
{
if (s->top != NULL) ClearStack(s);
free(s);
}