递归思想和树相关问题

1. 排列硬币

题目:你总共有 n 枚硬币,你须要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。java

给定一个数字 n,找出可造成完整阶梯行的总行数。n 是一个非负整数,而且在32位有符号整型的范围内。node

示例 1:算法

n = 5数组

硬币可排列成如下几行: ¤ ¤ ¤ ¤ ¤markdown

由于第三行不完整,因此返回2. 示例 2:函数

n = 8oop

硬币可排列成如下几行: ¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤spa

思路一:rest

等差数列通项公式、求和公式code

  • formula
  • formula

已知等差数列的和Sn,首项a1=1,d=1,求n

public int arrangeCoins(int n) {
      return (int) (-1 + Math.sqrt(1 + 8 * (long) n)) / 2;
    }
复制代码

2.[Pow(x, n)]

题目:实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。

示例 1:

输入:x = 2.00000, n = 10 输出:1024.00000 示例 2:

输入:x = 2.10000, n = 3 输出:9.26100 示例 3:

输入:x = 2.00000, n = -2 输出:0.25000 解释:2-2 = 1/22 = 1/4 = 0.25

public static double myPow(double x, int n) {
    if(n==0)  return 1;
    if(n==1)  return x;
    if(n==-1) return 1/x;

    double half=myPow(x,n/2);
    double rest=myPow(x,n%2);
    return half*half*rest;
}
复制代码

3. 二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例: 给定二叉树 [3,9,20,null,null,15,7],

3
复制代码

/
9 20 /
15 7 返回它的最大深度 3 。

public static int maxDepth(TreeNode root) {
    if(root == null){
        return 0;
    }

    int left = maxDepth(root.left);
    int right = maxDepth(root.right);
    return (left > right?left:right)+1;
}
复制代码

4. 对称二叉树

题目: 请实现一个函数,用来判断一颗二叉树是否是对称的。注意,若是一个二叉树同此二叉树的镜像是一样的,定义其为对称的。

思路:首先根节点以及其左右子树,左子树的左子树和右子树的右子树相同,左子树的右子树和右子树的左子树相同便可,采用递归,另外非递归也可,采用栈或队列存取各级子树根节点。

方法一:

boolean isSymmetrical(TreeNode pRoot)
{
    if(pRoot==null) return true;
    return isSymmetrical(pRoot.left,pRoot.right);
}
private boolean isSymmetrical(TreeNode left, TreeNode right) {
    if(left==null&&right==null) return true;
    if(left==null||right==null) return false;
    if(left.data==right.data)
        return isSymmetrical(left.left, right.right)&&isSymmetrical(left.right, right.left);
    return false;
}
复制代码

5. 二叉搜索树最小距离

题目:给定一个二叉搜索树的根节点 root,返回树中任意两节点的差的最小值。

示例:

输入: root = [4,2,6,1,3,null,null] 输出: 1 解释: 注意,root是树节点对象(TreeNode object),而不是数组。

给定的树 [4,2,6,1,3,null,null] 可表示为下图:

4
    /   \
  2      6
 / \    
1   3  
复制代码

最小的差值是 1, 它是节点1和节点2的差值, 也是节点3和节点2的差值。

定义: 二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具备下列性质的二叉树: 若它的左子树不空,则左子树上全部结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上全部结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树

class Solution {
    Integer prev = null, ans = Integer.MAX_VALUE;

    public int minDiffInBST(TreeNode root) {
        test(root);
        return ans;
    }

    public void test(TreeNode treeNode) {
        if (treeNode == null) {
            return;
        }
        test(treeNode.left);
        if (prev != null) {
            ans = Math.min(ans, treeNode.val - prev);
        }
        prev = treeNode.val;
        test(treeNode.right);
    }
}
复制代码

6. 打印二叉树的全部路径

给定一个二叉树,返回全部从根节点到叶子节点的路径。

img

算法分析:

  1. 假设起始节点为1;先搜索1节点的右子节点为2,此时将1节点置为已搜索状态;判断2节点是否为已搜索状态,若未搜索,则将2节点置为已搜索状态;每次都优先搜索判断右侧邻接节点;
  2. 如图所示,若此时已搜索至2节点,发现右子节点为null故递归至上一个节点2搜索左子节点5,继续搜索判断5节点无左右子节点,故递归返回至1节点
  3. 此时搜索判断1节点的左侧邻接节点3不为已搜索状态,故继续进行先右侧后左侧邻接节点搜索判断操做;
  4. 直至最后一个节点3不存在左右子节点,故递归至最初的1节点;判断结束,搜索结束
