zoukankan      html  css  js  c++  java
  • 后续遍历搜索二叉树还原最优算法--美团面试题

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

    针对这道题,最简单的算法:假设数组长度为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
  • 相关阅读:
    关于C++类中的静态数据成员
    关于C++中char,sizeof,strlen,string
    C++学习笔记(7)
    C++学习笔记(6)
    C++学习笔记(指针)
    C++学习笔记(4)
    UVA 10780
    UVA 531
    HDU, 3579 Hello Kiki
    UVA, 10413 Crazy Savages
  • 原文地址:https://www.cnblogs.com/fiftyonesteps/p/12747169.html
Copyright © 2011-2022 走看看