题目描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
题目来源:https://leetcode-cn.com/problems/longest-increasing-subsequence
解题思路
本题目解题思路有多种,在此一一列举。
- 基于深度搜索遍历所有的上升子序列,找到最长的子序列。
这种方法比较简单,但是由于复杂度过高,不通过。// 在子序列前一个元素为 pre时,nums 从 cur 到 末尾所能找到的最长上升子序列长度。 int lengthLTS(int pre,int cur,vector<int>& nums) { if(cur == nums.size()) return 0; int take = 0; if(nums[cur] > pre) // 如果符合条件,才将此元素加入到上升子序列中 { take = lengthLTS(nums[cur],cur + 1,nums) + 1; } int nottake = lengthLTS(pre,cur + 1,nums); return max(take,nottake); // 返回两者最大值 } int lengthOfLIS(vector<int>& nums) { return lengthLTS(INT_MIN,0,nums); // 这里是假定给定数组中最小元素值大于 INT_MIN }
- 基于动态规划求解
本题目很容易写出状态表示——f[i] 表示以 nums[i] 元素为结尾的最长上升子序列长度。
状态转移: f [ i ] = f [ j ] + 1 ( n u m s [ j ] < n u m s [ i ] ) f[i] = f[j] + 1 (nums[j] < nums[i]) f[i]=f[j]+1(nums[j]<nums[i])
状态初始化: f [ i ] = 1 ( i ∈ { 0 , n u m s . s i z e ( ) − 1 } ) f[i] = 1 (i \in \{0,nums.size() - 1\}) f[i]=1(i∈{0,nums.size()−1})int lengthOfLIS(vector<int>& nums) { int n = nums.size(); vector<int> f(n,1); for(int i = 0;i < n;i++) for(int j = 0;j < i;j++) { if(nums[j] < nums[i]) { f[i] = max(f[i],f[j] + 1); } } int result = 0; for(int i = 0;i < n;i++) { result = max(result,f[i]); } return result; }
- 基于二分搜索求解
这种方法的思想十分巧妙,它将该题目转换为一个可以基于二分搜索解决的题目。
该种方法定义一个数组 q,q[i] 表示上升子序列长度为 i 时采取的最小元素值 (假定存在两个上升子序列分别为 {1,4}、{1,2},那么 q[2] = 2)。
容易得知,q[i] 是一个单调递增的数组。
向该数组中添加一个新的元素,有两种情况。其一,如果该元素值大于 q 数组中最后一个元素的值,表明存在一个更长的上升子序列,应该向当前数组最后添加这一元素。其二,如果该元素值小于 q 数组中最后一个元素的值,首先找到它在该数组中的位置,然后修改符合上升子序列长度为 i 时采取的最小元素值。int lengthOfLIS(vector<int>& nums) { int n = nums.size(); vector<int> q(n + 1); int index = 1; // 从上升子序列长度为1的开始 for(int i = 0;i < n;i++) { if(index == 1 || nums[i] > q[index - 1]) // 如果上升子序列长度为1,或者值大于大于当前 q中最后一个元素 { q[index++] = nums[i]; continue; } // 基于二分搜索,调整相关上升子序列的最小元素值 int l = 1,r = index - 1; while(l < r) { int mid = l + r >> 1; if(q[mid] >= nums[i]) r = mid; else l = mid + 1; } q[l] = nums[i]; } return index - 1; }