一:问题
问题描述:
堆栈和队列通常被认为是数据结构的面包和黄油,可用于体系结构、解析,操作系统和离散事件模拟。堆栈在形式语言理论中也很重要。现在的问题涉及黄油和煎饼(而不是面包),同时还有一个根据唯一但完整的规则来翻煎饼的服务器。
给你一栈的煎饼,请你编写一个程序用于指示这个栈如何被排序以使得最大的煎饼在最下面而最小的煎饼在最上面。煎饼的直径将被给出。栈中的所有煎饼的直径都不一样。对栈排序是通过一系列“翻转”来完成的。
一次翻转的意思是:在两个煎饼之间插入铲子,然后将铲子上面的一堆煎饼整体翻过来。也就是指定一个位置,其上的子栈整体翻转。翻转的位置将会被给出。位置是这样定义的:栈底编号为1,栈顶编号为n。一个栈的煎饼的给出方式,是从上到下给出煎饼的直径。
举例来说,这是三个栈,左边的栈的最上面的煎饼直径为8
8 7 2
4 6 5
6 4 8
7 8 4
5 5 6
2 2 7
左侧栈,可在位置3(即直径7)处翻转,得到中间那个栈,而中间那个栈可在位置1(即直径2)处翻转,得到右侧的栈。
输入:
输入由多个煎饼栈组成。每个栈有1到30个煎饼,每个煎饼的直径在1-100直径,以文档结束为输入结束。每个栈,独占一行,从左到右依次代表从栈顶到栈底的煎饼的直径,空格隔开。
Sample Input
1 2 3 4 5
5 4 3 2 1
5 1 2 3 4
输出:
对于每个煎饼栈,输出首先应原样将栈的数据打印成一行,随后的一行是翻转位置的次序,空格隔开,以0结束。(结束的目标是最大直径在最下面,最小直径在最上面)。
Sample Output
1 2 3 4 5
0
5 4 3 2 1
1 0
5 1 2 34
1 2 0
二:分析
1.算法分析:
首先,题目要求的功能将将一堆煎饼按照特定方式排序,来让最大的煎饼在最下方(栈底),最小的煎饼在最上方(栈顶)。
对于一堆存储在栈中的煎饼:
①判断煎饼的最大值是不是在栈底,若是则进行第三步,若不是则进行第二步。
②把最大煎饼“挪”到栈底:分两步进行,先把最大煎饼翻转到栈顶(若已经在栈顶则忽略),再将煎饼堆整体翻转一下。
③把最小煎饼“挪”到栈顶:翻转一次即可
这里我专门写了一个flip翻转函数,给定翻转位置,返回翻转后的煎饼堆。
2.功能实现
⑴输入部分:要求输入若干个煎饼堆(栈),一行一个煎饼堆,以EOF为结束标志。用Scanner对象读取数据,以ArrayList<int[]>来存储输入数据,一行一行地读取数据,每一行数据存入一个整型数组,所有数组对象添加到ArrayList中。输入结束标志是按下了两个回车。
⑵实现部分:首先在主函数中创建一个Stack数组,通过for循环分别把对应数据压入栈中,然后通过for循环依次调用核心函数fun,在此之前,先得到每个栈的最大煎饼半径和最小煎饼半径。
三:代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
List<int[]> list = inputStacks();
int numOfStacks = list.size();
Stack<Integer>[] stacks = new Stack[numOfStacks];
for(int i=0;i<numOfStacks;i++) //遍历list所有成员,将数组内容分别压入每个栈中
{
int[]num = list.get(i);
stacks[i] = new Stack<Integer>();
for(int j=num.length-1;j>=0;j--)
{
stacks[i].push(new Integer(num[j]));
}
}
for(int i=0;i<numOfStacks;i++) //遍历所有栈,依次实现功能
{
int[]num = list.get(i);
for(int j=0;j<num.length;j++)
{
System.out.print(num[j]+" "); //先原封不动输出一遍输入(题目要求)
}
System.out.println("");
Arrays.sort(num);
int min = num[0];
int max = num[num.length-1]; //获取最大煎饼和最小煎饼的半径
fun(stacks[i],max,min);
}
}
public static List<int[]> inputStacks() //输入多个栈,并以数组的形式存储,所有数组存储在一个ArrayList中
{
Scanner in = new Scanner(System.in);
String s =null;
List<int[]> list = new ArrayList<int[]>();
while(!(s=in.nextLine()).equals(""))
{
String[] str = s.split(" ");
int[] nums = new int[str.length];
for(int i=0;i<str.length;i++)
{
nums[i]=Integer.parseInt(str[i]);
}
list.add(nums);
}
return list;
}
public static void fun(Stack<Integer> s,int max,int min) //核心功能函数
{
int maxAt = s.size()-s.search(max)+1; //search函数返回的位置是以栈顶为1的基准位置,与题目定义位置编号相反。
if(maxAt!=1) //先要确保最大的煎饼在栈底,这部操作可能需要两步完成
{
if(maxAt==s.size()) //最大值在栈顶,只需要整体翻转一次
{
System.out.print(1+" ");
s=flip(s,1);
maxAt=1;
}
else //最大值不在栈顶,需要两次翻转操作:即先把最大值翻转到栈顶,再整体翻转一次
{
System.out.print(maxAt+" ");
s=flip(s,maxAt); //把最大值翻转到栈顶
System.out.print(1+" ");
s=flip(s,1);
maxAt=1;
}
}
int minAt = s.size()-s.search(min)+1;
if(minAt!=s.size()) //然后再确保最小的煎饼再栈顶
{
System.out.print(minAt+" ");
s=flip(s,minAt);
minAt=s.size();
}
System.out.println(0);
}
public static Stack<Integer> flip(Stack<Integer> s,int flipAt) //在指定位置翻转栈,并返回新栈
{
int[] flipPancake = new int[s.size()-flipAt+1];
for(int i=0;i<flipPancake.length;i++)
{
flipPancake[i]=(int) s.pop();
}
for(int i=0;i<flipPancake.length;i++)
{
s.push(new Integer(flipPancake[i]));
}
return s;
}
}
四:运行结果