力扣剑指Offer(二)
1、重建二叉树(递归)
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return recursion(0,0,inorder.size()-1,preorder,inorder);
}
private:
TreeNode* recursion(int root,int left,int right,vector<int>& preorder, vector<int>& inorder){
if(left>right)
return nullptr;
TreeNode* p=new TreeNode(preorder[root]);
int j;
for(int i=left;i<=right;i++){
if(inorder[i]==preorder[root]){
j=i;
break;
}
}
p->left=recursion(root+1,left,j-1,preorder,inorder);
p->right=recursion(root+j-left+1,j+1,right,preorder,inorder);
return p;
}
};
优化:
构造中序序列中值和下标的哈希映射,方便递归时寻找
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
this->preorder=preorder;
for(int i=0;i<inorder.size();i++){
mp[inorder[i]]=i;
}
return recursion(0,0,inorder.size()-1);
}
private:
vector<int> preorder;
unordered_map<int,int> mp;
TreeNode* recursion(int root,int left,int right){
if(left>right)
return nullptr;
TreeNode* p=new TreeNode(preorder[root]);
int j=mp[preorder[root]];
p->left=recursion(root+1,left,j-1);
p->right=recursion(root+j-left+1,j+1,right);
return p;
}
};
2、斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:1
示例 2:
输入:n = 5
输出:5
提示:
0 <= n <= 100
方法一:动态规划
class Solution {
public:
int fib(int n) {
vector<int> dp(n+1,0);
if(n<2)
return n;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=(dp[i-1]+dp[i-2])%1000000007;
}
return dp[n];
}
};
优化:不用开数组
class Solution {
public:
int fib(int n) {
if(n<2)
return n;
int a=0;
int b=1;
int c;
for(int i=2;i<=n;i++){
c=(a+b)%1000000007;
a=b;
b=c;
}
return c;
}
};
方法二:递归(超时)
class Solution {
public:
int fib(int n) {
if(n==0)
return 0;
if(n==1)
return 1;
return (fib(n-1)+fib(n-2))%1000000007;
}
};
优化:记忆化递归法
class Solution {
public:
int b[110];
int fib(int n) {
if(n<=1)return n;
if(!b[n])return b[n]=(fib(n-1)+fib(n-2))%1000000007;
return b[n];
}
};
总结
-
递归法:
-
原理: 把 f(n)问题的计算拆分成f(n−1)和f(n−2)两个子问题的计算,并递归,以f(0)和 f(1)为终止条件。
-
缺点: 大量重复的递归计算,例如f(n)和f(n−1)两者向下递归需要各自计算f(n−2)的值。
-
-
记忆化递归法:
-
原理: 在递归法的基础上,新建一个长度为n的数组,用于在递归时存储f(0)至f(n)的数字值,重复遇到某数字则直接从数组取用,避免了重复的递归计算。
-
缺点: 记忆化存储需要使用 O(N)的额外空间。
-
-
动态规划:
- 原理: 以斐波那契数列性质f(n+1)=f(n)+f(n-1)f(n+1)=f(n)+f(n−1)为转移方程。
从计算效率、空间复杂度上看,动态规划是本题的最佳解法。

浙公网安备 33010602011771号