leetcode 二叉树简笔

LeetCode刷题记录
总结二叉树的题目


前言

总结树结构

一、二叉树的遍历

二叉树的遍历比较基本的问题了,易于理解并且是其他二叉树问题的基础之一
以二叉树的中序遍历为例:
中序遍历递归实现,这是最容易想到的了

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private List<Integer> list = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null)
        return list;
        mid(root);
        return list;
    }
    public void mid(TreeNode head)
    {
        if(head == null)
        return;
        mid(head.left);
        list.add(head.val);
        mid(head.right);
        return;
    }
}

二叉树的非递归遍历,使用栈的方法,也是比较基本的解决方案了

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
         private List<Integer> list = new ArrayList<>();
         private Stack<TreeNode> stack = new Stack<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null)
        return list;
        TreeNode cur = root;
        while(cur!=null||!stack.isEmpty())
        {
            if(cur!=null)
            {
                stack.push(cur);
                cur = cur.left;
            }
            else
            {
                cur = stack.pop();
                list.add(cur.val);
                cur = cur.right;
            }
        }
        return list;
    }
}

二叉树的非递归的中序遍历,左节点先进栈
前序遍历输出头节点信息后头节点进栈,之后是左节点进栈
后序遍历

还有一种遍历方法是莫里斯遍历,目的是找到当前节点的前驱结点,只用一个元素进行标记就好,省去了使用栈的空间复杂度。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
         private List<Integer> list = new ArrayList<>();
        // private Stack<TreeNode> stack = new Stack<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null)
        return list;
        TreeNode pre = null;
       while(root!=null)
       {
           if(root.left!=null)
           {
               pre = root.left;
               while(pre.right!=null&&pre.right!=root)
               {
                   pre = pre.right;
               }

               if(pre.right == null)
               {
                   pre.right = root;
                   root = root.left;
               }
               else
               {
                   list.add(root.val);
                   pre.right = null;
                   root= root.right;
               }
           }
           else{
               list.add(root.val);
               root = root.right;
           }
       }
        return list;
    }
}

还有一种比较精巧的颜色渲染法
使用的非递归的形式,但是理解起来和递归相同,代码模板前序,中序,后续全部一样,只是需要像递归一样调换语句的顺序罢了。
话不多说,直接上代码
(因为初学Java,python中一个栈元素两个类型的相同方法没有找到,所以定义了两个栈,实在是太蠢了。)
思想就是全体树的结点全部入栈然后做好标记。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private List<Integer> list = new ArrayList<Integer>();
    private Stack<Integer> stack = new Stack<Integer>();
    private Stack<TreeNode> _stack = new Stack<TreeNode>();
    public List<Integer> inorderTraversal(TreeNode root) {
        int white =0;
        int gray =1;
        stack.push(white);
        _stack.push(root);
        int color;
        TreeNode node;
        while(!stack.isEmpty()&&!_stack.isEmpty())
        {
            color = stack.pop();
            node = _stack.pop();
            if(node == null)
            {
                continue;
            }
            if(color ==white)
            {
                stack.push(white);
                _stack.push(node.right);
                stack.push(gray);
                _stack.push(node);
                stack.push(white);
                _stack.push(node.left);
            }
            else
            {
                list.add(node.val);
            }
        }
        return list;
    }
}

二叉树的层序遍历,比较基础的问题了,使用队列就可以实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null)
        return res;
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(root);
        while(!q.isEmpty())
        {
            List<Integer> list=new ArrayList<Integer>();
            int count = q.size();
            for(int i=0;i<count;i++)
            {
                TreeNode node = q.poll();
                list.add(node.val);
                if(node.left!=null)
                {
                    q.offer(node.left);
                }
                if(node.right!=null)
                {
                    q.offer(node.right);
                }
            }
            res.add(list);
        }
        return res;
    }
}

二叉树的锯齿形层序遍历
左右左右,头都晕了
这里总结一下对于Deque的学习
一.概述
  Deque是Queue的子接口,我们知道Queue是一种队列形式,而Deque则是双向队列,它支持从两个端点方向检索和插入元素,因此Deque既可以支持LIFO形式也可以支持LIFO形式.Deque接口是一种比Stack和Vector更为丰富的抽象数据形式,因为它同时实现了以上两者.

