后续遍历搜索二叉树还原最优算法--美团面试题

题目:现有一个数组,已知该数组是搜索二叉树的后续遍历结果,请还原出原二叉树的结构。

针对这道题,最简单的算法:假设数组长度为n,数组的最后一位必然是根节点,之后,遍历数组,找到第一个大于根节点的位置m,数组下标为0~m-1的递归建立左树,m~n-2的递归建立右树。这种算法的时间复杂度取决于遍历数组找m的算法,比如折半查找法,总体时间复杂度为O(nlogn),这种算法就不贴代码了,比较简单,读者可以自行实现。

但是这种算法并不是最优算法,让我们来想想:

  我们能不能读取一个值就把这个值给放到正确的位置呢?

  由于是后续遍历的,数组的整体情况是小的在前,大的在后,根节点在最后。

假设有如下树:

 

 

 

 

 

 

后续遍历结果为 [17,19,31,63, 50, 88, 69, 64, 37, 36]

你会怎么还原呢?

我会这么做,先拿到最后一个数字36,这个是根节点没跑了

然后拿出倒数第二个数字37,大于36那么是36的右节点没跑了

同理拿出64,大于37(只需要和上一个节点做比较),那么是37的有几点没跑了

同理69和88也都能找到确定合理的位置

当我们拿到50时,还是和前一个比,小于88,但是他不能作为88的左节点,需要往上看一层,小于69,同样再往上看一层,小于64,在网上看一层,大于37,那么可以确定50是64的左节点了

拿出63,大于50,作为50的右节点

拿出31,小于63、50、64、37、36,那么作为36的左节点

到这里似乎一切都很顺利,也很合理,但是当我们拿出19的时候,小于31,那么需要往上回溯吗?显然不需要,所以当我们拿到的节点小于上一个节点时,直接往上回溯的逻辑是不对的,我们需要知道当前节点是不是小于一路下来的最小节点,如果小于,回溯,没有,不回溯。

于是19作为31的左节点,17作为19的左节点。

至此,我们总结出了规律:

1.从最后一个数指针逐渐前移

2.如果当前节点大于上一个节点,作为上一个节点的右节点

3.如果当前节点小于上一个节点,查看上一个节点是其父节点的左节点还是右节点

3.1如果是左节点,当前节点作为上一节点的左节点

3.2如果是右节点,当前节点和上一节点的父节点比较

3.2.1如果大于,作为上一节点的左节点

3.2.2如果小于,往上回溯一层

易于理解的方式实现可以使用List保存节点路径

List<Integer> nodes = new ArrayList();

nodes的具体内容如下,读者可根据内容自行实现,本文最后给我个人的实现,没有使用List保存节点,而是使用了递归

1.获取36,nodes=[36],根节点

2.获取37,nodes=[36,37],36的右节点

3.获取64,nodes=[36,37,64],37的右节点

4.获取69,nodes=[36,37,64,69],64的右节点

5.获取88,nodes=[36,37,64,69,88],69的右节点

6.获取50,小于88,也小于69,88大于69,nodes=[36,37,64,69]

7.50,小于69,也小于64,69大于64,nodes=[36,37,64]

8.50,小于64,大于67,nodes=[36,37,64,50],64的左节点

9.获取63,大于50,nodes=[36,37,64,50,63],50的右节点

10.获取31,小于63,也小于50,nodes=[36,37,64,50]

中间省略3步

14.31,小于36,36是根节点,nodes=[36,31],36的左节点

15.获取19,小于31,也小于36,但是31小于36,nodes=[36,31,19],31的左节点

16.获取17,小于19,也小于31,但是19小于31,nodes=[36,31,19,17],17的左节点

代码实现如下:

//数据结构

public class SearchTree {
    //左节点
    private SearchTree left;
    //右节点
    private SearchTree right;
    //内容
    private int num;

    public SearchTree(){}
    public SearchTree(int num){
        this.num = num;
    }

    //添加节点
    public void add(int num){
        if(this.num > num){
            addLeft(num);
        }else if(this.num < num){
            addRight(num);
        }
    }

