栈的应用
简单计算器的实现:
思路:将中缀表达式转化为后缀表达式,计算后缀表达式
将中缀表达式转化为后缀表达式:
- opStack 操作符栈
- queue 存放后缀表达式的队列
- 有括号的情况
计算后缀表达式: 1.利用quque与空的opStack即可
代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
/**
* 简单计算器
*/
public class Codeup_1918 {
String s;//读入的中缀表达式
String[] strings;//空格分隔中缀表达式
Stack<Node> opStack=new Stack<>();//操作符栈
Queue<Node> queue=new LinkedList<>();//存放后缀表达式
HashMap<Character,Integer> hm=new HashMap<>();//存放运算符优先级,乘除为1,加减为0
public static void main(String[] args) throws IOException {
Codeup_1918 test = new Codeup_1918();
test.input();
test.change();
test.cal();
}
//读入与初始化
public void input() throws IOException {
InputStreamReader ir = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(ir);
s=br.readLine();
strings=s.split(" ");
hm.put('(',-1); //括号的优先级应该是最高的,但是我不想把括号加入到后缀表达式中,所以设定优先级为-1。当栈顶为左括号的时候,不管op的优先级是0还是1,都能直接入栈。
hm.put('+',0);
hm.put('-',0);
hm.put('*',1);
hm.put('/',1);
}
//将中缀表达式转换为后缀表达式
public void change(){
for (String string : strings) {
Node node = new Node();
if(string.matches("[0-9]+")){//可转化为整形
node.num=Integer.parseInt(string);
node.flag=true;
queue.offer(node);//将数字加入后缀表达式
}else if(string.charAt(0)=='('){
node.op=string.charAt(0);
node.flag=false;
opStack.push(node);
}else if(string.charAt(0)==')'){
while(opStack.peek().op!='('){//将栈中元素弹出直到遇到左括号为止
queue.offer(opStack.pop());
}
opStack.pop();//把左括号从栈中弹出来,不想把括号放入后缀表达式中,在生成后缀表达式的过程中便利用括号决定运算顺序
}else{//无法转化为整形则是运算符
node.op=string.charAt(0);
node.flag=false;
//判断与栈顶运算符的优先级,该op高于则将op压入操作符栈,若低于或等于则将操作符栈中的操作符不断弹出到后缀表达式中,直到op的优先级高于栈顶
while(!opStack.isEmpty()&&hm.get(opStack.peek().op)>=hm.get(node.op)){//只有op优先级高于栈顶优先级,才能将其压入操作符栈
queue.offer(opStack.pop());
}
opStack.push(node);
}
}
//如果运算符栈中还有元素,则全部弹入后缀表达式
while(!opStack.isEmpty()){
queue.offer(opStack.pop());
}
System.out.println(queue);
}
//计算后缀表达式
public double cal(){
while (!queue.isEmpty()){
Node node = queue.poll();
if(node.flag){//操作数
opStack.push(node);
}else{//运算符 弹出栈中两个数,先弹出的是第二操作数,后弹出的是第一操作数,运算后将结果push入栈
Node opNum2 = opStack.pop();
Node opNum1 = opStack.pop();
Node opResult = new Node();
opStack.push(opResult);
char op=node.op;
if(op=='+') opResult.num=opNum1.num+opNum2.num;
if(op=='-') opResult.num=opNum1.num-opNum2.num;
if(op=='*') opResult.num=opNum1.num*opNum2.num;
if(op=='/') opResult.num=opNum1.num/opNum2.num;
}
}
System.out.println("后缀表达式结果为:"+opStack.peek().num);
return opStack.peek().num;
}
}
/**
* 关于为什么要封装?
* 因为用队列存放后缀表达式的时候,要存放数字,也要存放运算符。那么使用泛型的话,泛型的选择就成为了一个难点.如果选String,显然很不方便,单个操作符是个String,数字也是个String;
* 选int不可能,无法存放操作符;选char会面临多位数的问题
*/
class Node{
double num;//操作数
char op;//操作符
boolean flag;//true表示操作数,false表示操作符
@Override
public String toString() {
return "Node{" +
"num=" + num +
", op=" + op +
", flag=" + flag +
'}';
}
}
链表处理
静态链表1:
class Node{
// int address;//地址就是自己的数组下标
char data;
int next;
boolean flag;//标志是否在链表上 //标记位,因为题目给出的节点不一定都在链表上
}
Node[] nodes=new Node[maxn];
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 静态链表
* 使用数组,在retrieve上的时间复杂度会远低于ArrayList
*/
public class PAT_A1032_2 {
//地址为5位
final static int maxn=100001;
static Node[] nodes=new Node[maxn];
public static void main(String[] args) throws IOException {
InputStreamReader ir = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(ir);
String line = br.readLine();
String[] s = line.split(" ");
int begin1=Integer.parseInt(s[0]);
int begin2=Integer.parseInt(s[1]);
int total=Integer.parseInt(s[2]);
for(int i=0;i<total;i++){
String line1 = br.readLine();
String[] s1 = line1.split(" ");
int address=Integer.parseInt(s1[0]);
char data=s1[1].charAt(0);
int next=Integer.parseInt(s1[2]);
Node node = new Node();
node.data=data;
node.next=next;
nodes[address]=node;
}
//由begin1遍历整个链表,并把遍历到的节点的flag设置为true
int address=-1;
int temp=begin1;
while(temp!=-1){
nodes[temp].flag=true;
temp=nodes[temp].next;
}
//由begin2遍历整个链表,遍历过程中判断flag,如果由等于true的,则直接返回节点地址,如果整个链表遍历完了都没有直接返回,则返回-1
temp=begin2;
while (temp!=-1){
if(nodes[temp].flag){
address=temp;
break;
}else{
temp=nodes[temp].next;
}
}
System.out.println(address);
br.close();
}
}
class Node{
// int address;//地址就是自己的数组下标
char data;
int next;
boolean flag;//标志是否在链表上
}
静态链表2:
注意点:
- 题目给出的节点不一定都在链表上
- 通过增加标志属性flag来区分有效节点与无效节点。
- 要给链表节点重新排序时,会涉及多层排序:(1) 先对对有效节点与无效节点排序 (2)再对有效节点之间的属性按要求排序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
public class PAT_A1052 {
final static int maxn=100001;
static Node[] nodes=new Node[maxn];
public static void main(String[] args) throws IOException {
for(int i=0;i<maxn;i++){
nodes[i]=new Node();//flag=0
}
InputStreamReader ir = new InputStreamReader(System.in);
BufferedReader bf = new BufferedReader(ir);
String line = bf.readLine();
String[] s = line.split(" ");
int total=Integer.parseInt(s[0]);//题目给出的总结点数,这些节点并不一定都在链表上! //TODO
int begin=Integer.parseInt(s[1]);
for (int i = 0; i < total; i++) {
String line1 = bf.readLine();
String[] s1 = line1.split(" ");
Node node = new Node();
// node.flag=1;//flag=1 不能这样,因为题目给出的节点不一定都是有效节点 //TODO
int address=Integer.parseInt(s1[0]);
nodes[address]=node;
node.address=address;
node.key=Integer.parseInt(s1[1]);
node.next=Integer.parseInt(s1[2]);
}
//需要自己遍历一次链表筛选出有效节点 //TODO
int temp=begin;
int trulyTotal=0;
while(temp!=-1){
nodes[temp].flag=1;
trulyTotal++;
temp=nodes[temp].next;
}
if(trulyTotal==0){//特判,链表中没有节点 //TODO
System.out.println("0 -1");
return;
}
//对所有节点重新排序------>排序有易错点:(需要二级排序) //TODO
// 1.由于节点存储方式是数组,那么会有大量无效节点存在,需要对有效节点与无效节点进行区分与排序,根据flag
// 2.需要根据两个有效节点之间的key排序
MyComparator myComparator = new MyComparator();
Arrays.sort(nodes,myComparator);
//重新构建逻辑关系
int newBegin=nodes[0].address;
for(int i=0;i<trulyTotal-1;i++){
nodes[i].next=nodes[i+1].address;
}
nodes[trulyTotal-1].next=-1;
System.out.printf("%d %05d\n",total,newBegin);
for(int i=0;i<trulyTotal;i++){
if(nodes[i].next==-1){
System.out.printf("%05d %d %d\n",nodes[i].address,nodes[i].key,nodes[i].next);
}else{
System.out.printf("%05d %d %05d\n",nodes[i].address,nodes[i].key,nodes[i].next);
}
}
}
}
class MyComparator implements Comparator<Node> { //TODO
@Override
public int compare(Node o1, Node o2) {
if(o1.flag==0||o2.flag==0){
return o2.flag-o1.flag;//一级排序
}
return o1.key-o2.key;//二级排序
}
}
class Node{
int address;
int key;
int next;
int flag=0;
}