首页 > 技术文章 > LeetCode 300. 最长上升子序列(Longest Increasing Subsequence)

wmx24 2018-05-07 23:19 原文

 

题目描述

 

给出一个无序的整形数组,找到最长上升子序列的长度。

例如,

给出 [10, 9, 2, 5, 3, 7, 101, 18]
最长的上升子序列是 [2, 3, 7, 101],因此它的长度是4。因为可能会有超过一种的最长上升子序列的组合,因此你只需要输出对应的长度即可。

 

解题思路

 

用动态规划思想,考虑用一个数组dp记录到当前数字为止,可能的最长上升子序列长度,注意并不一定是当前子序列的解。这样最后返回dp数组的长度即可。具体以上述数组为例:

  • 首先把10加入到dp中,此时最长上升子序列长度为1
  • 下一个数字是9,它比dp中仅有的数字10要小,可知以9为子序列首数字的可能长度要比10长,因此用9替换10
  • 同样把2替换dp中仅有的数字9
  • 加入5时,因为5比2大,所以可以组成最长上升子序列,因此把5加入到2之后
  • 当前数字3比dp中第二个数字5要小,考虑到之后可能出现的上升序列可能小于5,因此用3替换5
  • 加入7时,因为7比dp中最后一个数字3大,所以可以组成最长上升子序列,因此把7加入到3之后
  • 同样加入101到dp
  • 加入18时,按上述规则用18替换101,最后dp数组为[2,3,7,18],因此最长上升子序列长度为4

通过以上顺序,可以总结出dp数组变化规则:

  • 若当前数字大于dp中最后一个数字,则直接插入到最后
  • 找到dp数组中第一个大于当前数字的数,并替换为当前数字
  • 遍历完数组后,dp数组的大小即为最长上升子序列的长度

其中查找dp数组中第一个大于当前数字的数时,可用二分查找降低时间复杂度,这样此解法的总时间复杂度为Ο(nlogn)

 

代码

 

 1 class Solution {
 2 public:
 3     int lengthOfLIS(vector<int>& nums) {
 4         int l=nums.size();
 5         vector<int> dp;
 6         if(l==0)
 7             return 0;
 8         dp.push_back(nums[0]);
 9         for(int i=1;i<l;i++){
10             biReplace(dp,nums[i]);
11         }
12         return dp.size();
13     }
14     void biReplace(vector<int>& dp, int x){
15         int f=0,l=dp.size()-1;
16         if(x>dp[l]){
17             dp.push_back(x);
18             return;
19         }
20         int m=(f+l)/2;
21         while(dp[m]!=x){
22             if(dp[m]>x)
23                 l=m-1;
24             else f=m+1;
25             if(f>l){
26                 m=f;
27                 break;
28             }
29             m=(f+l)/2;
30         }
31         dp[m]=x;
32     }
33 };

 

推荐阅读