遞歸思想和樹相關問題

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;
    }
}
複製代碼
相關文章
相關標籤/搜索