题目如下:
描述:
给定一个正整数N代表火车数量,0<N<10,接下来输入火车入站的序列,一共N辆火车,每辆火车以数字1-9编号。要求以字典序排序输出火车出站的序列号。
知识点: 栈
题目来源: 内部整理
练习阶段: 高级
运行时间限制: 无限制
内存限制: 无限制
输入:
有多组测试用例,每一组第一行输入一个正整数N(0<N<10),第二行包括N个正整数,范围为1到9。
输出:
输出以字典序排序的火车出站序列号,每个编号以空格隔开,每个输出序列换行,具体见sample。
样例输入:
3
1 2 3
样例输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
思路:此处所谓字典序排序的意思是这n辆火车有多少种出站的可能顺序(也就是数据结构中的栈有多少种出栈顺序)。思路为用三个变量分别存储待进站火车,站中火车和已出站火车,其中待进站火车用Queue(队列)存储和站中火车采用stack(栈)存储,已出站火车采用StringBuilder来存储,具体实现是采用递归的方法,递归函数的参数为当前待进站火车、站中火车、已出站火车的值所组成的三元组,递归结束条件是,未进站火车和站中火车均为空,此时输出已出站火车即为所有出站的一种可能,递推关系为对于当前情况有让下一辆火车进站或让站中的一辆火车出站两种可能,对于两种可能分别调用递归函数,即可得出问题的解。
上代码:
import java.util.EmptyStackException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
public class Main{
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int num=sc.nextInt();
Queue<Integer> trainWait=new LinkedList<Integer>();//准备进站的火车队列
Stack<Integer> trainIn=new Stack<Integer>();//站中的火车
StringBuilder trainOut=new StringBuilder(); //已经出站的火车
//准备好要进站的火车队列
for(int i=0;i<num;i++)
{
trainWait.add(sc.nextInt());
}
stationDispatch(trainWait, trainIn, trainOut);
sc.close();
}
public static void stationDispatch(Queue<Integer> trainWait,Stack<Integer> trainIn,StringBuilder trainOut)
{
//查看是否还有要进站的火车和站中是否还有火车
boolean hasWaitTrain=true;
boolean hasParkTrain=true;
//对于队列如果没有元素peek函数返回null
if(trainWait.peek()==null)
{
hasWaitTrain=false;
}
//对于栈如果没有元素则抛出EmptyStackException
try {
trainIn.peek();
} catch (EmptyStackException e) {
hasParkTrain=false;
}
//处理
//情况1:即没有要进站的火车,站中也没有火车,则直接输出结果
if(!hasWaitTrain&&!hasParkTrain)
{
String out=trainOut.toString();
System.out.println(out.substring(0,out.length()-1));
return;
}
//情况2:有要出站的火车可以出站
if(hasParkTrain)
{
//复制个参数的值
Queue<Integer> trainWaitTmp=new LinkedList<Integer>(trainWait);
Stack<Integer> trainInTmp=(Stack<Integer>)trainIn.clone();
StringBuilder trainOutTmp=new StringBuilder(trainOut);
trainOutTmp.append(trainInTmp.pop().toString()+" ");
//递归调用函数
stationDispatch(trainWaitTmp, trainInTmp, trainOutTmp);
}
//情况3:有要进站的火车可以进站
if(hasWaitTrain)
{
//复制个参数的值
Queue<Integer> trainWaitTmp=new LinkedList<Integer>(trainWait);
Stack<Integer> trainInTmp=(Stack<Integer>)trainIn.clone();
StringBuilder trainOutTmp=new StringBuilder(trainOut);
trainInTmp.push(trainWaitTmp.poll());
//递归调用函数
stationDispatch(trainWaitTmp, trainInTmp, trainOutTmp);
}
}
}
写下来这段代码还是用到了不少东西,总结如下
(1)使用队列来存储待进站的火车,其目的是要实现先输入的火车序号先进入火车站即FIFO,在java中Queue为一个接口,其具体实现一般采用LinkedList来实现,所以才有代码中的
Queue<Integer> trainWait=new LinkedList<Integer>();
,对于Stack,java中是有具体实现的所以可以直接new。
(2)关于函数值传递和引用传递的相关问题,在写具体函数是对参数的操作有两种不同的需求,即仅复制参数的值进行处理,另一种是要改变参数所代表的变量的值。java函数的参数默认是引用传递,即在函数内部改变参数的值会对函数外部的变量产生影响。为了实现值传递就要进行相应处理。有两种处理方式,第一种是在调用函数的时候,将要传递的值复制一个副本再传入函数,将函数内部与外部隔离开。另一种方式在参数传入函数内部后在函数头部先对参数进行复制,对复制后的变量进行相关操作。两种方式大体上是一样的,但是在函数内部头部复制一个副本进行操作通用性较好。但是在有些情况下在函数外部复制副本传入函数更为方便,如本题中,因为在出入之前要进行一些操作,而又不希望这些操作影响到原来变量,所以先复制,处理后传入函数。另注意一下不同类型变量复制副本的不同方式。
(3)对于queue,如果内容为空调用peek()时返回null,而对于stack,如果内容为空调用peek()时会抛出EmptyStackException错误。另queue出队用poll(),而stack出栈用pop().