数据结构——栈
1、栈的定义
栈是一种特殊的线性表。
其特殊性在于限定插入和删除数据元素的操作仅仅能在线性表的一端进行。
该位置是表的末端。叫做栈顶(top
)
图像模拟
左边的栈的示意图 右边为用铁路调度站表示栈
2、栈的基本运算
构造空栈:InitStack(S)
判栈空:
StackEmpty(S)
判栈满:
StackFull(S)
进栈:
Push(S,x)
可形象地理解为压入,这时栈中会多一个元素
退栈:
Pop(S)
可形象地理解为弹出,弹出后栈中就无此元素了。
取栈顶元素:StackTop(S)
不同与弹出,仅仅是使用栈顶元素的值,该元素仍在栈顶不会改变
3、栈的存储结构
1)顺序存储
顺序存储也叫数组存储。
潜在问题:
须要提前预计一个数组的大小。事实上在实际应用中,这个不难做到;对错误的检測严重影响运行的效率问题(在顺序栈中有"上溢"和"下溢"的概念。
顺序栈好比一个盒子。我们在里头放了一叠书。当我们要用书的话仅仅能从第一本開始拿(你会把盒子翻过来吗?真聪明^^),那么当我们把书本放到这个栈中超过盒子的顶部时就放不下了(叠上去的不算,哼哼),这时就是"上溢","上溢"也就是栈顶指针指出栈的外面。显然是出错了。
反之。当栈中已没有书时,我们再去拿,看看没书,把盒子拎起来看看盒底,还是没有,这就是"下溢"。"下溢"本身能够表示栈为空栈,因此能够用它来作为控制转移的条件。)
简单应用:平衡符号
算法思想:做一个空栈。读入字符直到文件结尾,假设字符是一个开放符号。将其入栈。假设是一个封闭符号,则当栈空时报错,否则,将元素弹出,假设弹出的符号不是其相应的开放符号。则报错。
到了文件尾,假设栈非空则报错。
#include <iostream>
#include <string>
const int maxn = 100;
using namespace std;
struct *Stack
{
int Capacity;//栈的容量
int Top_of_stack;//栈的下标
char *Array; //存放栈中元素的数组
}; //栈的数组实现
typedef struct Mystack *Stack;
Stack CreateStack(int Max);//创建一个栈
void DisposeStack(Stack S);//释放栈
int IsEmpty(Stack S);//推断一个栈是否是空栈
int IsFull(Stack S);//推断一个栈是否是满栈
void Push(int x, Stack S);//入栈
void Pop(Stack S);//出栈
char Top(Stack S);//获取栈顶元素
Stack CreateStack(int Max)
{
Stack S;
S = (Stack)malloc(sizeof(struct Mystack));
if(S == NULL)
cout << "Create stack error!" << endl;
S->Array = (char *)malloc(sizeof(char) * Max);
if(S->Array == NULL)
cout << "Create stack error!" << endl;
S->Capacity = Max;
S->Top_of_stack = 0;
return S;
}
//释放栈
void DisposeStack(Stack S)
{
if(S != NULL)
{
free(S -> Array);
free(S);
}
}
//推断一个栈是否是空栈
int IsEmpty(Stack S)
{
return !S->Top_of_stack;
}
//推断一个栈是否为满栈
int IsFull(Stack S)
{
if(S->Top_of_stack == S->Capacity - 1)
return 1;
else
return 0;
}
//数据入栈
void Push(int x, Stack S)
{
if(IsFull(S))
cout << "The Stack is full!" << endl;
else
S->Array[S->Top_of_stack++] = x;
}
//数据出栈
void Pop(Stack S)
{
if(IsEmpty(S))
cout << "The Stack is empty!" << endl;
else
S->Top_of_stack--;
}
//将栈顶返回
char Top(Stack S)
{
if(!IsEmpty(S))
return S->Array[S->Top_of_stack - 1];
cout << "The Stack is empty!" << endl;
return 0;
}
int main()
{
char str[maxn];
cin >> str;
int len = strlen(str);
//依据序列的长度来创建栈
struct Mystack *my_stack = CreateStack(len + 1);
for(int i = 0;i < len; i ++)
{
//假设字符时开放符号,则将其推入栈中
if(str[i] == '{' || str[i] == '[' || str[i] == '(')
Push(str[i],my_stack);
//假设字符时一个封闭符号,则当栈空时报错,否则将栈弹出来
if(str[i] == '}')
{
if(Top(my_stack) == '{')
Pop(my_stack);
else
break;
}
else if(str[i] == ']')
{
if(Top(my_stack) == '[')
Pop(my_stack);
else
break;
}
else if(str[i] == ')')
{
if(Top(my_stack) == '(')
Pop(my_stack);
else
break;
}
}
//假设最后占空则序列是合法的,否则是不平衡的
if(IsEmpty(my_stack))
cout << "The symbol that you input is balance!" << endl;
else
cout << "The symbol that you inut is imbalance!" << endl;
DisposeStack(my_stack);
return 0;
}
2)链式存储
栈的操作诗线性表操作的特例:
若是栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存储结构。人们将用链式存储结构表示的栈称作"链栈"。链栈通经常使用一个无头结点的单链表表示。
简单应用:平衡符号
#include <stdio.h>#include <stdlib.h>#define Error(Str) fprintf(stderr,"%s\n",Str),exit(1)
struct Node{
char elem;
struct Node *next;
};//栈的链表实现
typedef struct Node *Stack;
int CheckSymbol(Stack S);//检測平衡符号的函数
Stack CreateStack(void);/*创建一个空栈*/void MakeEmpty(Stack);
int IsEmpty(Stack);//測试栈是否是空栈void Push(char ,Stack);//入栈void Pop(Stack);//出栈char Top(Stack);//获取栈顶元素void DisposeStack(Stack);//销毁栈
int main()
{
Stack S;
S=CreateStack();
if(CheckSymbol(S))
printf("wrong\n");
else
printf("right\n");
DisposeStack(S);
return 0;
}
int CheckSymbol(Stack S)
{
char ch;
printf("input characters as {,} or (,)or [,] \n");
printf("and # to quit\n");
while((ch=getchar())!='#')//输入平衡字符
{
if(ch=='{'||ch=='['||ch=='(') /*开放符号*/
Push(ch,S);
else if(ch=='}'||ch==']'||ch==')') /*封闭符号*/
{
if(IsEmpty(S)) /*栈里无字符*/
Error("stack is empty.");
else
{
switch(ch)
{
case '}':
if(Top(S)!='{')//不匹配
Error("not match");
else
break;//匹配成功
case ')':if(Top(S)!='(')
Error("not match");
else
break;
case ']':if(Top(S)!='[')
Error("not match");
else break;
}
/*匹配成功,将栈中匹配成功的符号弹出*/
Pop(S);
}
}
}
if(!IsEmpty(S))//假设最后栈里还有字符,则说明未匹配完,即出错
Error("the stack is not empty last");
else
return 0;//成功
}
/*栈的基本操作——链表实现*/
Stack CreateStack(void)
{
Stack S;
S=malloc(sizeof(struct Node));
if(S==NULL)
Error("out of space");
S->next=NULL;
MakeEmpty(S);
return S;
}
void MakeEmpty(Stack S)//设置Next指针指向NULL
{
if(S==NULL) //未创建栈
Error("must usr CreateStack first");
else
while(!IsEmpty)
Pos(S);
}
int IsEmpty(Stack S)
{
return S->next==NULL;
}
void Push(char ch,Stack S)//向前链表前端进行插入实现
{
Stack tmp;
tmp=malloc(sizeof(struct Node));
if(!tmp)
Error("out of space");
tmp->elem=ch;
tmp->next=S->next;
S->next=tmp;
}
void Pop(Stack S)//通过删除表的前端元素而实现
{
Stack tmp;
if(IsEmpty(S))
Error("empty stack");
else
{
tmp=S->next;
S->next=tmp->next;
free(tmp);
}
}
char Top(Stack S)//通过考查表的第一个位置上元素而完毕的
{
if(!IsEmpty(S))
return S->next->elem;
Error("empty stack.");
return 0;
}
void DisposeStack(Stack S)
{
if(S==NULL)
Error("no stack");
MakeEmpty(S);
free(S);
}
4、C++ STL—stack
1)stack 模板类的定义在<stack>头文件里。
2)stack 模板类须要两个模板參数。一个是元素类型。一个容器类型,但仅仅有元素类型是必要的,在不指定容器类型时,默认的容器类型为deque。
3)定义stack 对象的演示样例代码例如以下:stack<int> s1; stack<string> s2;
4)stack 的基本操作有:
入栈,如例:s.push(x);
出栈,如例:s.pop();
注意,出栈操作仅仅是删除栈顶元素,并不返回该元素。
訪问栈顶,如例:s.top()
推断栈空。如例:s.empty()
,当栈空时,返回true。
訪问栈中的元素个数,如例:s.size()
。
简单应用:中缀到后缀的转换
算法思路:
①遇到数字:直接输出
②遇到'(':压栈
③遇到')':持续出栈,假设出栈的符号不是'('则输出。否则终止出栈。
④遇到符号:推断该符号与栈顶符号的运算优先级,假设栈顶符号的运算优先级高。则出栈并输出,直到优先级相等或栈为空;假设栈顶符号的运算优先级低于或等于当前符号的运算优先级,则将当前符号压栈。
⑤处理完字符串后将栈中剩余的符号所有输出。
例子:中缀表达式:6*[5+(2+3)*8+3]
后缀表达式:6523+8*+3+*
#include<iostream>#include<string>#include<sstream> //基于字符串的流#include<stack> //STL堆栈容器using namespace std;
int main()
{
//输入字符串
string in;
cin >> in;
stack<char> stack;
for(size_t i = 0; i != in.size(); ++i) //size_t是在标准C库中定义,为unsigned int
{
if(in[i] == '(' || in[i] == '*' || in[i] == '/') //遇到'('入栈,因为*/优先级别比栈顶高,直接入栈
{
stack.push(in[i]);
}
//持续出栈
else if(in[i] == ')')
{
while(1)
{
char tmp = stack.top();
stack.pop();
if(tmp != '(')
{
cout << tmp;
}
else
break;
}
}
else if(in[i] == '+' || in[i] == '-')
{
if(stack.top() == '*' || stack.top() == '/')
{
while(stack.top() == '*' || stack.top() == '/')
{
cout << stack.top();
stack.pop();//比栈顶优先级低。直接出栈而且输出
}
cout << in[i];
}
else
stack.push(in[i]);
}
else
cout << in[i]; //遇到数字直接输出
}
while(stack.size())
{
cout << stack.top();
stack.pop();
}
}
很多其它应用:
UVa514 Rails(铁轨)
UVa442 Matrix Chain Multiplication(矩阵链乘)
參考文献与链接:
《数据结构与算法分析——C语言描写叙述》
《算法竞赛入门经典》
堆栈概念
C++ STL