树-leetcode做题笔记
总结:对于二叉树的常见问题:
当涉及到按层序遍历,对二叉树进行各种操作时,常常采用栈或队列来对每一层数据进行保存
收获:对做题时常用的结构的方法进行了一下简要总结
1.Queue
Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构
Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接 口。
下表显示了jdk1.5中的阻塞队列的操作:
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
remove、element、offer 、poll、peek 其实是属于Queue接口。
在日常做题时,能使用offer(),就不使用add()
2.ArrayList
ArrayList是Java集合框架中的一个重要的类,它继承于AbstractList,实现了List接口,是一个长度可变的集合,提供了增删改查的功能。集合中允许null的存在。ArrayList类还是实现了RandomAccess接口,可以对元素进行快速访问。实现了Serializable接口,说明ArrayList可以被序列化,还有Cloneable接口,可以被复制。和Vector不同的是,ArrayList不是线程安全的。
size() 函数返回集合中元素的数量;
isEmpty() 函数返回集合是否为空,检查size是否为0,即使容量不为0(没有元素)
contains(Object o) 检查集合中是否包含给定的元素
indexOf(Object o) 函数返回集合中给定元素的第一次出现的位置,如果没有就返回-1
lastIndexOf(Object o) 函数返回给定元素最后一次出现的位置,如果没有就返回-1:
toArray() 将集合转化为数组;
get(int index) 返回指定位置的元素
set(int index,E element) 设置给定位置的元素为给定的元素,然后返回原来的元素
add(E e) 添加元素;
add(int index,E element) 在指定位置添加元素
remove(int index) 删除指定位置的元素,然后返回这个元素
addAll(Collection<? extends E> c) 添加给定集合中的所有元素到集合中,从末尾开始添加
removeRange(int fromIndex,int toIndex) 删除给定范围内的所有元素
3.LinkedList
ArrayList基于动态数组的实现,它长于随机访问元素,但是在中间插入和移除元素时较慢
LinkedList基于链表实现,在List中间进行插入和删除的代价较低,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。
支持的基础操作
getFirst()
getLast()
removeFirst()
removeLast()
addFirst()
addLast()
本题要用到的双端队列
public boolean offerFirst(E e) { addFirst(e); return true; } public boolean offerLast(E e) { addLast(e); return true; } public E peekFirst() { if (size == 0) return null; return getFirst(); } public E peekLast() { if (size == 0) return null; return getLast(); } public E pollFirst() { if (size == 0) return null; return removeFirst(); } public E pollLast() { if (size == 0) return null; return removeLast(); }
题目如下(按之字形层序遍历)
Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between).
For example:
Given binary tree{3,9,20,#,#,15,7},
3 / \ 9 20 / \ 15 7
return its zigzag level order traversal as:
[ [3], [20,9], [15,7] ]
对于这类问题,首先想到的是利用中序遍历来达到层序遍历的结果,然后依次对于间隔行进行翻转
1 import java.util.*; 2 public class Solution { 3 public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) { 4 ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>(); 5 6 Queue<TreeNode> queue=new LinkedList<>(); 7 if(root==null) return res; 8 queue.offer(root); 9 ArrayList<TreeNode> tmp=new ArrayList<>(); 10 int k=0; 11 while(!queue.isEmpty()){ 12 int size=queue.size(); 13 ArrayList<Integer> list=new ArrayList<>(); 14 15 for(int i=0;i<size;i++){ 16 TreeNode node =queue.poll(); 17 tmp.add(node); 18 list.add(node.val); 19 if(node.left!=null) queue.offer(node.left); 20 if(node.right!=null) queue.offer(node.right); 21 } 22 if(k%2==1){ 23 Collections.reverse(list); 24 } 25 26 k++; 27 res.add(list); 28 } 29 30 return res; 31 } 32 }
如果熟悉ArrayList的add(int index,E element) 函数,也可以利用将元素直接添加到list位置0处以替换上面代码中对应列表的翻转,如下:
1 import java.util.*; 2 public class Solution { 3 public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) { 4 ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>(); 5 6 Queue<TreeNode> queue=new LinkedList<>(); 7 if(root==null) return res; 8 queue.offer(root); 9 ArrayList<TreeNode> tmp=new ArrayList<>(); 10 int k=0; 11 while(!queue.isEmpty()){ 12 int size=queue.size(); 13 ArrayList<Integer> list=new ArrayList<>(); 14 15 for(int i=0;i<size;i++){ 16 TreeNode node =queue.poll(); 17 tmp.add(node); 18 if(k%2==0) list.add(node.val); 19 if(k%2==1) list.add(0,node.val); 20 if(node.left!=null) queue.offer(node.left); 21 if(node.right!=null) queue.offer(node.right); 22 } 23 24 25 k++; 26 res.add(list); 27 } 28 29 return res; 30 } 31 }
如果,我们将整个操作仔细细想一下,发现也可以用两个栈相互配合,交替使用,依次保存本行和下一行的数据,也可以达到同样的效果,只是整个操作没有前面方法方便
1 import java.util.*; 2 public class Solution { 3 public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) { 4 5 TreeNode c = root; 6 ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>(); 7 if (c == null) 8 return res; 9 Stack<TreeNode> s1 = new Stack<TreeNode>(); 10 Stack<TreeNode> s2 = new Stack<TreeNode>(); 11 s1.push(root); 12 while (!s1.isEmpty() || !s2.isEmpty()) { 13 ArrayList<Integer> tmp = new ArrayList<Integer>(); 14 while (!s1.isEmpty()) { 15 //先用栈1保存第一行的数据,然后将下一行的节点数据保存到栈2 16 c = s1.pop(); 17 tmp.add(c.val); 18 if (c.left != null) 19 s2.push(c.left); 20 if (c.right != null) 21 s2.push(c.right); 22 } 23 res.add(tmp); 24 //将第一行的数据保存 25 tmp = new ArrayList<Integer>(); 26 while (!s2.isEmpty()) { 27 //将栈2中的依次数据出栈,然后将其子节点推入栈1 28 //需要注意的是子节点入栈的顺序,因为是之字形遍历,所以对于上面的要从左到右的入栈, 29 //对于此处要从右到左的入栈 30 c = s2.pop(); 31 tmp.add(c.val); 32 if (c.right != null) 33 s1.push(c.right); 34 if (c.left != null) 35 s1.push(c.left); 36 } 37 if (!tmp.isEmpty()) 38 res.add(tmp); 39 } 40 return res; 41 } 42 }
既然可以用两个栈来完成,那么也可以用队列再来试试,但显然用两个队列是不行的,但是我们可以使用一个双向队列来进行操作,同样AC
1 import java.util.*; 2 public class Solution { 3 public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) { 4 5 TreeNode c = root; 6 boolean flag=true; 7 ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>(); 8 if (c == null) return res; 9 LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); 10 queue.offerFirst(c); 11 while(!queue.isEmpty()){ 12 ArrayList<Integer> list=new ArrayList<>(); 13 int size=queue.size(); 14 if(flag){ 15 for(int i=0;i<size;i++){ 16 TreeNode node=queue.pollFirst(); 17 list.add(node.val); 18 19 if(node.left!=null) queue.offerLast(node.left); 20 if(node.right!=null) queue.offerLast(node.right); } 21 res.add(list); 22 }else{ 23 for(int i=0;i<size;i++){ 24 TreeNode node=queue.pollLast(); 25 list.add(node.val); 26 27 if(node.right!=null) queue.offerFirst(node.right); 28 if(node.left!=null) queue.offerFirst(node.left); 29 } 30 res.add(list); 31 32 } 33 flag=!flag; 34 } 35 return res; 36 } 37 } 38
资料参考: