1. 题目
2. 解答
2.1. 动态规划
我们定义状态 state[i] 表示以 nums[i] 为结尾元素的最长上升子序列的长度,那么状态转移方程为:
[state[i] = max(state[j] + 1) space 如果 space nums[i] > nums[j], 0 leqslant j < i
]
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> state(n, 1);
for (int i = 1; i < n; i++)
{
for (int j = i-1; j >= 0; j--)
{
if (nums[i] > nums[j])
state[i] = max(state[i], state[j]+1);
}
}
int ret = 0;
for (int i = 0; i < n; i++)
ret = max(ret, state[i]);
return ret;
}
};
易知上面代码的时间复杂度为 (O(n^2))。
2.2. 贪心思想
我们用一个列表 result 来放置我们的最长上升子序列,然后向后遍历数组,如果 nums[i] > result[-1],说明当前元素应该被放进列表中去,因为这样就能组成一个更长的上升子序列;如果 nums[i] < result[-1],说明目前的上升子序列里面有大于当前元素的数据,贪心思想是说让我们用当前元素去替换掉上升子序列里面第一个大于等于当前元素的数,这样我们就可以有更大的操作空间去向 result 里面放更多的元素,从而形成更长的上升子序列。
比如 nums=[10,9,2,5,3,7,101,18],算法过程如下所示:
[result = [10] \
result = [9] gets 9 < 10 \
result = [2] gets 2 < 9 \
result = [2, 5] gets 5 > 2 \
result = [2, 3] gets 3 < 5 \
result = [2, 3, 7] gets 7 > 3 \
result = [2, 3, 7, 101] gets 101 > 7 \
result = [2, 3, 7, 18] gets 18 < 101 \
]
class Solution:
def binary_search(self, result, data):
n = len(result)
i, j = 0, n-1
while i <= j:
mid = i + (j - i) // 2
if result[mid] >= data:
if mid == 0 or result[mid-1] < data:
return mid
else:
j = mid - 1
else:
i = mid + 1
return -1
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
if n == 0: return 0
result = [nums[0]]
for i in range(1, n):
if nums[i] > result[-1]:
result.append(nums[i])
elif nums[i] < result[-1]:
#在result中找到第一个大于等于nums[i]的元素位置
pos = self.binary_search(result, nums[i])
result[pos] = nums[i]
return len(result)
循环需要 (n) 次,二分查找复杂度为 (O(logn)),所以总体代码的时间复杂度为 (O(nlogn))。
获取更多精彩,请关注「seniusen」!