    //添加右节点
    private void addRight(int num) {
        if(this.right == null){
            this.right = new SearchTree();
            this.right.setNum(num);
        }else{
            this.right.add(num);
        }
    }

    //添加左节点
    private void addLeft(int num) {
        if(this.left == null){
            this.left = new SearchTree();
            this.left.setNum(num);
        }else{
            this.left.add(num);
        }
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    //后续遍历
    public List<Integer> houxu(){
        List<Integer> list = new ArrayList<>();
        if(this.left != null){
            list.addAll(this.left.houxu());
        }
        if(this.right != null){
            list.addAll(this.right.houxu());
        }
        list.add(num);
//        System.out.println(num);
        return list;
    }


    public List<Integer> zhongxu(){
        List<Integer> list = new ArrayList<>();
        if(this.left != null){
            list.addAll(this.left.zhongxu());
        }
        list.add(num);
        if(this.right != null){
            list.addAll(this.right.zhongxu());
        }
        return list;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == this){
            return true;
        }
        if(obj == null){
            return false;
        }
        if(!(obj instanceof SearchTree)){
            return false;
        }
        SearchTree tree = (SearchTree) obj;

        return compare(tree, this);
    }

    //比较两棵树,要当前节点num一样,并且左右树递归num一样
    private boolean compare(SearchTree tree, SearchTree tree1){
        if(tree == tree1){
            return true;
        }
        if(tree == null){
            return false;
        }
        if(tree.num != tree1.num){
            return false;
        }
        return compare(tree.left, tree1.left) && compare(tree.right, tree1.right);
    }

    public SearchTree getLeft() {
        return left;
    }

    public void setLeft(SearchTree left) {
        this.left = left;
    }

    public SearchTree getRight() {
        return right;
    }

    public void setRight(SearchTree right) {
        this.right = right;
    }
}

//测试类及还原算法

public class ReverseTest {

    public static void main(String[] args) {
        //创建树
        SearchTree tree = createTree(new int[]{17, 19, 64, 37, 36, 31, 63, 50, 88, 69});
        //后序遍历
        List<Integer> houxu = tree.houxu();
        Integer[] is = houxu.toArray(new Integer[houxu.size()]);
        //还原树
        SearchTree searchTree = reverseTree(is);
        //比较
        System.out.println(tree.equals(searchTree));
    }

    private static SearchTree reverseTree(Integer[] arr){
        SearchTree tree = new SearchTree();
        //设置根节点
        tree.setNum(arr[arr.length - 1]);
        if(arr.length >= 2){
            add(null, tree,arr, new AtomicInteger(arr.length - 2));
        }
        return tree;
    }

    /**
     * 
     * @param pOfLast 上一节点的父节点
     * @param last 上一节点
     * @param arr 数组
     * @param index 当前节点的位置
     */
    private static void add(SearchTree pOfLast, SearchTree last,Integer[] arr, AtomicInteger index) {
        if(index.get() == -1){
            return;
        }
        SearchTree now = new SearchTree(arr[index.get()]);
        if(now.getNum() > last.getNum()){
            //大于当前,走右节点逻辑
            last.setRight(now);
            index.decrementAndGet();
            add(last, now, arr, index);
        }
        //走完右节点逻辑可能所有数据都走完了,不需要再走了
        if(index.get() == -1){
            return;
        }
        now = new SearchTree(arr[index.get()]);
        //如果上一节点为根节点,或者当前节点大于上一节点的父节点,或者上一节点小于其父节点,设置为左节点
        if(pOfLast == null || now.getNum() > pOfLast.getNum() || last.getNum() < pOfLast.getNum()){
            last.setLeft(now);
            index.decrementAndGet();
            add(last, now, arr, index);
        }
    }

    private static SearchTree createTree(int[] arr){
        SearchTree tree = new SearchTree(arr[arr.length - 1]);
        for (int i = arr.length - 2; i >= 0; i--) {
            tree.add(arr[i]);
        }
        System.out.println(tree.houxu());
        return tree;
    }
}

 


17
, 31, 19, 63, 50, 88, 69, 64, 37, 36
posted @ 2021-07-27 15:35  五十一步  阅读(131)  评论(0编辑  收藏  举报