300. Longest Increasing Subsequence

300. Longest Increasing Subsequence

Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input: [10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 
Note:
* 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?



https://leetcode.com/problems/longest-increasing-subsequence/solution/


Solution 1: dp 
Time O(n^ 2)

public class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxans = 1;
        for (int i = 1; i < dp.length; i++) {
            int maxval = 0;
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    maxval = Math.max(maxval, dp[j]);
                }
            }
            dp[i] = maxval + 1;
            maxans = Math.max(maxans, dp[i]);
        }
        return maxans;
    }
}

* Time complexity : O(n^2)
Two loops of n are there.

* Space complexity : O(n). dp array of size n is used.





Solution 2: dp +  binary search n* logn. 


Each time we only do one of the two:

(1) if x is larger than all tails, append it, increase the size by 1
(2) if tails[i-1] < x <= tails[i], update tails[i]

Doing so will maintain the tails invariant. The the final answer is just the size.





https://leetcode.com/problems/longest-increasing-subsequence/discuss/74897/Fast-Java-Binary-Search-Solution-with-detailed-explanation/167272


below is the example i copied from dragon, so my understanding is that the correct subsequence is 1 6 8 9, when we meet 5 , since 5 is not bigger than the last number of current subsequence, which is 1 6 8, so this means 5 can not be added after 8 to make a longer subsequence, but 5 could be part of the potential longest subsequence in the future, say if there are more numbers after 5 , like 6, 7, 8, 9, 10, 11... so in this case, we want to keep 5 somewhere , just in case we need 5 in the future to make the longest subsequence.
one way to do that is to replace 6 with 5, since we know already the longest subsequnce is 3 (1 6 8), by replacing 6 with 5 , the length is the still same, since we are not adding or removing, we are just replacing. so we basically do two things here, one is to record the current longest subsequent length,
which can be done without keeping the current correct subsequence. but we do keep partial correct subsequence of the longest , globalwise, by replacing 6 with 5, because all numbers later bigger than 5 are defintly bigger than 6, so we are not missing anything, so 5 is a better choice.

another example to help you understand this, after replacing 6 with 5. the current subsequence is 1 5 8 , say 7, 8 comes in after 5,
first when we meet 7, since 7 is smaller than 8, we replace 7 with 8 and the length is still 3 , the current subsequence is 1 5 7
and then when we meet 8, 8 is bigger than 7, so 8 is added after 7, the current subsequence is 1 5 7 8 , which has length 4

imagine we didn't replace 8 with 7 when we first meet 7, we still have 1 5 8 ,
and then when we meet 8, since 8 is not bigger than the last element of current subsequence 1 5 8, then 8 is not added after the end of the list

and hopefully you see how we missed the chance to have a longer subsequence when we don't replace 8 with 7. and also hope you understand that
replacing elements doesn't change the length of the current longest increasing subsequence.

For example, if the nums is
1 6 8 5 9

then the sequence will be like

1
1 6
1 6 8
1 5 8
1 5 8 9
The result will be 4. But the LIS is not 1 5 8 9.


This solution uses Binary Search + DP

1, traverse from 0 to len-1, the DP array keep the longest sequence.
2, if the val is bigger than largest in the dp array, add it to the end;
3, if it is among the sequence, return the pos that bigger than pres, update the array with this position if val is smaller than dp[pos];
This is to keep the sequence element with the smallest number.

For example:

10, 9, 2, 5, 3, 7, 101, 18

10 
9
2
2,5
2,3
2,3,7
2,3,7,101
2,3,7,18
class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums == null || nums.length == 0) return 0;
        // when a new number comes in, if this number is bigger than the number at the end of this nums 
        // then we just need to append this new number at the end of this list . 
        // if the new number is smaller than the number at the end of the current list, if this number can replace any number in 
        // the list, when this number is smaller than the old number 
        // for example if we have the current list as : 1 4 7 . 
        // the new number is 3 , then we can replace 4 with 3 , then our new list is 1 3 7 
        // (our potential longest increasing subsequence could be 1 3 4 5 6 7 , if we have 4 5 6 7 later in our later sequence )
        // okay , right now, our new list is 1 3 7, if 4 comes, in, 4 is smaller than 7. so we find a replacement 
        // we replace 7 with 4, then we have 1 3 4 , 
        // so when a new number comes in, we have two options, if the new number is bigger than the biggest number in the current
        // list, which is the number at the end of the current list, we can append the new number at the end of the current list 
        // if the new new number is smaller than the biggest number of the current list, we can replace some element in the list
        // with the new number 
        // finally return the length of the list 
        List<Integer> res = new ArrayList<>();
        res.add(nums[0]);
        for(int i = 1; i < nums.length; i++){
            int cur = nums[i];
            if(cur == res.get(res.size() - 1)) continue;
            if(cur > res.get(res.size() - 1)){
                res.add(cur);
            }else{
                int index = bs(res, cur);
                // replacement 
                res.set(index, cur);
            }
        }
        return res.size();
    }
    private int bs(List<Integer> res, int newNum){
        // find the index of the element in the list, which is the first number that is bigger than the new element 
        int left = 0;
        int right = res.size() - 1;
        while(left < right){
            int mid = left + (right - left) / 2;
            if(res.get(mid) == newNum){
                return mid;
            }else if(res.get(mid) > newNum){
                right = mid;
            }else{
                // res.get(mid) < newNum
                left = mid + 1;
            }
        }
        return left;
    }
    
}
    
//     res = 1 4 7 , newNum = 3 , should return 1 
//         mid = 1 index, 4 value, 4 > 3 , right = mid, now right = 1 index, left = 0 index, mid = 0, 1 < 3, left = mid + 1, 
//     left = 1, right = 1, 
    
//     res = 1 3 7 , newNum = 3,

 

Smallest larger than 4 is 5 

 

1 3 5 7 

4 

1 3 4 7 

 

 这道题,遇到新的数, 如果新的数比队尾的数还要大, 那么就把新的数加到队尾, 如果新的数等于或者小于队尾的数, 那么就在队列中找到比第一个比新的数大的最小的数, smallest larger 

, 这里用到了二分。 

posted on 2018-11-06 07:32  猪猪&#128055;  阅读(127)  评论(0)    收藏  举报

导航