二.主要方法
修饰符和返回值 方法名 描述
添加功能
void push(E) 向队列头部插入一个元素,失败时抛出异常
void addFirst(E) 向队列头部插入一个元素,失败时抛出异常
void addLast(E) 向队列尾部插入一个元素,失败时抛出异常
boolean offerFirst(E) 向队列头部加入一个元素,失败时返回false
boolean offerLast(E) 向队列尾部加入一个元素,失败时返回false
获取功能
E getFirst() 获取队列头部元素,队列为空时抛出异常
E getLast() 获取队列尾部元素,队列为空时抛出异常
E peekFirst() 获取队列头部元素,队列为空时返回null
E peekLast() 获取队列尾部元素,队列为空时返回null
删除功能
boolean removeFirstOccurrence(Object) 删除第一次出现的指定元素,不存在时返回false
boolean removeLastOccurrence(Object) 删除最后一次出现的指定元素,不存在时返回false
弹出功能
E pop() 弹出队列头部元素,队列为空时抛出异常
E removeFirst() 弹出队列头部元素,队列为空时抛出异常
E removeLast() 弹出队列尾部元素,队列为空时抛出异常
E pollFirst() 弹出队列头部元素,队列为空时返回null
E pollLast() 弹出队列尾部元素,队列为空时返回null
迭代器
Iterator descendingIterator() 返回队列反向迭代器
可以看出Deque在Queue的方法上新添了对队列头尾元素的操作,add,remove,get形式的方法会在有界队列满员和空队列时抛出异常,offer,poll,peek形式的方法则会返回false或null.

此外方法表中需要注意push = addFirst,pop = removeFirst,只是使用了不同的方法名体现队列表示栈结构时的特点.

三.实现
  同Queue一样Deque的实现也可以划分成通用实现和并发实现.

通用实现主要有两个实现类ArrayDeque和LinkedList.

ArrayDeque是个可变数组,它是在Java 6之后新添加的,而LinkedList是一种链表结构的list.LinkedList要比ArrayDeque更加灵活,因为它也实现了List接口的所有操作,并且可以插入null元素,这在ArrayDeque中是不允许的.

从效率来看,ArrayDeque要比LinkedList在两端增删元素上更为高效,因为没有在节点创建删除上的开销.最适合使用LinkedList的情况是迭代队列时删除当前迭代的元素.此外LinkedList可能是在遍历元素时最差的数据结构,并且也LinkedList占用更多的内存,因为LinkedList是通过链表连接其整个队列,它的元素在内存中是随机分布的,需要通过每个节点包含的前后节点的内存地址去访问前后元素.

总体ArrayDeque要比LinkedList更优越,在大队列的测试上有3倍与LinkedList的性能,最好的是给ArrayDeque一个较大的初始化大小,以避免底层数组扩容时数据拷贝的开销.

LinkedBlockingDeque是Deque的并发实现,在队列为空的时候,它的takeFirst,takeLast会阻塞等待队列处于可用状态

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> ans = new LinkedList<List<Integer>>();
        if (root == null) {
            return ans;
        }

        Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
        nodeQueue.offer(root);
        boolean isOrderLeft = true;

        while (!nodeQueue.isEmpty()) {
            Deque<Integer> levelList = new LinkedList<Integer>();
            int size = nodeQueue.size();
            for (int i = 0; i < size; ++i) {
                TreeNode curNode = nodeQueue.poll();
                if (isOrderLeft) {
                    levelList.offerLast(curNode.val);
                } else {
                    levelList.offerFirst(curNode.val);
                }
                if (curNode.left != null) {
                    nodeQueue.offer(curNode.left);
                }
                if (curNode.right != null) {
                    nodeQueue.offer(curNode.right);
                }
            }
           // ans.add(new LinkedList<Integer>(levelList));
           ans.add(new LinkedList<Integer>(levelList));//这里做了等同于类型强制转换的操作否则会报错
            isOrderLeft = !isOrderLeft;
        }

        return ans;
    }
}

二、Leetcode二叉树面试题

1.二叉树求和路径

