携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情
题目描述
如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:
n >= 3 对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_{i+2} 给定一个严格递增的正整数数组形成序列 arr ,找到 arr 中最长的斐波那契式的子序列的长度。如果一个不存在,返回 0 。
(回想一下,子序列是从原序列 arr 中派生出来的,它从 arr 中删掉任意数量的元素(也可以不删),而不改变其余元素的顺序。例如, [3, 5, 8] 是 [3, 4, 5, 6, 7, 8] 的一个子序列)
示例 1:
输入: arr = [1,2,3,4,5,6,7,8]
输出: 5
解释: 最长的斐波那契式子序列为 [1,2,3,5,8] 。
示例 2:
输入: arr = [1,3,7,11,12,14,18]
输出: 3
解释: 最长的斐波那契式子序列有 [1,11,12]、[3,11,14] 以及 [7,11,18] 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析
- 今天的算法题目是斐波那契式数组题目,斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)。
- 理解了斐波那契数列的计算方式之后,题目要求求出最长的斐波那契式的子序列的长度,子序列不需要连续。
- 首先可以想到的是朴素解法,我们遍历数组的中每一个元素,先确定两个元素,在按照斐波那契数列计算规则,计算下一个数值,如果下一个数值在给出的数组中,则长度至少为3。在查找元素的时候,我们可以使用 hashMap 数据结构,提升查询效率。
- 在朴素解法中,由于是双重循环遍历,有多余的重复计算。为了提升计算效率,我们可以采用记忆化的方式,缓存之前计算的结果。定义二维数组 memo, 缓存计算结果。memo[i][j] 表示 i, j 位置的斐波那契式数子序列的长度。具体实现代码如下,供参考。
通过代码
class Solution {
public int lenLongestFibSubseq(int[] arr) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int n = arr.length;
for (int i = 0; i < n; i++) {
map.put(arr[i], i);
}
int ans = 0;
int[][] memo = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int cnt = dfs(map, arr, i, j, memo);
if (cnt >= 3) {
ans = Math.max(cnt, ans);
}
}
}
return ans;
}
private int dfs(Map<Integer, Integer> map, int[] arr, int i, int j, int[][] memo){
if (memo[i][j] > 0) {
return memo[i][j];
}
memo[i][j] = 2;
int next = arr[i] + arr[j];
if (map.containsKey(next)) {
memo[i][j] = 1 + dfs(map, arr, j, map.get(next), memo);
}
return memo[i][j];
}
}
总结
- 上述算法的时间复杂度是O(n * n), 空间复杂度是O(n)
- 坚持算法每日一题,加油!