class Solution {
public List preorderTraversal(TreeNode root) {
List list = new ArrayList<>();
preorder(list,root);
return list;
}
public void preorder(List list,TreeNode root){
if(root == null){
return ;
}
list.add(root.val);
preorder(list,root.left);
preorder(list,root.right);
}
}
class Solution {
public List inorderTraversal(TreeNode root) {
List list = new ArrayList<>();
inorder(list, root);
return list;
}
public void inorder(List list, TreeNode root) {
if (root == null) {
return;
}
inorder(list, root.left);
list.add(root.val);
inorder(list, root.right);
}
}
class Solution {
public List postorderTraversal(TreeNode root) {
List list = new ArrayList<>();
postorder(list,root);
return list;
}
public void postorder(List list,TreeNode root){
if(root == null){
return ;
}
postorder(list,root.left);
postorder(list,root.right);
list.add(root.val);
}
}
使用递归实现树的遍历可以看出前中后序遍历还是挺像的,区别是消费节点的时机不同,例如前序遍历是先消费节点再对左右节点递归处理。
class Solution {
public List preorderTraversal(TreeNode root) {
List list = new ArrayList<>();
if(root == null){
return list;
}
Stack stack = new Stack<>();
stack.push(root);
TreeNode cur = root;
while(cur != null && !stack.isEmpty()){
TreeNode node = stack.pop();
list.add(node.val);
if(node.right != null){
stack.push(node.right);
}
if(node.left != null){
stack.push(node.left);
}
}
return list;
}
}
class Solution {
public List inorderTraversal(TreeNode root) {
List res = new ArrayList<>();
if(root == null){
return res;
}
Stack stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
if(cur!=null){
stack.push(cur);
cur = cur.left;
}else {
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
}
return res;
}
}
class Solution {
public List postorderTraversal(TreeNode root) {
List res = new ArrayList<>();
if(root == null){
return res;
}
Stack stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
res.add(node.val);
if(node.left != null){
stack.push(node.left);
}
if(node.right != null){
stack.push(node.right);
}
}
Collections.reverse(res);
return res;
}
}
迭代实现与递归实现非常不同,迭代需要使用while循环遍历整个树,除此之外,还需要容器来保存节点,延迟消费,即访问和消费不在同一时间,这里根据特性使用栈(先进后出)。首先把根节点放入栈中,消费栈中的根节点依次将右节点和左节点放入栈中。这里先将右节点放入栈中,再把左节点放入栈中,目的是消费弹出的时候先弹出左节点再弹出右节点,符合先序的特点(头左右)。
后序与先序的特点一样,这里先讲后序的思路。后序的特点是左右头,但是由于栈先进后出的特性,头进入栈中就一定比左右孩子先消费(头不出来,左右孩子进不了栈中,更不可能被消费)。所以我这里的思路还是先消费头节点,即使用的顺序是头右左,但是最后对数组进行反序。那么这个时候与前序的区别只有两点,先左孩子入栈,再右孩子入栈,最终结果反序。
中序与前序后序不同,头节点如何访问与消费都无法满足中序的特点。中序的思路:遍历的过程中一值往左孩子走,并将左孩子压入栈中,当当前节点为null时说明左节点已经走到头了,从栈中弹出一个节点(最近的左孩子(如果有左孩子的话)),消费该节点,再将该节点的右孩子压入栈中。重复该行为,总结就是有左孩子就往左走并压入栈中,没有左孩子就消费该节点再将其右孩子压入栈中。
class Solution {
public List> levelOrder(TreeNode root) {
List> res = new ArrayList>();
if (root == null) {
return res;
}
Queue que = new LinkedList();
que.offer(root);
while (!que.isEmpty()) {
List itemList = new ArrayList();
int len = que.size();
while (len > 0) {
TreeNode tmpNode = que.poll();
itemList.add(tmpNode.val);
if (tmpNode.left != null) {
que.offer(tmpNode.left);
}
if (tmpNode.right != null) {
que.offer(tmpNode.right);
}
len --;
}
res.add(itemList);
}
return res;
}
}
class Solution {
public List> levelOrderBottom(TreeNode root) {
List> res = new ArrayList>();
if (root == null) {
return res;
}
Queue que = new LinkedList();
que.offer(root);
while (!que.isEmpty()) {
List itemList = new ArrayList();
int len = que.size();
while (len > 0) {
TreeNode tmpNode = que.poll();
itemList.add(tmpNode.val);
if (tmpNode.left != null) {
que.offer(tmpNode.left);
}
if (tmpNode.right != null) {
que.offer(tmpNode.right);
}
len --;
}
res.add(itemList);
}
List> resList = new ArrayList>();
for (int i = res.size() - 1; i >= 0; i-- ) {
resList.add(res.get(i));
}
return resList;
}
}
class Solution {
public List rightSideView(TreeNode root) {
List res = new ArrayList<>();
if (root == null) {
return res;
}
Queue que = new LinkedList();
que.offer(root);
while (!que.isEmpty()) {
int len = que.size();
while (len > 0) {
TreeNode tmpNode = que.poll();
if(tmpNode.left != null) que.offer(tmpNode.left);
if(tmpNode.right != null) que.offer(tmpNode.right);
len --;
if(len == 0) res.add(tmpNode.val);
}
}
return res;
}
}
class Solution {
public List averageOfLevels(TreeNode root) {
List resList = new ArrayList<>();
if(root == null){
return resList;
}
Queue que = new LinkedList();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
int num = len;
Double sum = 0.0;
while(len > 0){
TreeNode tmpNode = que.poll();
sum += tmpNode.val;
if(tmpNode.left != null) que.offer(tmpNode.left);
if(tmpNode.right != null) que.offer(tmpNode.right);
len --;
}
Double res = sum / num;
resList.add(res);
}
return resList;
}
}
class Solution {
public List> levelOrder(Node root) {
List> resList = new ArrayList>();
if(root == null){
return resList;
}
Queue que = new LinkedList();
que.offer(root);
while(!que.isEmpty()){
List res = new ArrayList();
int len = que.size();
while(len > 0){
Node tmpNode = que.poll();
res.add(tmpNode.val);
List children = tmpNode.children;
if(children == null){
continue;
}
for(Node node : children){
if(node != null){
que.offer(node);
}
}
len --;
}
resList.add(res);
}
return resList;
}
}
class Solution {
public List largestValues(TreeNode root) {
List resList = new ArrayList<>();
Queue que = new LinkedList();
if(root == null) return resList;
que.offer(root);
while(!que.isEmpty()){
int cur_max = - Integer.MAX_VALUE - 1;
int len = que.size();
while(len > 0){
TreeNode tmpNode = que.poll();
cur_max = Math.max(cur_max,tmpNode.val);
if(tmpNode.left != null) que.offer(tmpNode.left);
if(tmpNode.right != null) que.offer(tmpNode.right);
len --;
}
resList.add(cur_max);
}
return resList;
}
}
class Solution {
public Node connect(Node root) {
if(root == null){
return root;
}
Queue que = new LinkedList();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
Node prev = null; // 前一个节点
for(int i = 0; i < len; i++){
Node curr = que.poll(); // 当前节点
// 处理当前节点的子节点
if(curr.left != null) que.offer(curr.left);
if(curr.right != null) que.offer(curr.right);
// 连接当前节点到前一个节点
if(prev != null){
prev.next = curr;
}
prev = curr;
}
// 最后一个节点的next已经默认为null,无需额外设置
}
return root;
}
}
class Solution {
public Node connect(Node root) {
if(root == null){
return root;
}
Queue que = new LinkedList();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
Node prev = null; // 前一个节点
for(int i = 0; i < len; i++){
Node curr = que.poll(); // 当前节点
// 处理当前节点的子节点
if(curr.left != null) que.offer(curr.left);
if(curr.right != null) que.offer(curr.right);
// 连接当前节点到前一个节点
if(prev != null){
prev.next = curr;
}
prev = curr;
}
// 最后一个节点的next已经默认为null,无需额外设置
}
return root;
}
}
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
Queue que = new LinkedList();
int depth = 0;
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
depth ++;
while(len > 0){
TreeNode tmpNode = que.poll();
if(tmpNode.left != null) que.offer(tmpNode.left);
if(tmpNode.right != null) que.offer(tmpNode.right);
len --;
}
}
return depth;
}
}
class Solution {
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
Queue que = new LinkedList();
int depth = 0;
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
depth ++;
while(len > 0){
TreeNode tmpNode = que.poll();
if(tmpNode.left == null && tmpNode.right == null){
return depth;
}
if(tmpNode.left != null) que.offer(tmpNode.left);
if(tmpNode.right != null) que.offer(tmpNode.right);
len --;
}
}
return depth;
}
}
这些题目的思路都一样,流程都一样,只不过对节点的消费方式不同。流程:定义一个队列,消费队列里的元素直到队列为空,在循环内,首先获得队列的大小size(),表示存在的元素个数,也代表上一层的个数。循环元素个数次,每次消费一个元素,将该元素从队列中poll出来并进行消费,如果存在左孩子则将左孩子加入队列中,如果存在右孩子则将右孩子加入队列中,直到队列为空,退出循环,返回要维护的数据。
今天的收获特别大,对树的递归遍历,迭代遍历和相关题目掌握更深了,除此之外,不仅是技术上的提升,一天写了这么多的题目,leetcode上题目完成数的增加也让我更加自信,学习更加有兴趣了。希望天天都有这么多的收获。