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;
}
}
总结
未完待续