今天看到了一道感觉比较有意思的题目,一开始就想到了使用双递归进行求解,但是大家懂得都懂,双递归虽然比较容易理解,但是复杂度实在是太高了,所以一直在想比较简单的方法,就让这道题做了很久。先贴出双递归求解的代码吧。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int n = 0;
    public int pathSum(TreeNode root, int sum) {
        if(root == null)
        return 0;
        pathSum_private(root,sum);
        pathSum(root.left,sum);
        pathSum(root.right,sum);
        return n;
    }
    public void pathSum_private(TreeNode root,int sum)
    {
        if(root == null)
        return;
        sum = sum-root.val;
        if(sum == 0)
        n++;
        pathSum_private(root.left,sum);
        pathSum_private(root.right,sum);
        sum = sum+root.val;  //使用回溯法基本思想
    }
}

使用前缀和能达到避免重复的方法,哎,评论区人均大佬,看一看就很受益了

 public int pathSum(TreeNode root,int sum)
    {
        Map<Integer,Integer> map = new HashMap<>();//建立前缀和和路径数目的哈希表
        map.put(0,1);//前缀和为零的路径只有一条,什么都不选的路径
        return helper(root,map,sum,0);
    }
    private int helper(TreeNode node,Map<Integer,Integer> prefiSum,int sum,int curSum)
    {
        if(node == null)
        return 0;
        curSum+=node.val;
        int count = prefiSum.getOrDefault(curSum-sum,0);
        //涉及到getOrDefault方法的使用,如果map中有前面的key值,就是用key对应的value,否则使用默认的0
        prefiSum.put(curSum,prefiSum.getOrDefault(curSum,0)+1);
        count += helper(node.left,prefiSum,sum,curSum);
        count += helper(node.right,prefiSum,sum,curSum);
        prefiSum.put(curSum,prefiSum.getOrDefault(curSum,0)-1);
        return count;
                
    }
}

2.合法二叉搜索树

面试题04.05

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int flag=0;
    public boolean isValidBST(TreeNode root) {
        
       return isbst(root, Long.MIN_VALUE, Long.MAX_VALUE);
        
    }
    public boolean isbst(TreeNode root,double min,double max)
    {
        return root == null || (root.val > min && root.val < max && isbst(root.left, min, root.val) && isbst(root.right, root.val, max));
    }
}

3 特定深度节点链表

给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode[] listOfDepth(TreeNode tree) {
        if(tree == null)
        return new ListNode[0];
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(tree);
        List<ListNode> list = new ArrayList<>();
        while(!q.isEmpty())
        {
            int size = q.size();
            ListNode dhead = new ListNode(-1);
            ListNode cur = dhead;
            for(int i =0;i<size;i++)
            {
                TreeNode node = q.poll();
                cur.next = new ListNode(node.val);
                cur = cur.next;
                if(node.left!=null)
                q.offer(node.left);
                if(node.right!=null)
                q.offer(node.right);
            }
            list.add(dhead.next);
        }
        ListNode[] res = list.toArray(new ListNode[list.size()]);
        return res;
    }
}

面试题04.10,检查子树,这道题的技巧就是检查t2是否是t1的子树


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean checkSubTree(TreeNode t1, TreeNode t2) {
        if(t1==null||t2==null)
        return false;
        if(t1==null&&t2==null)
        return true;
        
        return helper(t1,t2)||checkSubTree(t1.left,t2)||checkSubTree(t1.right,t2);
    }
    public boolean helper(TreeNode t1,TreeNode t2)
    {
        if(t1==null&&t2==null)
        return true;
        if(t1==null||t2==null)
        return false;
        return t1.val == t2.val&&helper(t1.left,t2.left)&&helper(t1.right,t2.right);
    }
}

二叉树中的最大路径

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。
在这里插入代码片

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private int _max = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
       getmax(root);
       return _max;
    }
    public int getmax(TreeNode root)
    {
        if(root == null)
        return 0;
        int left = Math.max(0,getmax(root.left));
        int right = Math.max(0,getmax(root.right));

        _max = Math.max(_max,left+right+root.val);
        return Math.max(left,right)+root.val;
    }
}

总结

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值