数据结构专题一(栈和队列)

栈的定义

  栈是一种后进先出的数据结构。

  栈顶指针始终指向栈的最上方元素的一个标记,当使用数组实现栈时,栈顶指针是一个int型的变量(数组下标从0开始),通常记为TOP;当使用链表实现栈时,则是一个int*型指针。栈中没有元素(栈空)时令TOP为-1

栈的常见操作

  1. 清空(clear)
    栈的清空操作将栈顶指针TOP置为-1,表示栈中没有元素
	void clear(){
		TOP = -1;
	}
  1. 获取栈内元素个数(size)
    由于栈顶指针TOP始终指向栈顶元素,而数组下标从0开始,因此栈内元素的数为TOP+1
int size(){
	return TOP+1;
}
  1. 判空(empty)
    当TOP==-1时栈空
	bool empty(){
		if(TOP == -1) return true;
		else return false;
	}
  1. 进栈(push)
    push(x)将x置于栈顶,先把TOP++,然后把x存入TOP指向的位置。
	void push(x){
		st[++TOP] = x;
	}
  1. 出栈(pop)
    pop()将栈顶元素出栈,将TOP–;
	void pop(){
		TOP--;
	}
  1. 取栈顶元素(top)
    st[TOP]即为栈顶元素。
int top(){
	return st[TOP];
}

  在使用pop()和top()函数之前必须先使用empty()来判断栈是否为空

  使用STL的stack容器可以很方便的实现栈,但是stack没有清空操作,因此要实现清空操作,可以用一个while循环反复pop出元素直到栈空:

while(!st.empty()){
	st.pop();
}

队列

队列的应用

  队列是一种先进先出的数据结构。队列总是从队尾加入元素,而从队首移除元素。

  需要一个队首指针front来指向队首元素的前一个位置,使用一个队尾指针rear来指向队尾元素。当使用数组实现栈时,栈顶指针是一个int型的变量(数组下标从0开始),通常记为TOP;当使用链表实现栈时,则是一个int*型指针。

队列的常用操作

  1. 清空(clear)
    初始状态没有元素时,front = rear = -1
  2. 获取队列内元素的个数(size)
    rear-front即为队列内元素的个数。
  3. 判空(empty)
    判断队列为空的条件是 front = rear
  4. 入队(push)
    先把rear+1,然后再把元素存放到rear指向的位置。
  5. 出队(pop)
    直接把队首指针+1来实现出队
  6. 取队首元素(get_front)
    由于front指向的是队首元素的前一个位置,因此front+1才是队首元素的位置。
  7. 取队尾元素(get_rear)
    直接访问rear的位置

  出队操作和取队首队尾元素操作必须在队列非空的情形下才能使用,因此pop()、get_front()、get_rear()函数必须先使用empty()函数判断队列是否为空。

  可以使用STL容器中的queue来实现队列,由于queue中没有清空操作,因此实现清空可以使用一个while循环,反复pop元素直到队列为空。

例题

  读入一个只包含 + , - , ×, / 的非负整数计算表达式,计算该表达式的值。精确到小数点后2位。

例如:输入 :
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
输出:12178.21

