数据结构笔记2:栈
栈是一种后进先出的线性表,一般还是用顺序存储结构的比较多,链栈用的较少。
关键注意top指针指向的是最后一个入栈元素的下一个位置;
base指针指向首地址,也是指向最先入栈的元素;realloc函数的用法;清空和销毁栈的区别。
s.base-s.top差值就是此时栈中的元素个数,因为指针+1与地址值+1不同
一、下面是一个可以实现混合运算的基于栈实现的计算器。
Stack.h
#ifndef _STACK_H_
#define _STACK_H_
using namespace std;
typedef int status;
typedef char ElemType;
#define ERROR -1
#define OK 1
typedef struct StackList
{
ElemType *top; //指向栈顶的指针
ElemType *base;//指向栈底的指针
int stacksize;//存储栈的大小,如果不够是在这里扩容
//主要用处是方便判断栈满
}Stack;
/***********************************************************/
status Stack_Init(Stack &s);
status Stack_IfFull(Stack &s);//判断栈是否为满,为满则返回OK
status Stack_IfEmpty(Stack &s);//判断栈是否为空,为空则返回OK
status Stack_Push(Stack &s,ElemType &e);
status Stack_Pop(Stack &s,ElemType &e);
ElemType Stack_GetTop(Stack s);
status Stack_Clear(Stack &s);
status Stack_Destroy(Stack &s);
#endif
Stack.cpp
#include <iostream>
#include <Stack.h>
#include <cstdlib>
using namespace std;
#define STACKINITSIZE 100 //这里是给栈初始化时分配内存的
#define STACKINCREMENT 10
/*************************************************************/
status Stack_Init(Stack &s)
{
s.base = (ElemType*)malloc(STACKINITSIZE * sizeof(ElemType));
s.top = s.base;
s.stacksize = STACKINITSIZE;//初始化时分配好栈的大小
return OK;
}
status Stack_IfFull(Stack &s)
{
if(s.top-s.base >= s.stacksize) //两个指针指向的地址相减得到此时栈的大小(即所占空间大小)
return OK;
else
return 0;
}
status Stack_IfEmpty(Stack &s)
{
if(s.top == s.base) //两个指针指向相同地址
return OK;
else
return 0;
}
status Stack_Push(Stack &s,ElemType &e)
//感觉这个地方e不用引用直接传参也可以啊,可能就是省的额外给形参分配空间了吧
//这里注意一下,top指向的是最后一个入栈元素的下一个位置,而不是最后一个入栈的元素
//而base作为栈的起始指针,其实是指向最早入栈的元素的,而不存在一个空位置专门存放base指针
{
if(Stack_IfFull(s))//先判断栈有没有满
{
s.base = (ElemType*)realloc(s.base,(s.stacksize + STACKINCREMENT)
* sizeof(ElemType));//相当于重新分配了一段内存,把原来栈的内容直接复制过去
if(!s.base) return ERROR;
s.top = s.base + s.stacksize;//栈的位置变了,top指针不再是指向原来的地方,重新赋值
s.stacksize += STACKINCREMENT;//扩容,其实是起标示作用
}
*(s.top) = e;
s.top++;
return OK;
}
status Stack_Pop(Stack &s,ElemType &e)//出栈,并用e保存出栈元素
{
if(s.base == s.top) return ERROR;
s.top--;
e = *(s.top);
return OK;
}
ElemType Stack_GetTop(Stack s)//得到栈顶元素,因为不能改变栈本身的结构,
//但是需要操作top指针,因此这里不用引用而用形参操作,函数主体与出栈类似
{
ElemType e;
if(s.base == s.top) return ERROR;
s.top--;
e = *(s.top);
return e;
}
status Stack_Clear(Stack &s)//清空栈,不用释放存储空间
{
s.top = s.base;//top指到base就行了
return OK;
}
status Stack_Destroy(Stack &s) //完全释放
{
int i,len;
len = s.stacksize;
for(i=0;i<len;i++)
{
free(s.base);//利用base指针清除,清除后指向下一个元素
s.base++;
}
s.base = s.top =NULL;
s.stacksize = 0;
return OK;
}
Culculate.h
#ifndef _CULCULATE_H_
#define _CULCULATE_H_
#include "Stack.h"
status char_or_num(char a);
char cul_plus(char a,char b);
char cul_minus(char a,char b);
char cul_mul(char a,char b);
char cul_div(char a,char b);
int culculate(Stack s,char str[],int &len);
int judge(char a);
status translate(Stack &s,char str1[],char str2[],int &strlen);
#endif
Culculate.cpp
#include"Culculate.h"
#include <iostream>
#include "stdlib.h"
status char_or_num(char a)//判断数字还是字符
{
if(a>='0' && a<='9')
return 1;
else return 0;
}
char cul_plus(char a,char b)//字符加法
{
int c;
char m;
c = (int(a)-48) + (int(b)-48);
m = c + 48;
return m;
}
char cul_minus(char a,char b)//字符减法
{
int c;
char m;
c = (int(a)-48) - (int(b)-48);
m = c + 48;
return m;
}
char cul_mul(char a,char b)//字符乘法
{
int c;
char m;
c = (int(a)-48) * (int(b)-48);
m = c + 48;
return m;
}
char cul_div(char a,char b)//字符除法
{
int c;
char m;
c = (int(a)-48) / (int(b)-48);
m = c + 48;
return m;
}
int judge(char a)//判断符号优先级
{
int ret;
if(a == '*'|| a == '/')
ret = 2;
if(a == '+'|| a == '-')
ret = 1;
return ret;
}
//translate函数,将标准表达式转化乘逆波兰表达式。
//具体思路,遍历字符串,遇到数字直接放到str2中,碰到运算符号
//左括号入栈,等待匹配右括号,匹配成功的话把左右括号中的符号全部输出,包括括号出栈
//*/的优先级高于+-,碰到*/入栈(优先级不低于栈顶元素),碰到+-让元素全部出栈(没有比+-优先度低的),
//再将该元素入栈。全部遍历后再把栈里剩余元素全部输出。
status translate(Stack &s,char str1[],char str2[],int &strlen)
{
int i,j;
int len = 0;
char e;
while(1)
{
if(str1[len] == '\0') break;
len++;
}
for(i = 0,j = 0;i<len;i++)
{
if(char_or_num(str1[i]))//输出数字
{
str2[j] = str1[i];
j++;
}
else
{
if(str1[i] == '('||str1[i] == ')')//匹配括号
{
if(str1[i] == '(')
Stack_Push(s,str1[i]);
if(str1[i] == ')')
{
while(Stack_GetTop(s) != '(')
{
Stack_Pop(s,str2[j]);
j++;
}
Stack_Pop(s,e);
}
}
else
{ //判断优先级
if((judge(str1[i]) > judge(Stack_GetTop(s)))||Stack_IfEmpty(s))
{
Stack_Push(s,str1[i]);
}
else
{
while(!Stack_IfEmpty(s))
{
Stack_Pop(s,str2[j]);
j++;
}
Stack_Push(s,str1[i]);
}
}
}
}
while(!Stack_IfEmpty(s))//输出剩余元素
{
Stack_Pop(s,str2[j]);
j++;
}
strlen = j+1;
Stack_Clear(s);//记得清空用过的栈给之后用
return OK;
}
//碰到数字,入栈;碰到运算符,让栈的倒数两个元素出栈进行运算,运算结果入栈。直到运算结束。
int culculate(Stack s,char str[],int &len)
{
int i,cul;
char nex,pre,ret;
Stack_Push(s,str[0]);
for(i=1;i<len;i++)
{
if(char_or_num(str[i]))
{
Stack_Push(s,str[i]);//是数字,入栈
}
else
{
Stack_Pop(s,nex);
Stack_Pop(s,pre);//栈顶两个元素出栈
switch(str[i])
{
case '+':ret = cul_plus(pre,nex);break;
case '-':ret = cul_minus(pre,nex);break;
case '*':ret = cul_mul(pre,nex);break;
case '/':ret = cul_div(pre,nex);break;
}
Stack_Push(s,ret);//运算完的结果入栈
}
}
ret = Stack_GetTop(s);//最后栈中只剩一个元素为运算结果
cul = int(ret)-48;
return cul;
}
main.cpp
#include "Stack.h"
#include "Culculate.h"
#include <iostream>
using namespace std;
int a[3] = {1,2,3};
/**************************字符串计算器逆波兰表达式运算求解**************************/
/***********这个计算器存在弊端,输入的数只能是0-9以内,大于9的数在整型字符型转换上相对复杂一些******/
//"9*2-(7+1)/((2+2)*2)="; //例子
char str1[22];
char str2[20];
int main()
{
Stack s;
int i = 0;
Stack_Init(s);
cout << "本计算器可运算混合运算,但输入的数字只能是0-9内的,主要还是本人太菜了"<<endl;
cout << "请输入运算表达式,输入“=”按下回车开始运算"<<endl;
int cul,len;
for(i=0;;i++) //输入原表达式
{
cin >> str1[i];//可以直接把表达式敲进去
if(str1[i] == '=')//输入结束
{
str1[i] = '\0';
break;
}
}
translate(s,str1,str2,len);//翻译成逆波兰表达式
i = 0;
cout << "逆波兰表达式为:";
while(str2[i] != '\0')//输出翻译后的逆波兰表达式
{
cout << str2[i];
i++;
}
cout << endl;
cul = culculate(s,str2,len);
cout << "计算结果为:";
cout << cul <<endl;
return 0;
}
运行结果
易错整理:
1、
2、
3、栈顶top,队头,队尾的定义不是唯一的,要审题。
比如top大多数情况指最后入栈元素的下一个位置,有的时候题中指向最后一个入
栈的元素。
4、共享栈更有效利用存储空间,存取数据时间复杂度O(1),可能会发生上溢。