Welcome to Subscribe On Youtube

Question

Formatted question description: https://leetcode.ca/all/300.html

Given an integer array nums, return the length of the longest strictly increasing subsequence.

 

Example 1:

Input: nums = [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.

Example 2:

Input: nums = [0,1,0,3,2,3]
Output: 4

Example 3:

Input: nums = [7,7,7,7,7,7,7]
Output: 1

 

Constraints:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

 

Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity?

Algorithm

First create an empty dp array, and then start to traverse the original array, for each traversed number, use binary search to find the first number not less than it in the dp array

  • If this number does not exist, add the traversed number directly after the dp array
  • If it exists, update this number to the number currently traversed, and finally return the length of the dp array
input: {4,  2, 4, 5, 3, 7}
dp[]:  {2, 3, 5, 7}

process

num: 4
i: 0
len: 1
dp[]: [4, 0, 0, 0, 0, 0]
------
num: 2
i: 0
len: 1
dp[]: [2, 0, 0, 0, 0, 0]
------
num: 4
i: 1
len: 2
dp[]: [2, 4, 0, 0, 0, 0]
------
num: 5
i: 2
len: 3
dp[]: [2, 4, 5, 0, 0, 0]
------
num: 3
i: 1
len: 3
dp[]: [2, 3, 5, 0, 0, 0]
------
num: 7
i: 3
len: 4
dp[]: [2, 3, 5, 7, 0, 0]

Code

  • import java.util.Arrays;
    
    public class Longest_Increasing_Subsequence {
    
        // ref: https://leetcode.com/articles/longest-increasing-subsequence/
    
        // NlogN
        public class Solution {
            public int lengthOfLIS(int[] nums) {
    
                // store the increasing subsequence formed by including the currently encountered element
                int[] dp = new int[nums.length];
    
                int len = 0;
                for (int num : nums) {
                    // Arrays.binarySearch() method returns index of the search key, if it is contained in the array,
                    // else it returns (-(insertion point) - 1)
                    int i = Arrays.binarySearch(dp, 0, len, num);
                    if (i < 0) {
                        i = -(i + 1);
                    }
    
                    dp[i] = num;
    
                    if (i == len) {
                        len++;
                    }
                }
                return len;
            }
        }
    
        // NlogN
        public class Solution_manualBS {
            public int lengthOfLIS(int[] nums) {
    
                // store the increasing subsequence formed by including the currently encountered element
                int[] dp = new int[nums.length];
    
                int len = 0;
                for (int num : nums) {
    
                    // binary search self implementation
                    int left = 0, right = len;
                    while (left < right) {
                        int mid = left + (right - left) / 2;
                        if (dp[mid] < num) left = mid + 1;
                        else right = mid;
                    }
                    dp[right] = num;
    
                    if (right == len) {
                        len++;
                    }
                }
                return len;
            }
        }
    
        public class Solution_dp {
            public int lengthOfLIS(int[] nums) {
    
                if (nums.length == 0) {
                    return 0;
                }
    
                // dp[i] represents the length of the longest increasing subsequence possible
                //      considering the array elements upto the i-th index only,
                //      by necessarily including the i-th element
                int[] dp = new int[nums.length];
                dp[0] = 1;
    
                int longest = 1;
    
                for (int i = 1; i < dp.length; i++) {
    
                    int maxValBeforeI = 0;
    
                    for (int j = 0; j < i; j++) {
                        if (nums[i] > nums[j]) {
                            maxValBeforeI = Math.max(maxValBeforeI, dp[j]);
                        }
                    }
    
                    dp[i] = maxValBeforeI + 1;
    
                    longest = Math.max(longest, dp[i]);
                }
                return longest;
            }
        }
    
        public class Solution_recursion_with_memorization {
    
            public int lengthOfLIS(int[] nums) {
    
                // memo[i][j]memo[i][j] represents the length of the LIS possible using
                //      * nums[i] as the previous element considered to be included/not included in the LIS,
                //      * nums[j]nums[j] as the current element considered to be included/not included in the LIS.
                int memo[][] = new int[nums.length + 1][nums.length];
    
                for (int[] l : memo) {
                    Arrays.fill(l, -1);
                }
    
                return lengthofLIS(nums, -1, 0, memo);
            }
    
            public int lengthofLIS(int[] nums, int previndex, int currentIndex, int[][] memo) {
                if (currentIndex == nums.length) {
                    return 0;
                }
    
                // @note: prev+1
                if (memo[previndex + 1][currentIndex] >= 0) {
                    return memo[previndex + 1][currentIndex];
                }
    
                int taken = 0;
                if (previndex < 0 || nums[currentIndex] > nums[previndex]) {
                    taken = 1 + lengthofLIS(nums, currentIndex, currentIndex + 1, memo);
                }
    
                int nottaken = lengthofLIS(nums, previndex, currentIndex + 1, memo);
    
                // record before return
                memo[previndex + 1][currentIndex] = Math.max(taken, nottaken);
    
                return memo[previndex + 1][currentIndex];
            }
    
        }
    
    
        public class Solution_bruteForce {
    
            public int lengthOfLIS(int[] nums) {
                return lengthofLIS(nums, Integer.MIN_VALUE, 0);
            }
    
            public int lengthofLIS(int[] nums, int prev, int currentIndex) {
    
                if (currentIndex == nums.length) {
                    return 0;
                }
    
                int included = 0;
    
                if (nums[currentIndex] > prev) {
                    included = 1 + lengthofLIS(nums, nums[currentIndex], currentIndex + 1);
                }
    
                // diff is what prev is passed down
                int notIncluded = lengthofLIS(nums, prev, currentIndex + 1);
    
                return Math.max(included, notIncluded);
            }
        }
    
    }
    
    ############
    
    class Solution {
        public int lengthOfLIS(int[] nums) {
            int n = nums.length;
            int[] d = new int[n + 1];
            d[1] = nums[0];
            int size = 1;
            for (int i = 1; i < n; ++i) {
                if (nums[i] > d[size]) {
                    d[++size] = nums[i];
                } else {
                    int left = 1, right = size;
                    while (left < right) {
                        int mid = (left + right) >> 1;
                        if (d[mid] >= nums[i]) {
                            right = mid;
                        } else {
                            left = mid + 1;
                        }
                    }
                    int p = d[left] >= nums[i] ? left : 1;
                    d[p] = nums[i];
                }
            }
            return size;
        }
    }
    
  • // OJ: https://leetcode.com/problems/longest-increasing-subsequence/
    // Time: O(N^2)
    // Space: O(N)
    class Solution {
    public:
        int lengthOfLIS(vector<int>& A) {
            if (A.empty()) return 0;
            int N = A.size();
            vector<int> dp(N, 1);
            for (int i = 1; i < N; ++i) {
                for (int j = 0; j < i; ++j) {
                    if (A[j] < A[i]) dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            return *max_element(begin(dp), end(dp));
        }
    };
    
  • # find the largest end element in tails that is smaller than nums[i]
    # and then replace it with nums[i] and discard the list in the same length
    # which is implemented by `tail[idx] = num`
    
    class Solution(object):
        def lengthOfLIS(self, nums):
            """
            :type nums: List[int]
            :rtype: int
            """
            tail = []
            for num in nums:
                # if using bisect_right(tail, num), then input=[7,7,7,7,7,7,7] will output 7 but expected result is 1
    
                idx = bisect.bisect_left(tail, num)
                if idx == len(tail): # same as in java: if (i == len) len++;
                    tail.append(num)
                else:
                    tail[idx] = num
            return len(tail)
    
    ############
    
    class Solution:
        def lengthOfLIS(self, nums: List[int]) -> int:
            d = [nums[0]]
            for x in nums[1:]:
                if x > d[-1]:
                    d.append(x)
                else:
                    idx = bisect_left(d, x)
                    if idx == len(d):
                        idx = 0
                    d[idx] = x
            return len(d)
    
    
  • func lengthOfLIS(nums []int) int {
    	d := make([]int, len(nums)+1)
    	d[1] = nums[0]
    	size := 1
    	for _, x := range nums[1:] {
    		if x > d[size] {
    			size++
    			d[size] = x
    		} else {
    			left, right := 1, size
    			for left < right {
    				mid := (left + right) >> 1
    				if d[mid] >= x {
    					right = mid
    				} else {
    					left = mid + 1
    				}
    			}
    			if d[left] < x {
    				left = 1
    			}
    			d[left] = x
    		}
    	}
    	return size
    }
    
  • function lengthOfLIS(nums: number[]): number {
        const n = nums.length;
        let d = new Array(n + 1);
        d[1] = nums[0];
        let size = 1;
        for (let i = 1; i < n; ++i) {
            if (nums[i] > d[size]) {
                d[++size] = nums[i];
            } else {
                let left = 1,
                    right = size;
                while (left < right) {
                    const mid = (left + right) >> 1;
                    if (d[mid] >= nums[i]) {
                        right = mid;
                    } else {
                        left = mid + 1;
                    }
                }
                const p = d[left] >= nums[i] ? left : 1;
                d[p] = nums[i];
            }
        }
        return size;
    }
    
    
  • impl Solution {
        pub fn length_of_lis(nums: Vec<i32>) -> i32 {
            let n = nums.len();
            let mut dp = vec![1; n];
            for i in 1..n {
                for j in 0..i {
                    if nums[i] > nums[j] {
                        dp[i] = dp[i].max(dp[j] + 1);
                    }
                }
            }
            *dp.iter().max().unwrap()
        }
    }
    
    

All Problems

All Solutions