public void sreachPaths(TreeNode root, List<String> paths, String path) {

    if(root != null) {
        path += Integer.toString(root.data);
        if(root.left == null && root.right == null) {
            paths.add(path);
        }else {
            path += "->";
            sreachPaths(root.left, paths, path);
            sreachPaths(root.right, paths, path);
        }

    }
}
复制代码

7. 二叉搜索树的范围和

给定二叉搜索树的根结点 root,返回 L 和 R(含)之间的全部结点的值的和。

二叉搜索树保证具备惟一的值。

示例 1:

输入:root = [10,5,15,3,7,null,18], L = 7, R = 15 输出:32

示例 2: 输入:root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10 输出:23

提示:

树中的结点数量最多为 10000 个。 最终的答案保证小于 2^31。

题解思路

条件给出了根结点,从上往下遍历.可使用递归,来求解.重复的条件就是结点的左右子树,结束条件就是左右子树的值是否在L,R的范围内,返回结点在范围内的值的和

public int rangeSumBST(TreeNode root, int L, int R) {
    if (root == null) {
        return 0;
    }
    if (root.data < L) {
        return rangeSumBST(root.right, L, R);
    }
    if (root.data > R) {
        return rangeSumBST(root.left, L, R);
    }
    return root.data + rangeSumBST(root.left, L, R) + rangeSumBST(root.right, L, R);
}
复制代码

8. 二叉树的最近公共祖先

题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,知足 x 是 p、q 的祖先且 x 的深度尽量大(一个节点也能够是它本身的祖先)。”

例如,给定以下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1: 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 输出: 3 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2: 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 输出: 5 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。由于根据定义最近公共祖先节点能够为节点自己。

说明: 全部节点的值都是惟一的。 p、q 为不一样节点且均存在于给定的二叉树中。

思路:

  • 一、在左、右子树中分别查找是否包含p或q,若是(两种状况:左子树包含p,右子树包含q/左子树包含q,右子树包含p),

  • 那么此时的根节点就是最近公共祖先

  • 二、若是左子树包含p和q,那么到root->left中查找,最近公共祖先在左子树里面

  • 三、若是右子树包含p和q,那么到root->right中查找,最近公共祖先在右子树里面

  • 四、注意:不可能left和right的返回值同时都是nullptr

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || p == root || q == root) {
            return root;
        }
    
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
    
        if (left!=null && right!=null) {
            return root;
        }
    
        return left == null ? right : left;
    }
    复制代码

思路二(非递归):

public TreeNode lowestCommonAncestorII(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null || p == root || q == root) {
        return root;
    }

    List<TreeNode> pPath = findPath(root, p);
    List<TreeNode> qPath = findPath(root, q);

    TreeNode common = null;
    for (int i=0, j=0; i<pPath.size() && j<qPath.size(); i++,j++) {
        if (pPath.get(i) == qPath.get(j)) {
            common = pPath.get(i);
        }
    }

    return common;
}

private List<TreeNode> findPath(TreeNode root, TreeNode node) {
    List<TreeNode> path = new ArrayList<>();
    dfs(root, node, new ArrayList<>(), path);
    return path;
}

private void dfs(TreeNode root, TreeNode node, List<TreeNode> tmp, List<TreeNode> path) {
    if (root == null) {
        return;
    }

    tmp.add(root);

    if (root == node) {
        path.addAll(new ArrayList<>(tmp));
    }

    dfs(root.left, node, tmp, path);
    dfs(root.right, node, tmp, path);

    tmp.remove(tmp.size()-1);
}
复制代码

9. 跳台阶

​ 一只青蛙一次能够跳上1级台阶,也能够跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(前后次序不一样算不一样的结果)。

​ 考虑使用 斐波那契数列(递归)

方法一:使用递归的思想

public class Solution {
    public int JumpFloor(int target) {
        if(target<1)
            return 0;
        if(target==1)
            return 1;
        if(target==2)
            return 2;
        return JumpFloor(target-1)+ JumpFloor(target-2);
    }
}
复制代码

方法二:使用迭代的思想

public class Solution {
    public int JumpFloor(int target) {
        int f1=1,f2=2;
        while(--target>0){
            f2=f2+f1;
            f1=f2-f1;
        }
        return f1;
    }
}
复制代码
相关文章
相关标签/搜索