题目:请把纸条竖着放在桌上,然后从纸条的下边向上对折,压出折痕后再展开。此时有1条折痕,突起的方向指向纸条的背面,这条折痕叫做“下”折痕 ;突起的方向指向纸条正面的折痕叫做“上”折痕。如果每次都从下边向上边对折,对折N次。请从上到下计算出所有折痕的方向。给定折的次数n,请返回从上到下的折痕的数组,若为下折痕则对应元素为"down",若为上折痕则为"up".
思路:对于这种实际问题,动手这一下来找规律:
n=1:下
n=2:下 下 上
n=3:下 下 上 下 下 上 上
n=4:下 下 上 下 下 上 上 下 下 下 上 上 下 上 上
……可以发现是如果建立一棵二叉树,令根结点为下,之后每个结点的左结点为下,右结点为上,那么折痕的顺序恰好就是中序序遍历这棵二叉树的顺序,因此先建立一棵具有上下值的二叉树,然后按照中序遍历的顺序遍历这棵二叉树即可。
首先建立二叉树:显然这是一棵满二叉树,n就是这棵树的层数,按层遍历的方式建立起一棵二叉树,使用一个队列,不需要考虑换行,先将root放入到queue中,然后弹出temp,并为其建立左右结点并分别放入queue中,再弹出一个结点……根据规律,当弹出2^(n-1)-1个结点时恰好将全部结点建立并放入到队列中,于是返回的root结点就是新建二叉树的根结点。
之后使用中序遍历的方式遍历这棵二叉树并将每个结点的值放入到String[]即可。
上面的办法当然可行,但是需要建立二叉树比较麻烦,其实由于这道题目比较特殊,每个结点的子节点之后2个值down和up而且左结点必然是down,右结点必然是up,因此其实不需要建立二叉树就可以直接进行遍历,根结点的值直接给定为true或者false,显然要进行的是对一棵二叉树的中序遍历,建立一个递归函数,实现的功能逻辑是:传入根结点的值和根结点所在的层数,要求中序遍历这棵树。对于一个结点root所在的树,要中序遍历这棵树就要先遍历它的左子树,然后遍历这个根结点(将其放入字符串数组string[]),然后再遍历这个结点的右子树。如何遍历左右子树?由于这里没有建立树的结构,于是递推关系是通过层数来建立的,每一次遍历一棵子树时要给定这棵子树的层数level,初始时root结点的level=1;然后递推关系中,它的左子树的层数就是level+1,根结点的值是down,右子树的层数就是level+1,根结点的值就是up,一直递归调用,当level==对折次数n时遍历结束。
这里关键是构造出一个递归的逻辑,递归的逻辑要求完善而严谨,多练多理解。
常识:key1:集合可以使用toArray()方法直接转换为Object[]类型的数组或者使用toArray(T[])转换为指定类型的数组,但是注意,T[]只能是包装类型的数组,即只能是对象类型的数组String[],Integer[],不能是基本类型的数组int[],否则无法转换。本题中是要求转换为String[]数组因此是可以进行转换的。其实这个原理就是数组类型强制转换的原理,Object[]数组可以强转为(Integer[])objects,但是不能转换为int[] ints=(int[])objects;因此遇到集合转换为String[]的问题可以直接使用toArray()进行转换,但是遇到转换为int[]的不能使用toArray(因为这样只能转换为Integer[]),需要遍历集合逐个取出赋值给数组。但是为了统一,还是选择逐个取出集合中的元素赋值给数组中的元素。
import java.util.*;
//折纸打印问题:利用中序遍历的思想,关键是构造出一个正确的递归函数
public class FoldPaper {
public String[] foldPaper(int n) {
//特殊输入
if(n<1) return null;
//由于折痕条数不知道所以使用集合(其实折痕条目是知道的)
List<String> list=new ArrayList<>();
//调用递归函数getFolder()来返回所有的折痕
this.getFolder(1,n,"down",list);
//key1:将list中的结果逐一取出放入到String[]中,因为list<Integer>不能直接转int[],但是List<String>可以转String[]
//但是为了统一,还是使用遍历集合的方式逐个取出
String[] results=new String[list.size()];
for(int i=0;i<results.length;i++){
results[i]=(String)(list.get(i));
}
return results;
}
//设计一个递归方法,用于对给定层次上指定根结点开始的子树进行中序遍历,将遍历的结点放入list中
//level:当前遍历的子树根结点的层数;n:最大层数,即对折次数;val:当前子树根结点的值
private void getFolder(int level,int n,String val,List<String> list){
//边界条件
if(level>n) return;
//①先遍历左子树
this.getFolder(level+1,n,"down",list);
//②遍历中间结点(这里的所谓遍历是指将其放入到集合list中)
list.add(val);
//③遍历右子树
this.getFolder(level+1,n,"up",list);
}
}