思路

  题目中给出的是中缀表达式,因此步骤分为:
  ① 中缀表达式转后缀表达式
  ② 计算后缀表达式

  1. 步骤一:中缀表达式转后缀表达式
    ①设立一个操作符栈,用以临时存放操作符;设立一个队列,用以存放后缀表达式。
    ②从左到右扫描中缀表达式,如果碰到操作数(操作数可能不止一位,需要一位一位读入然后合并在一起),就把操作数加入后缀表达式中。
    ③如果碰到操作符op,就将其优先级与操作符栈的栈顶操作符的优先级比较。
     ● 若op的优先级高于栈顶操作符的优先级,则压入操作符栈。
     ● 若op的优先级低于或等于栈顶操作符的优先级,则将操作符栈的操作符不断弹出到后缀表达式中,直到op的优先级高于栈顶操作符的优先级。
    ④重复上述操作,直到中缀表达式扫描完毕,之后若操作符栈中仍有元素,则将它们依次弹出至后缀表达式中。

  ●所谓操作符的优先级即他们计算的优先级,其中乘法=除法>加法=减法,在实现上可以用map建立操作符和优先级的映射,优先级可以用数字表示,例如乘法和除法优先级为1,加法和减法优先级为0。
  ●为什么当op高于栈顶时就压入操作符栈?
  对于中缀表达式3+2*5,显然如果先计算加法就会引起错误,必须先计算乘法。当从左到右扫描时,加号先进操作符栈,而由于乘号优先级大于加号,其必须先计算,因此后缀表达式中乘号必须在加号前面,于是在栈中乘号要比加号更靠近栈顶,以让其先于加号进入后缀表达式。

  1. 步骤二:计算后缀表达式
      从左到右扫描后缀表达式,如果是操作数,就压入栈;如果是操作符,就连续弹出两个操作数(后弹出的是第一操作数,先弹出的是第二操作数),然后进行操作符的操作,生成新的操作数压入栈中。直到后缀表达式扫描完毕,这是栈中只有一个数,就是答案。
#include<bits/stdc++.h>
using namespace std;

struct node{
	double num;//操作数 
	char op;//操作符 
	bool flag;//true表示操作数,false表示操作符 
}; 

string str;
stack<node> s;//操作符栈
queue<node> q;//后缀表达式序列
map<char,int> op; //操作符优先级 

void change(){ //将中缀表达式转换为后缀表达式 
	double num;
	node temp;
	for(int i =0;i<str.length();){//遍历中缀表达式 
		if(str[i] >='0' && str[i] <= '9'){ //是数字 
			temp.flag = true; //标记是操作数
			temp.num = str[i++]-'0'; //记录操作数的第一个数位
			while(i<str.length() && str[i] >='0' && str[i] <= '9'){
				temp.num = temp.num *10 + (str[i]-'0'); //更新操作数 
				i++;
			} 
			q.push(temp); //将操作数入队 
		}else{ //如果是操作符 
			temp.flag = false;
			//只要操作符栈的栈顶元素比该操作符优先级高
			//就把操作符栈栈顶元素弹出到后缀表达式的队列中
			while(!s.empty() && op[str[i]] <= op[s.top().op]){
				q.push(s.top());
				s.pop();
			} 
			temp.op = str[i];
			s.push(temp); //把该操作符入操作符栈
			i++; 
		} 
	}
	//如果操作符栈中海油操作符,就把它弹出到后缀表达式队列中
	while(!s.empty()){
		q.push(s.top());
		s.pop();
	} 
}

double cal(){ //计算后缀表达式 
	double temp1,temp2;
	node cur,temp;
	while(!q.empty()){//只要后缀表达式队列非空 
		cur = q.front();//cur记录队首元素
		q.pop();
		if(cur.flag == true) s.push(cur);//如果是操作数直接入栈
		else{//如果是操作符 
			temp2 = s.top().num;//弹出第二操作数
			s.pop();
			temp1 = s.top().num;//弹出第一操作数
			s.pop();
			temp.flag = true;//临时记录操作数
			if(cur.op == '+') temp.num = temp1+temp2;
			else if(cur.op == '-') temp.num = temp1-temp2;
			else if(cur.op == '*') temp.num = temp1*temp2;
			else temp.num = temp1/temp2;
			s.push(temp);//把该步结果新的操作数压入栈 
		} 
	}
	return s.top().num; //栈顶元素就是结果值 
}

int main(){
	op['+'] = op['-'] = 1;//设定操作符的优先级 
	op['*'] = op['/'] = 2;
	while(getline(cin,str) ,str !="0"){
		for(string::iterator it = str.end();it!=str.begin();it--){
			if(it == ' ') str.erase(it);//把操作符中的空格去掉 
		}
		while(!s.empty()) s.pop();//初始化栈
		change();//将中缀表达式变为后缀表达式
		printf("%.2f\n",cal());//计算后缀表达式 
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值