美文网首页java学习之路算法提高之LeetCode刷题
leetCode进阶算法题+解析(十六)

leetCode进阶算法题+解析(十六)

作者: 唯有努力不欺人丶 | 来源:发表于2020-03-06 19:20 被阅读0次

    从中序和后序遍历序列构造二叉树

    题目:根据一棵树的中序遍历与后序遍历构造二叉树。

    注意:
    你可以假设树中没有重复的元素。
    例如,给出
    中序遍历 inorder = [9,3,15,20,7]
    后序遍历 postorder = [9,15,7,20,3]
    返回如下的二叉树:
    3
    /
    9 20
    /
    15 7

    思路:这个算是上一个题目前序中序构造二叉树的姊妹版,不说一模一样大体思路也是差不多的。只不过前序的第一个是根节点变成了后序的最后一个是根节点。我去尝试写代码了。
    第一版代码直接写出来了,就是昨天我自己方法的改版,果然还是自己想的印象深刻。我直接贴代码:

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public TreeNode buildTree(int[] inorder, int[] postorder) {
            if(inorder.length==0) return null;
            TreeNode res = new TreeNode(postorder[postorder.length-1]);
            int i = 0;
            while(postorder[postorder.length-1]!=inorder[i]) i++;
            res.left = buildTree(Arrays.copyOfRange(inorder,0,i),Arrays.copyOfRange(postorder,0,i));
            res.right = buildTree(Arrays.copyOfRange(inorder,i+1,inorder.length),Arrays.copyOfRange(postorder,i,postorder.length-1));
            return res;
            
        }
    }
    

    额,和昨天一样,实现是ok 的,但是性能很一言难尽,直接附上性能排行第一的代码瞻仰:

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public TreeNode buildTree(int[] inorder, int[] postorder) {
            swap(postorder);swap(inorder);
            return helper(postorder,inorder,0,postorder.length-1,0,inorder.length-1);
        }
        public TreeNode helper(int[] pre,int[] in,int pre_left,int pre_right,int in_left,int in_right){
            if(pre_left>pre_right) return null;
            TreeNode root = new TreeNode(pre[pre_left]);
            int i=0;
            while(in[i+in_left]!=pre[pre_left]){
                i++;
            }
            TreeNode left_node=helper(pre,in,pre_left+1,pre_left+i,in_left,in_left+i);
            TreeNode right_node=helper(pre,in,pre_left+i+1,pre_right,in_left+i+1,in_right);
            root.right=left_node;
            root.left=right_node;
            return root;
        }
        public void swap(int[] a){
            int left=0;int right=a.length-1;
            while(left < right){
                int temp=a[left];
                a[left]=a[right];
                a[right]=temp;
                left++;right--;
            }
        }
    }
    

    有序链表转化成二叉搜索树

    题目:给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

    示例:
    给定的有序链表: [-10, -3, 0, 5, 9],
    一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
    0
    /
    -3 9
    / /
    -10 5

    思路:这道题我觉得有点意思啊,怎么说呢,什么都不考虑的情况下应该还是很简单的吧,我不知道坑在哪里,目前的思路就是快慢指针找到链表的中间值,然后作为根节点,然后左右递归,依旧是中间节点作为根节点,就这么一直递归下去。额,还有一点,就是我总觉得链表是很麻烦的东西啊,打算先遍历成数组处理,我去实现下看看。
    喏,很佩服我自己,举一反三,完美的用了和上个题类似的方法做出来了,撒花·~~~~我贴代码:

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public TreeNode sortedListToBST(ListNode head) {
            List<Integer> list = new ArrayList<Integer>();
            while(head!=null){
                list.add(head.val);
                head = head.next;
            }
            int[] d = new int[list.size()];
            int idx = 0;
            for(int i : list){
                d[idx] = i;
                idx++;
            }
            return dfs(d);
        }
        public TreeNode dfs(int[] d){
            if(d.length==0) return null;
            int i = d.length/2;
            TreeNode res = new TreeNode(d[i]);
            res.left = dfs(Arrays.copyOfRange(d,0,i));
            res.right = dfs(Arrays.copyOfRange(d,i+1,d.length));
            return res;
        }
    }
    

    啊哈,这个是第一版本,其实是有点问题的,这里绝对用不到数组复制,我就是懒得改动所以直接用了。我去改动下:

    class Solution {
        public TreeNode sortedListToBST(ListNode head) {
            List<Integer> list = new ArrayList<Integer>();
            while(head!=null){
                list.add(head.val);
                head = head.next;
            }
            int[] d = new int[list.size()];
            int idx = 0;
            for(int i : list){
                d[idx] = i;
                idx++;
            }
            return dfs(d,0,d.length);
        }
        public TreeNode dfs(int[] d ,int start,int end){
            if(start>=end) return null;
            int i = (end+start)/2;
            TreeNode res = new TreeNode(d[i]);
            res.left = dfs(d,start,i);
            res.right = dfs(d,i+1,end);
            return res;
        }
    }
    

    额,不用数组来回来去复制了,但是性能还没上来。。我去瞻仰性能排行第一的代码了,唔,跟我第一个想法一样,快慢指针找中点,然后来回来去的,我直接贴代码:

    class Solution {
        public TreeNode sortedListToBST(ListNode head) {
            if (head == null) {
                return null;
            }
    
            ListNode dummyNode = new ListNode(1);
            dummyNode.next = head;
            ListNode f = head;
            ListNode s = dummyNode;
            while (f.next != null && f.next.next != null) {
                f = f.next.next;
                s = s.next;
            }
    
            TreeNode nh = new TreeNode(s.next.val);
            nh.right = sortedListToBST(s.next.next);
            s.next = null;
            nh.left = sortedListToBST(dummyNode.next);
            return nh;
        }
    }
    

    说实话这个方法我想到了,不过我以为来回来去链表遍历会比这个数组浪费性能呢,所以pass了。。哈哈,看来还是我理解的不够深刻哈。。这道题就这么过了。

    路径总和2

    题目:给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。说明: 叶子节点是指没有子节点的节点。

    示例:
    给定如下二叉树,以及目标和 sum = 22,
    5
    /
    4 8
    / /
    11 13 4
    / \ /
    7 2 5 1
    返回:
    [
    [5,4,11,2],
    [5,8,4,5]
    ]

    思路:这个题怎么说呢,标准回溯?做法肯定是从根节点开始,选择当前元素,把所有能向下走的路径记录,然后最后到了叶子节点,是给定值则添加到结果集,不是给定值则这条路不行,pass。额,至于说到剪枝,我不知道二叉树元素会不会是负数,如果不是的话当当前和大于给定值则可以直接剪枝,但是没这个要求,所以还是先都走一遍吧,我去写下代码。
    好吧,挺简单的一道题让我想复杂了,这个题其实就是个遍历的问题,从根节点遍历,到了叶子节点判断和是不是给定值,不是不操作,是则添加res。最后遍历完结果也出来了,我直接贴代码:

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        List<List<Integer>> res;
        public List<List<Integer>> pathSum(TreeNode root, int sum) {
            res = new ArrayList<List<Integer>>() ; 
            dfs(new ArrayList<Integer>(),sum,0,root);
            return res;
        }
        public void dfs(List<Integer> list,int sum, int temp,TreeNode root){
            if(root==null) return;  
            temp += root.val;     
            list.add(root.val);
            //到了叶子节点。
            if(root.left==null && root.right==null){
                if(temp==sum) res.add(list);
            }else{
                dfs(new ArrayList(list),sum,temp,root.left);
                dfs(new ArrayList(list),sum,temp,root.right);
            }     
        }
    }
    

    但是这个性能只有3ms,我直接看看大佬的代码吧:

    class Solution {
        private List<List<Integer>> lists=new ArrayList();
        public List<List<Integer>> pathSum(TreeNode root, int sum) {
             ArrayList list=new ArrayList();
             if(root==null) return lists;
             sums(root,sum,list);
             return lists;
        }
        public void sums(TreeNode root, int sum, ArrayList list){
            if(root.left==null&&root.right==null&&sum-root.val==0) {list.add(root.val);lists.add(new ArrayList(list));list.remove(list.size()-1);return;}
            if(root.left==null&&root.right==null&&sum-root.val!=0) {return;}
            list.add(root.val);
            if(root.left!=null)  sums(root.left, sum-root.val,  list);
            if(root.right!=null) sums(root.right, sum-root.val, list);
            list.remove(list.size()-1);
        }
    }
    

    说实话,人家的代码性能1ms,挺好的。但是我仍然没有焕然一新的感觉。首先这是标准回溯,回溯三部曲添加,递归,删除一步没少。而我用的新list来解决这个问题,不得不说这么处理更优雅。减少了内存空间的占用。。总体来说是人能想到的,处理的比较优雅的。还不错。
    今天的笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利!生活平平安安健健康康!另外周末愉快~!

    相关文章

      网友评论

        本文标题:leetCode进阶算法题+解析(十六)

        本文链接:https://www.haomeiwen.com/subject/ekfwrhtx.html