leetcode300---Longest Increasing Subsequence(最长递增子序列)

来源:互联网 发布:自助餐收银软件下载 编辑:程序博客网 时间:2024/04/29 14:54

问题描述:

Given an unsorted array of integers, find the length of longest increasing subsequence.

For example,

Given [10, 9, 2, 5, 3, 7, 101, 18],

The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

给定一个未经排序的整数数组,寻找最长递增子序列的长度。

问题求解:

方法一:动态规划。时间复杂度为O(n^2)。
状态转移方程:dp[i] = max{dp[j]+1,dp[i]}, j<i&&a[j]<a[i].

class Solution {public://动态规划。时间复杂度为O(n^2)。    int lengthOfLIS(vector<int>& nums) {        int n=nums.size();        if(n <= 1) return n;        vector<int> dp(n, 1);        for(int i=1;i<n;i++)        {//dp[i]表示LIS的长度。nums[i]作为LIS的最后一个元素。            for(int j=0;j<i;j++)            {                if(nums[i] > nums[j])                {//满足递增                    dp[i]=max(dp[i], dp[j]+1);//利用状态转移方程                }            }        }        int res=0;        for(int i=0;i<n;i++)        {//求得最大的dp[i]            res = max(res, dp[i]);        }        return res;    }};

方法二:贪心法+二分搜索!!!时间复杂度O(n * log n),空间复杂度O(n)!!!

设dp[i]表示以i为结尾的最长递增子序列的长度。考虑两个数a[x]和a[y],x < y且a[x] < a[y],且dp[x]=dp[y],当a[t]要选择时,到底取哪一个构成最优的呢?显然选取a[x]更有潜力,因为可能存在a[x]<a[z]<a[y],这样a[t]可以获得更优的值。在这里给我们一个启示,当dp[t]一样时,尽量选择更小的a[x]。

1,3,5,2,8,4,6这个例子中,当到6时,一共可以有四种不同长度,且保证该升序序列在同长度升序序列中末尾最小的升序序列。

(1)1,(2)1,3,(4)1,3,5,(6)

注:每个序列的末尾数字加括号。
这些序列都是有可能成为最长递增子序列的候选者。
tail[i]表示长度为i的递增序列末尾的数字(如上1,2,4,6存在tail中)tail[]数组性质:tail[0] < tail[1] < … < tail[n] !!!
每次读入一个新元素时,可按照以下规则更新这些序列:
1)nums[i]比所有递增序列的末尾都小,则长度为1的序列更新为这个更小的末尾。
如果读入数字0,则更新序列为:

(0)1,(2)1,3,(4)1,3,5,(6)

2)nums[i]比所有序列的末尾都大,则直接将nums[i]加到后面。
如果读入数字9,更新序列为:

(1)1,(2)1,3,(4)1,3,5,(6)1,3,5,6,(9)

3)在中间,则更新那个末尾数字刚好大于等于nums[i]的那个序列,nums[i]替换其末尾数字。该情况下,利用二分搜索寻找。
如果读入数字3,更新序列为:

(1)1,(2)1,3,(3)1,3,5,(6)

代码:

class Solution {public://    int lengthOfLIS(vector<int>& nums) {        int n=nums.size();        if(n <= 1) return n;        //tail[i]表示长度为i的递增序列末尾的数字        //tail[]数组性质:tail[0]<tail[1]<...tail[n] !!!        vector<int> tail(n);//初始化为n个值为0的元素        //1.len为当前最长的递增序列长度(为方便操作将len减1,从0开始,最后再加上1)        int len=0;        tail[0]=nums[0];        //2.每次读入一个新元素nums[i]        for(int i=1;i<n;i++)        {//遍历nums[]中的数            if(nums[i] < tail[0])            {//(1)nums[i]比所有递增序列的末尾都小,则长度为1的序列更新为这个更小的末尾。                tail[0]=nums[i];            }            else if(nums[i] > tail[len])            {//(2)nums[i]比所有序列的末尾都大,则直接将nums[i]加到后面                tail[++len]=nums[i];            }            else            {//(3)在中间,则更新那个末尾数字刚好大于等于nums[i]的那个序列,nums[i]替换其末尾数字                tail[biSearch(tail, 0, len, nums[i])]=nums[i];            }        }        return len+1;    }    int biSearch(vector<int>& tail, int low, int high, int target)    {//由于tail数组是有序的,故可二分查找其中元素        while(low <= high)//不能是low<high        {//当low=high时还要进行一次循环!!!         //此时mid=low=high.若tail[mid]<target,则low=mid+1.而不是直接返回low!!!            int mid = low + (high-low)/2;            if(tail[mid] == target) return mid;            else if(tail[mid] > target)            {                high=mid-1;            }            else            {                low=mid+1;            }        }        return low;    }};
0 0
原创粉丝点击