统计和生成所有不同的二叉树 & 通过先序和中序数组生成后序数组
统计和生成所有不同的二叉树
题目:不同的二叉搜索树 & 不同的二叉搜索树 II
《程序员代码面试指南》第54题 P173 难度:尉★★☆☆
原问题只是统计二叉树的数量。
首先,如果中序遍历有序且无重复值,则二叉树必为搜索二叉树。
假设num(a)代表a个节点的搜索二叉树有多少种可能。再假设序列为{1,…,i,…,N}。
如果以i作为头节点,则左边和右边分别作为其左右子树。显然,这种情况下有num(i-1)×num(N-i)种可能。
另外,当i=1或者i=N时,有num(0)×num(N-1)=num(N-i)种可能。
书上也提到,利用了动态规划来加速计算的过程。代码如下:
public int numTrees(int n) {
if (n < 2) {
return 1;
}
int[] num = new int[n + 1];
num[0] = 1;
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < i + 1; j++) {
num[i] += num[j - 1] * num[i - j];
}
}
return num[n];
}
进阶问题则是要生成所有不同的二叉树。
思路和原问题类似。枚举每一个值作为头节点,并且通过{1...i-1}和{i+1...N}递归生成左右子树。
public List<Node> generateTrees(int n) {
return generate(1, n);
}
public List<Node> generate(int start, int end) {
List<Node> res = new LinkedList<Node>();
if (start > end) {
res.add(null);
}
Node head = null;
for (int i = start; i < end + 1; i++) {
head = new Node(i);
List<Node> lSubs = generate(start, i - 1);
List<Node> rSubs = generate(i + 1, end);
for (Node l : lSubs) {
for (Node r : rSubs) {
head.left = l;
head.right = r;
res.add(cloneTree(head));
}
}
}
return res;
}
public Node cloneTree(Node head) {
if (head == null) {
return null;
}
Node res = new Node(head.value);
res.left = cloneTree(head.left);
res.right = cloneTree(head.right);
return res;
}
需要额外注意一下,这个克隆节点的意义。如果不新生成节点,把前一个头节点放入list,再修改其左右子树,以为此时是一个新的头节点了,把它放入了list,但是此时之前放入list中的头节点的值也发生了变化。因为它们的引用相同,指向同一个区域了。导致在list中相同的头节点的结构全部一样了。这肯定是不行的。所以对于每一种可能性都要新生成一个头节点。
通过先序和中序数组生成后序数组
《程序员代码面试指南》第53题 P172 难度:士★☆☆☆
题目要求不要重建整棵二叉树,通过这两个数组直接生成正确的后序数组。
书上思路为:根据当前的先序和中序数组,设置后序数组最右边的值,然后划分出左子树的先序、中序数组,以及右子树的先序、中序数组,先根据右子树的划分设置好后序数组,再根据左子树的划分,从右边到左边依次设置好后序数组的全部位置。
public int[] getPosArray(int[] pre, int[] in) {
if (pre == null || in == null) {
return null;
}
int len = pre.length;
int[] pos = new int[len];
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < len; i++) {
map.put(in[i], i);
}
setPos(pre, 0, len - 1, in, 0, len - 1, pos, len - 1, map);
return pos;
}
// 从右往左依次填好后序数组s
// si为后序数组s该填的位置
// 返回值为s该填的下一个位置
public int setPos(int[] p, int pi, int pj, int[] n, int ni, int nj,
int[] s, int si, HashMap<Integer, Integer> map) {
if (pi > pj) {
return si;
}
s[si--] = p[pi];
int i = map.get(p[pi]);
si = setPos(p, pj - nj + i + 1, pj, n, i + 1, nj, s, si, map);
return setPos(p, pi + 1, pi + i - ni, n, ni, i - 1, s, si, map);
}
这个setPos函数还不太好懂。我个人认为我自己的代码实现还算挺好,把题解代码中map用到我自己的代码里的寻找中序数组中的索引就更好了(空间换时间)。

浙公网安备 33010602011771号