Loading

统计和生成所有不同的二叉树 & 通过先序和中序数组生成后序数组

统计和生成所有不同的二叉树

题目:不同的二叉搜索树 & 不同的二叉搜索树 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用到我自己的代码里的寻找中序数组中的索引就更好了(空间换时间)。

posted @ 2022-02-20 12:13  幻梦翱翔  阅读(47)  评论(0)    收藏  举报