1.栈
1.1 栈的定义
栈是一种特殊的线性表。其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行。如下所示:
结论:后进先出(Last In First Out),简称为LIFO线性表。
栈的基本运算有六种:
构造空栈:InitStack(S)、
判栈空: StackEmpty(S)、
判栈满: StackFull(S)、
进栈: Push(S,x)、可形象地理解为压入,这时栈中会多一个元素
退栈: Pop(S) 、 可形象地理解为弹出,弹出后栈中就无此元素了。
取栈顶元素:StackTop(S),不同与弹出,只是使用栈顶元素的值,该元素仍在栈顶不会改变。
由于栈也是线性表,因此线性表的存储结构对栈也适用,通常栈有顺序栈和链栈两种存储结构,这两种存储结构的不同,则使得实现栈的基本运算的算法也有所不同。
我们要了解的是,在顺序栈中有"上溢"和"下溢"的概念。顺序栈好比一个盒子,我们在里头放了一叠书,当我们要用书的话只能从第一本开始拿(你会把盒子翻过来吗?真聪明^^),那么当我们把书本放到这个栈中超过盒子的顶部时就放不下了(叠上去的不算,哼哼),这时就是"上溢","上溢"也就是栈顶指针指出栈的外面,显然是出错了。反之,当栈中已没有书时,我们再去拿,看看没书,把盒子拎起来看看盒底,还是没有,这就是"下溢"。"下溢"本身可以表示栈为空栈,因此可以用它来作为控制转移的条件。
链栈则没有上溢的限制,它就象是一条一头固定的链子,可以在活动的一头自由地增加链环(结点)而不会溢出,链栈不需要在头部附加头结点,因为栈都是在头部进行操作的,如果加了头结点,等于要在头结点之后的结点进行操作,反而使算法更复杂,所以只要有链表的头指针就可以了。
代码:
// Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
using namespace std;
#define MAX 10 // MAXIMUM STACK CONTENT
class stack
{
private:
int arr[MAX];
int top;
public:
stack()
{
inItStack();
}
/************************************************************************/
/* 初始化栈 */
/************************************************************************/
void inItStack()
{
top=-1;
}
/************************************************************************/
/* 入栈 */
/************************************************************************/
void push(int a)
{
top++;
if(top < MAX) {
arr[top]=a;
} else {
cout<<"STACK FULL!!"<<top;
}
}
/************************************************************************/
/* 出栈 */
/************************************************************************/
int pop()
{
if(isEmpty()) {
cout<<"STACK IS EMPTY ";
return NULL;
} else {
int data=arr[top];
arr[top]=NULL;
top--;
return data;
}
}
/************************************************************************/
/* 是否为空 */
/************************************************************************/
bool isEmpty()
{
if(top == -1) return true;
else return false;
}
};
int main()
{
stack a;
a.push(3);
a.push(10);
a.push(1);
cout<<"Pop:"<<a.pop();
return 0;
}
结论:由于栈的插入和删除操作具有它的特殊性,所以用顺序存储结构表示的栈并不存在插入删除数据元素时需要移动的问题,但栈容量难以扩充的弱点仍就没有摆脱。
1.3 栈的链式存储
若是栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存储结构。人们将用链式存储结构表示的栈称作"链栈"。链栈通常用一个无头结点的单链表表示。如图所示:
栈的操作是线性表操作的特例。
简单用c实现:
// Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include "stdlib.h"
#include <iostream>
using namespace std;
//宏定义
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define STACKEMPTY -3
#define LT(a,b) ((a)<(b))
#define N = 100
typedef int Status;
typedef int ElemType;
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
typedef struct stack{
LinkList top;
} STACK;
/************************************************************************/
/* 接口:
*/
/************************************************************************/
void InitStack(STACK &S);
void Push(STACK &S,ElemType e);
void Pop(STACK &S, ElemType *e);
ElemType GetTop(STACK S,ElemType *e);
int StackEmpty(STACK S);
/************************************************************************/
/*
*/
/************************************************************************/
void InitStack(STACK &S)
{
S.top=NULL;
}
/************************************************************************/
/* 入栈
*/
/************************************************************************/
void Push(STACK &S,ElemType e)
{
LinkList p;
p = (LinkList )malloc(sizeof(LNode));
if (!p) exit(OVERFLOW);
p->data = e;
p->next = S.top;
S.top = p;
}
/************************************************************************/
/* 出栈
*/
/************************************************************************/
void Pop(STACK &S, ElemType *e)
{
LinkList p;
if(StackEmpty(S)) exit(STACKEMPTY);
*e = S.top->data;
p = S.top;
S.top = p->next;
free(p);
}
/************************************************************************/
/* 获取栈顶元素内容
*/
/************************************************************************/
ElemType GetTop(STACK S, ElemType *e)
{
if(StackEmpty(S)) exit(STACKEMPTY);
*e = S.top->data;
}
/************************************************************************/
/* 判断栈S是否空
*/
/************************************************************************/
int StackEmpty(STACK S)
{
if(S.top==NULL) return TRUE;
return FALSE;
}
void main()
{
STACK S;
InitStack( S);
Push(S, 3);
Push(S, 4);
ElemType e;
Pop(S,&e);
cout<<"Pop elem:"<<e;
}
1.4 栈的应用
1) 数制转换
2)语法词法分析
3)表达式求值等
1.5 栈的递归和实现
汉诺塔的问题:
解决:
1)如果有一个盘子,直接从X移到Z即可。
2)如果有n个盘子要从X移到Z,Y作为辅助。问题可以转化为,先将上面n-1个从X移动到Y,Z作为辅助,然后将第n个从X移动到Z,最后将剩余的n-1个从Y移动到Z,X作为辅助。
完整实现代码,包括栈的实现:
/ Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include "stdlib.h"
#include <iostream>
using namespace std;
//宏定义
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define STACKEMPTY -3
#define LT(a,b) ((a)<(b))
#define N = 100
typedef int Status;
typedef int ElemType;
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
typedef struct stack{
LinkList top;
} STACK;
/************************************************************************/
/* 接口:
*/
/************************************************************************/
void InitStack(STACK &S);
void Push(STACK &S,ElemType e);
void Pop(STACK &S, ElemType *e);
ElemType GetTop(STACK S,ElemType *e);
int StackEmpty(STACK S);
/************************************************************************/
/*
*/
/************************************************************************/
void InitStack(STACK &S)
{
S.top=NULL;
}
/************************************************************************/
/* 入栈
*/
/************************************************************************/
void Push(STACK &S,ElemType e)
{
LinkList p;
p = (LinkList )malloc(sizeof(LNode));
if (!p) exit(OVERFLOW);
p->data = e;
p->next = S.top;
S.top = p;
}
/************************************************************************/
/* 出栈
*/
/************************************************************************/
void Pop(STACK &S, ElemType *e)
{
LinkList p;
if(StackEmpty(S)) exit(STACKEMPTY);
*e = S.top->data;
p = S.top;
S.top = p->next;
free(p);
}
/************************************************************************/
/* 获取栈顶元素内容
*/
/************************************************************************/
ElemType GetTop(STACK S, ElemType *e)
{
if(StackEmpty(S)) exit(STACKEMPTY);
*e = S.top->data;
}
void printStack(STACK S){
LinkList p;
p = S.top;
printf("栈: ");
while (p) {
printf("%d ", p->data);
p = p->next;
}
}
/************************************************************************/
/* 如果有一个盘子,直接从X移到Z即可。
如果有n个盘子要从X移到Z,Y作为辅助。问题可以转化为,先将上面n-1个从X移动到Y,Z作为辅助,然后将第n个从X移动到Z,最后将剩余的n-1个从Y移动到Z,X作为辅助。
*/
/************************************************************************/
void move(STACK &Sa,STACK &Sb)
{
ElemType e;
Pop(Sa,&e);
Push(Sb, e);
}
void hanoi(int n,STACK &X,STACK &Y,STACK &Z)
{
if(n==1) return move(X, Z); //将圆盘1号直接移到z
hanoi(n-1,X,Z,Y); //将x上的1大n-1圆盘移到y,z做辅助塔
move(X, Z); //将编号为n的圆盘移z
hanoi(n-1,Y,X,Z); //将y上的1大n-1圆盘移到z,x做辅助塔
}
/************************************************************************/
/* 判断栈S是否空
*/
/************************************************************************/
int StackEmpty(STACK S)
{
if(S.top==NULL) return TRUE;
return FALSE;
}
void main()
{
STACK Sx, Sy,Sz;
InitStack( Sx);
InitStack( Sy);
InitStack( Sz);
int i, n = 10;
for (i = 10 ; i>=1 ;i--) {
Push(Sx, i);
}
printStack(Sx);
hanoi(n, Sx,Sy,Sz);
printStack(Sz);
}