树
标签(空格分隔): 面试
二叉树的遍历
深度优先搜索
递归法
递归的三要素:
1. 递归的定义:做了什么事情,接收了什么参数,返回了什么值
2. 拆分为规模更小的相同问题
3. 一定有一种可以退出程序的情况;
递归法有可能爆栈
public class Solution {
public ArrayList<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> result = new ArrayList<>();
traversal(root,result);
return result;
}
// 递归的定义,把root为根的树preorder加入里面
private void traversal(TreeNode root,ArrayList<Integer> result) {
if (root == null) {
return;
}
result.add(root.val);
traversal(root.left,result);
traversal(root.right,result);
}
}
分治递归:分–合 要把两个子问题的结果合并在一起
public class Solution {
public ArrayList<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> result = new ArrayList<>();
if (root == null) {
return;
}
// 分
ArrayList<Integer> left = preorderTraversal(root.left);
ArrayList<Integer> right = preorderTraversal(root.right);
// 合
result.add(root.val);
result.addAll(left);
result.addAll(right);
return result;
}
}
非递归
非递归算法,需要栈来存储树的节点,需要一个队列来存储返回的值。
需要注意的一点,对于前序遍历,顺序是“根左右”,那么压栈的时候,是先将右节点插入,后将左节点插入。这样取出来才是“根左右”。即压栈和顺序是相反的。
public class Solution {
public ArrayList<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<Tree>();
List<Integer> preorder = new ArrayList<Integer>();
if (root == null) {
return preorder;
}
stack.push(root);
while (!stack.empty) {
TreeNode node = stack.pop();
preporder.add(node.val);
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return preorder;
}
}
中序遍历非递归
中序遍历的非递归算法。左右根
public class Solution {
public ArrayList<Integer> inorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
ArrayList<Integer> result = new ArrayList<>();
// 将左孩子入栈,我都直到找到最左的叶子节点
while (root != null) {
stack.push(root);
root = root.left;
}
while (!stack.isEmpty()) {
// 取出最下的左子树,将其值存入队列中
TreeNode node = stack.peek();
result.add(node.val);
// 如果最左子树没有右子树,说明它是叶子节点,取出它并取出其根节点
if (node.right == null) {
node = stack.pop();
// 当前节点的根节点,是栈中的上一个点
// 但是要判断根节点的左子树是不是当前节点
while (!stack.isEmpty() && stack.peek().right == node) {
node = stack.pop();
}
} else {
// 如果最左子树有右子树,说明这个最左子树是个根节点,并已经处理过了
// 压入这个右子树根节点
node = node.right;
while (node != null) {
stack.push(node);
node = node.left;
}
}
}
return result;
}
}
二叉树的最大深度
分治
public int maxDepth(TreeNode root) {
// write your code here
// 处理特殊情况
if (root == null)
return 0;
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth,rightDepth) + 1;
}
平衡二叉树 AVL
平衡二叉树定义(AVL):它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。
平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1
平衡二叉树是在二叉排序树(BST)上引入的,就是为了解决二叉排序树的不平衡性导致时间复杂度大大下降。那么AVL就保持住了(BST)的最好时间复杂度O(logn),所以每次的插入和删除都要确保二叉树的平衡
判断一个树是否为平衡二叉树
使用分治法
// 一个结果集,即包括是否是平衡二叉树,又包含最大深度
class ResultType {
public boolean isBalanced;
public int maxDepth;
public ResultType(boolean isBalanced, int maxDepth) {
this.isBalanced = isBalanced;
this.maxDepth = maxDepth;
}
}
public class Solution {
/**
* @param root: The root of binary tree.
* @return: True if this Binary tree is Balanced, or false.
*/
public boolean isBalanced(TreeNode root) {
return helper(root).isBalanced;
}
// helper函数处理逻辑,返回一个结果集
private ResultType helper(TreeNode root) {
if (root == null) {
return new ResultType(true, 0);
}
// 分成左右两个子树
ResultType left = helper(root.left);
ResultType right = helper(root.right);
// 如果有一个子树不平衡,则不平衡
if (!left.isBalanced || !right.isBalanced) {
return new ResultType(false, -1);
}
// 左右子树的高度差>1不平衡
if (Math.abs(left.maxDepth - right.maxDepth) > 1) {
return new ResultType(false, -1);
}
// 返回结果集:平衡且最大深度+1
return new ResultType(true, Math.max(left.maxDepth, right.maxDepth) + 1);
}
}
最近公共祖先
使用分治法:
在root为跟的二叉树中找A,B的LCA:
- 如果找到了就直接返回这个LCA
- 如果只找到了n1,就返回n1
- 如果只找到了n2,就返回n2
- 如果都没有,就返回null
public class Solution {
/*
* @param root: The root of the binary search tree.
* @param A: A TreeNode in a Binary.
* @param B: A TreeNode in a Binary.
* @return: Return the least common ancestor(LCA) of the two nodes.
*/
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) {
// write your code here
if (root == null) {
return null;
}
if (root == A || root == B) {
return root;
}
// 分
TreeNode left = lowestCommonAncestor(root.left,A,B);
TreeNode right = lowestCommonAncestor(root.right,A,B);
// 合
if (left != null && right != null) {
return root;
}
if (left != null) {
return left;
}
if (right != null) {
return right;
}
return null;
}
}
宽度优先搜索算法
public class Solution {
/*
* @param root: A Tree
* @return: Level order a list of lists of integer
*/
public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
// write your code here
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
if (root == null) {
return result;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
ArrayList<Integer> currentLevel = new ArrayList<Integer>();
for (int i = 0; i < size; i++) {
TreeNode head = queue.poll();
currentLevel.add(head.val);
if (head.left != null) {
queue.offer(head.left);
}
if (head.right != null) {
queue.offer(head.right);
}
}
result.add(currentLevel);
}
return result;
}
}