题目23:从上往下打印二叉树的每个结点,同一层的结点按照从左到右的顺序打印。二叉树定义如下:
public class TreeNode{
public int value;
public TreeNode leftNode;
public TreeNode rightNode;
public TreeNode() {}
public TreeNode(int value) {
this.value = value;
}
}
分析:
前序遍历:根节点排最先,然后同级先左后右;中序遍历:先左后根最后右;后序遍历:先左后右最后根。本题则不同,不妨先画一下打印二叉树的过程,如下图和表所示:
步骤 | 操作 | 队列 |
---|---|---|
1 | 打印结点8 | 结点6、10 |
2 | 打印结点6 | 结点10、5、7 |
3 | 打印结点10 | 结点 5、7、9、11 |
4 | 打印结点5 | 结点7 、9、11 |
5 | 打印结点7 | 结点 9、11 |
6 | 打印结点9 | 结点 11 |
7 | 打印结点 11 |
从根结点开始分析,为了能够打印值为8的结点的两个子节点,我们应该在遍历到该结点时把6和10两个结点保存到容器中,现在容器中有两个结点,按照从左到右的要求,先取出6结点,打印6结点的值时分别把5和7结点放入容器,此时容器中三个结点:10、5、7,接着取出10结点…以此类推,直至容器中没有结点。
从上面的推理可以发现规律:每一次打印一个结点的时候,如果该结点有子结点,则把该结点的子结点放到一个队列的末尾。接下来到队列的头部取出最早进入队列的结点,重复前面的操作,直至队列中所有的结点都被打印出来为止。
代码如下所示:
public static void printFromTopToBottom(TreeNode pNode) {
if (pNode == null) return;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(pNode);
while (queue.size() != 0) {
TreeNode node = queue.poll();
System.out.print(node.value + " ");
if (node.leftNode != null) queue.add(node.leftNode);
if (node.rightNode != null) queue.add(node.rightNode);
}
}
public static void main(String[] args) {
TreeNode node1 = new TreeNode(8);
TreeNode node2 = new TreeNode(6);
TreeNode node3 = new TreeNode(10);
TreeNode node4 = new TreeNode(5);
TreeNode node5 = new TreeNode(7);
TreeNode node6 = new TreeNode(9);
TreeNode node7 = new TreeNode(11);
node1.leftNode = node2;
node1.rightNode = node3;
node2.leftNode = node4;
node2.rightNode = node5;
node3.leftNode = node6;
node3.rightNode = node7;
printFromTopToBottom(node1);
printFromTopToBottom(null);
}
题目28:输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、cab和cba。
分析:
对于复杂的问题,可以将问题分解,将一个字符串看成两部分组成:第一部分为它的第一个字符,第二部分是后面的所有字符。首先求所有可能出现在第一个位置的所有字符,即把第一个字符和后面的所有字符交换。第二步固定第一个字符,求后面所有字符的排列,这个时候仍然把后面的字符分成两部分…
代码如下所示:
public static ArrayList<String> permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if (str == null || str.length() == 0) return result;
char[] chars = str.toCharArray();
getResult(chars, 0, str.length() - 1, result);
return result;
}
public static void getResult(char[] chars, int start, int end, ArrayList<String> result) {
if (start == end) result.add(String.valueOf(chars));
else {
for (int i = start; i <= end; i++) {
swap(chars, start, i); //交换
getResult(chars, start + 1, end, result);
swap(chars, start, i); //换回来,保证下次位置是正确的
}
}
}
public static void swap(char[] chars, int a, int b) {
if (a != b) { // 会出现自身交换
char temp = chars[a];
chars[a] = chars[b];
chars[b] = temp;
}
}
public static void main(String[] args) {
System.out.println(permutation("abc"));
System.out.println(permutation("a"));
System.out.println(permutation(""));
System.out.println(permutation(null));
}
变型1:如果不是求字符串的所有排列,而是求字符的所有组合,应该怎么办?还是输入a、b、c,则它们的所有组合有a、b、c、ab、ac、bc、abc。
分析:
如果输入 n 个字符,则这 n 个字符能构成长度为 1 的组合、长度为 2 的组合、….、长度为 n 的组合。在求 n 个字符的长度为 m ( 1<=m<=n) 的 组合的时候,我们把这 n 个字符分成两部分:第一个字符和其余的所有字符。如果组合里包含第一个字符,则下一步在剩余的字符里选取 m-1个字 符;如果组合里不包含第一个字符,则下一步在剩余的 n-1 个字符里选取 m 个字符。也就是说,我们可以把求 n 个字符组成长度为 m 的组合的问题分解成两个子问题,分别求 n-1 个字符串中长度为 m-1 的组合,以及求 n-1 个字符的长度为 m 的组合。这两个子问题都可以用递归的方式解决。
public static void combination(String str, int index, int number, StringBuilder sb) {
if (number == -1) {
System.out.println(sb.toString());
return;
}
if (index == str.length()) return;
sb.append(str.charAt(index));
combination(str, index + 1, number - 1, sb);
sb.deleteCharAt(sb.length() - 1);
combination(str, index + 1, number, sb);
}
public static void combine(String str) {
if (str == null) return;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
combination(str, 0, i, sb);
}
}
public static void main(String[] args) {
combine("abc");
System.out.println("------------");
combine("a");
System.out.println("------------");
combine(null);
System.out.println("------------");
combine("");
}
变型2:输入一个含有8个数字的数组,判断有没有可能把这8个数字分别放到正方体的8个顶点上,使得正方体上三组相对的面上的4个顶点的和都相等。
分析:
相当于先得到a1、a2、a3、a4、a5、a6、a7和a8这8个数字的全排列,然后判断有没有一个排列符合题目给定的条件,即a1+a2+a3+a4=a5+a6+a7+a8,a1+a3+a5+a7=a2+a4+a6+a8,a1+a2+a5+a6=a3+a4+a7+a8
变型3:在8×8的国际象棋上摆放8个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角线上。请问总共有多少种符合条件的摆法?
8个皇后的任意两个不能处在同一行,那么肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把数组ColumnIndex的8个数字分别用0~7初始化,接下来就是对数组ColumnIndex做全排列。因为我们是用不同的数字初始化数组,所以任意两个皇后肯定不同列。我们只需判断每一个排列对应的8个皇后是不是在同一对角线上,也就是对于数组的两个下标i和j,是不是i-j=ColumnIndex[i]-ColumnIndex[j]或者j-i=ColumnIndex[i]-ColumnIndex[j]。