LeetCode 热题 HOT 100:https://leetcode.cn/problem-list/2cktkvj/
题目链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
递归方式:
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
InOrder(root);
return list;
}
public void InOrder(TreeNode root){
if(root != null){
InOrder(root.left);
list.add(root.val);
InOrder(root.right);
}
}
}
非递归方式:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>(); // 双端队列模拟栈
List<Integer> list = new ArrayList<>();
TreeNode p = root;
while(p != null || !stack.isEmpty()){
if(p != null){
stack.push(p);
p = p.left;
}else{
p = stack.pop();
list.add(p.val);
p = p.right;
}
}
return list;
}
}
题目链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/description/
递归做法:
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
preOrder(root);
return list;
}
public void preOrder(TreeNode root){
if(root != null){
list.add(root.val);
preOrder(root.left);
preOrder(root.right);
}
}
}
非递归做法:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>(); // 双端队列模拟栈
List<Integer> list = new ArrayList<>();
TreeNode p = root;
while(p != null || !stack.isEmpty()){
if(p != null){
list.add(p.val);
stack.push(p);
p = p.left;
}else{
p = stack.pop();
p = p.right;
}
}
return list;
}
}
题目链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/description/
递归做法:
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
postOrder(root);
return list;
}
public void postOrder(TreeNode root){
if(root != null){
postOrder(root.left);
postOrder(root.right);
list.add(root.val);
}
}
}
非递归做法:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
TreeNode p, r;
p = root;
r = null;
while(p != null || !stack.isEmpty()){
if(p != null){ // 走到最左边
stack.push(p);
p = p.left;
}else{ // 向右
p = stack.peek(); // 得到栈顶元素
if(p.right != null && p.right != r){ // 右子树存在,且未访问
p = p.right;
}else{ // 否则弹出节点并访问
p = stack.pop();
list.add(p.val);
r = p; // 记录最近访问节点
p = null; // 节点访问后,重置 p
}
}
}
return list;
}
}
题目链接:https://leetcode.cn/problems/unique-binary-search-trees/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
动态规划:
n
个节点存在二叉搜索树的个数是 G(n)
,令 f(i)
为以 i
为根的二叉排序树的个数,则:G(n) = f(1) + f(2) + … + f(n-1) + f(n)
i
为根节点时,由于是二叉搜索树,则其左子树节点个数为 i-1
个,右子树节点为 n-i
个,得:f(i) = G(i-1) * G(n-i)
G(n) = f(1) + f(2) + … + f(n-1) + f(n)
= G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-2) * G(1) + G(n-1) * G(0)
class Solution {
public int numTrees(int n) {
int[] dp = new int[n+1]; // dp[i] 代表 i 个节点存在二叉搜索树的个数
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; i ++){
for(int j = 0; j < i; j ++){
dp[i] += dp[j]*dp[i-1-j];
}
}
return dp[n];
}
}
题目链接:https://leetcode.cn/problems/validate-binary-search-tree/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
递归中序遍历:
class Solution {
long max = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root == null){
return true;
}
if(!isValidBST(root.left)){
return false;
}
if(root.val <= max){
return false;
}else{
max = root.val;
}
return isValidBST(root.right);
}
}
非递归中序遍历:
class Solution {
public boolean isValidBST(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>(); // 双端队列模拟栈
List<Integer> list = new ArrayList<>();
TreeNode p = root;
long max = Long.MIN_VALUE;
while(p != null || !stack.isEmpty()){
if(p != null){
stack.push(p);
p = p.left;
}else{
p = stack.pop();
if (p.val <= max){
return false;
}else{
max = p.val;
}
list.add(p.val);
p = p.right;
}
}
return true;
}
}
题目链接:https://leetcode.cn/problems/symmetric-tree/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return false;
}
return recur(root.left, root.right);
}
public boolean recur(TreeNode l, TreeNode r){
if(l == null && r == null){
return true;
}
if(l == null || r == null || l.val != r.val){
return false;
}
return recur(l.left, r.right) && recur(l.right, r.left);
}
}
题目链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/description/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
双端队列模拟队列实现层序遍历:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Deque<TreeNode> queue = new LinkedList<>();
if(root != null){
queue.push(root);
}
while(!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
for(int i = queue.size(); i > 0; i --){ // // 注意避坑点:queue.size(),在出队时会发生变化,因此实现用 i 记录队列长度,倒序循环
TreeNode p = queue.remove();
list.add(p.val);
if(p.left != null){
queue.add(p.left);
}
if(p.right != null){
queue.add(p.right);
}
}
res.add(list);
}
return res;
}
}
题目链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
深搜DFS:
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
广搜BFS:
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
Deque<TreeNode> queue = new LinkedList<>();
TreeNode p = root;
int depth = 0;
queue.add(root);
while(!queue.isEmpty()){
int size = queue.size();
for(int i = queue.size(); i > 0; i --){
p = queue.remove();
if(p.left != null){
queue.add(p.left);
}
if(p.right != null){
queue.add(p.right);
}
}
depth++;
}
return depth;
}
}
题目链接:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) { // 先序,中序
return build(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);
}
public TreeNode build(int[] preorder, int lpre, int rpre, int[] inorder, int lmid, int rmid){ // 先序左边界、右边界,中序左边界、右边界
if(lmid>rmid || lpre>rpre){
return null;
}
TreeNode node = new TreeNode(preorder[lpre]);
int fa = lpre; // fa代表中序遍历数组中的根节点的索引
for(int i = lmid; i <= rmid; i ++){
if(inorder[i] == preorder[lpre]){
fa = i;
break;
}
}
int len = fa-lmid; // 左子树的长度
// 先序(根左右):左边界+1、左边界+长度,中序(左根右):左边界、根节点位置-1
node.left = build(preorder, lpre+1, lpre+len, inorder, lmid, fa-1);
// 先序(根左右):左边界+长度+1、右边界,中序(左根右):根节点位置+1、右边界
node.right = build(preorder, lpre+len+1, rpre, inorder, fa+1, rmid);
return node;
}
}
参考思路:团体程序设计天梯赛-练习集 L2-011 玩转二叉树 (25分) 先序中序建树 思路详解
题目链接:https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/description/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
原始做法: 将节点前序遍历存储在集合当中,然后对集合中的节点依次展开。
class Solution {
public void flatten(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
TreeNode p = root;
List<TreeNode> list = new LinkedList<>();
while(p!=null||!stack.isEmpty()){
if(p!=null){
stack.push(p);
list.add(p);
p = p.left;
}else{
p = stack.pop();
p = p.right;
}
}
TreeNode q = root;
for (int i = 1; i < list.size(); i++) {
q.left = null;
q.right = list.get(i);
q = list.get(i);
}
}
}
优化后做法:利用先序遍历 根左右 的特点。将根的右侧指向左子树,左子树的最右节点指向原来根的右子树,并将左子树置为空。
class Solution {
public void flatten(TreeNode root) {
TreeNode curr = root; // 当前节点指向根
while(curr != null){
if(curr.left != null){
TreeNode next = curr.left; // 左子树
TreeNode pre = next;
while(pre.right != null){ // 查询左子树的最右节点
pre = pre.right;
}
pre.right = curr.right; // 最右节点的右指针指向当前节点的右子树
curr.right = next; // 当前节点的右子树指向当前节点的左子树的根
curr.left = null; // 当前节点左子树置为空
}
curr = curr.right;
}
}
}
题目链接:https://leetcode.cn/problems/binary-tree-maximum-path-sum/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
a
/ \
b c
class Solution {
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if(root == null){
return 0;
}
dfs(root);
return max;
}
/**
* 返回经过root的单边分支最大和, 即 Math.max(root, root+left, root+right)
*/
public int dfs(TreeNode root){
if(root == null){
return 0;
}
// 计算左子树最大值,左边分支如果为负数还不如不选择
int leftMax = Math.max(0, dfs(root.left));
// 计算右子树最大值,右边分支如果为负数还不如不选择
int rightMax = Math.max(0, dfs(root.right));
// left->root->right 作为路径与已经计算过历史最大值做比较
max = Math.max(max, leftMax + root.val + rightMax);
// 返回经过root的单边最大分支给当前root的父节点计算使用
return root.val + Math.max(leftMax, rightMax);
}
}
参考:【二叉树中的最大路径和】递归,条理清晰
题目链接:https://leetcode.cn/problems/invert-binary-tree/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
递归做法:
class Solution {
public TreeNode invertTree(TreeNode root) {
overturnTree(root);
return root;
}
public void overturnTree(TreeNode root){
if(root == null){
return;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
overturnTree(root.left);
overturnTree(root.right);
}
}
非递归做法: 利用层次遍历,在每层放入队列之后,交换左右节点。
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
Deque<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode p = queue.remove();
if(p.left != null){
queue.add(p.left);
}
if(p.right != null){
queue.add(p.right);
}
TreeNode tmp = p.left;
p.left = p.right;
p.right = tmp;
}
return root;
}
}
题目链接:https://leetcode.cn/problems/convert-bst-to-greater-tree/description/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
递归做法:
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
order(root);
return root;
}
public void order(TreeNode root){
if(root!=null){
order(root.right);
sum += root.val;
root.val = sum;
order(root.left);
}
}
}
非递归做法:
class Solution {
public TreeNode convertBST(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
TreeNode p = root;
int sum = 0;
while(!stack.isEmpty() || p != null){ // 反中序遍历
if(p!=null){
stack.push(p);
p = p.right;
}else{
p = stack.pop();
sum += p.val;
p.val = sum;
p = p.left;
}
}
return root;
}
}
题目链接:https://leetcode.cn/problems/diameter-of-binary-tree/description/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
左右节点深度加一起就是最大路径:
class Solution {
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
if(root == null){
return 0;
}
maxDepth(root);
return max;
}
public int maxDepth(TreeNode root){
if(root == null){
return 0;
}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
max = Math.max(max, leftDepth + rightDepth); // 左右节点深度加一起就是最大路径
return Math.max(leftDepth, rightDepth) + 1;
}
}
题目链接:https://leetcode.cn/problems/diameter-of-binary-tree/description/?envType=featured-list&envId=2cktkvj?envType=featured-list&envId=2cktkvj
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
return merge(root1, root2);
}
public TreeNode merge(TreeNode root1, TreeNode root2){
if(root1 == null && root2 == null){
return null;
}else if(root1 == null){
return root2;
}else if(root2 == null){
return root1;
}else{
root1.val += root2.val;
root1.left = merge(root1.left, root2.left);
root1.right = merge(root1.right, root2.right);
return root1;
}
}
}