Array Binary Search Dynamic Programming Longest Increasing Subsequence
Description 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?
Solutions Solution 1 Thinking
A subsequence must keep relative order and increase strictly. Enumerating all subsequences explodes with \(n\) ; \(n \le 2500\) still allows an \(O(n^2)\) transfer by ending index.
The LIS ending at \(nums[i]\) depends only on smaller values to its left. Define \(f[i]\) as that length, and for each \(j < i\) with \(nums[j] < nums[i]\) take \(\max(f[j]+1)\) . A singleton has length \(1\) , and the answer is \(\max f\) .
Python3 Java C++ Go TypeScript Rust
class Solution :
def lengthOfLIS ( self , nums : List [ int ]) -> int :
n = len ( nums )
f = [ 1 ] * n
for i in range ( 1 , n ):
for j in range ( i ):
if nums [ j ] < nums [ i ]:
f [ i ] = max ( f [ i ], f [ j ] + 1 )
return max ( f )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Solution {
public int lengthOfLIS ( int [] nums ) {
int n = nums . length ;
int [] f = new int [ n ] ;
Arrays . fill ( f , 1 );
int ans = 1 ;
for ( int i = 1 ; i < n ; ++ i ) {
for ( int j = 0 ; j < i ; ++ j ) {
if ( nums [ j ] < nums [ i ] ) {
f [ i ] = Math . max ( f [ i ] , f [ j ] + 1 );
}
}
ans = Math . max ( ans , f [ i ] );
}
return ans ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class Solution {
public :
int lengthOfLIS ( vector < int >& nums ) {
int n = nums . size ();
vector < int > f ( n , 1 );
for ( int i = 1 ; i < n ; ++ i ) {
for ( int j = 0 ; j < i ; ++ j ) {
if ( nums [ j ] < nums [ i ]) {
f [ i ] = max ( f [ i ], f [ j ] + 1 );
}
}
}
return * max_element ( f . begin (), f . end ());
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 func lengthOfLIS ( nums [] int ) int {
n := len ( nums )
f := make ([] int , n )
for i := range f {
f [ i ] = 1
}
ans := 1
for i := 1 ; i < n ; i ++ {
for j := 0 ; j < i ; j ++ {
if nums [ j ] < nums [ i ] {
f [ i ] = max ( f [ i ], f [ j ] + 1 )
ans = max ( ans , f [ i ])
}
}
}
return ans
}
1
2
3
4
5
6
7
8
9
10
11
12 function lengthOfLIS ( nums : number []) : number {
const n = nums . length ;
const f : number [] = new Array ( n ). fill ( 1 );
for ( let i = 1 ; i < n ; ++ i ) {
for ( let j = 0 ; j < i ; ++ j ) {
if ( nums [ j ] < nums [ i ]) {
f [ i ] = Math . max ( f [ i ], f [ j ] + 1 );
}
}
}
return Math . max (... f );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14 impl Solution {
pub fn length_of_lis ( nums : Vec < i32 > ) -> i32 {
let n = nums . len ();
let mut f = vec! [ 1 ; n ];
for i in 1 .. n {
for j in 0 .. i {
if nums [ j ] < nums [ i ] {
f [ i ] = f [ i ]. max ( f [ j ] + 1 );
}
}
}
* f . iter (). max (). unwrap ()
}
}
Solution 2 Thinking
Method 1 scans the left side linearly for the best \(f[j]\) below \(x\) , which costs \(O(n^2)\) . After discretizing values, that query is a prefix maximum on ranks, which a Fenwick tree maintains in \(O(\log n)\) .
Scan each \(x\) in original order: query the prefix max strictly below \(x\) , then write \(x\) . Only left-hand information is used, so the recurrence matches Method 1 in \(O(n \log n)\) time.
Python3 Java C++ Go TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 class BinaryIndexedTree :
def __init__ ( self , n : int ):
self . n = n
self . c = [ 0 ] * ( n + 1 )
def update ( self , x : int , v : int ):
while x <= self . n :
self . c [ x ] = max ( self . c [ x ], v )
x += x & - x
def query ( self , x : int ) -> int :
mx = 0
while x :
mx = max ( mx , self . c [ x ])
x -= x & - x
return mx
class Solution :
def lengthOfLIS ( self , nums : List [ int ]) -> int :
s = sorted ( set ( nums ))
m = len ( s )
tree = BinaryIndexedTree ( m )
for x in nums :
x = bisect_left ( s , x ) + 1
t = tree . query ( x - 1 ) + 1
tree . update ( x , t )
return tree . query ( m )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 class Solution {
public int lengthOfLIS ( int [] nums ) {
int [] s = nums . clone ();
Arrays . sort ( s );
int m = 0 ;
int n = s . length ;
for ( int i = 0 ; i < n ; ++ i ) {
if ( i == 0 || s [ i ] != s [ i - 1 ] ) {
s [ m ++] = s [ i ] ;
}
}
BinaryIndexedTree tree = new BinaryIndexedTree ( m );
for ( int x : nums ) {
x = search ( s , x , m );
int t = tree . query ( x - 1 ) + 1 ;
tree . update ( x , t );
}
return tree . query ( m );
}
private int search ( int [] nums , int x , int r ) {
int l = 0 ;
while ( l < r ) {
int mid = ( l + r ) >> 1 ;
if ( nums [ mid ] >= x ) {
r = mid ;
} else {
l = mid + 1 ;
}
}
return l + 1 ;
}
}
class BinaryIndexedTree {
private int n ;
private int [] c ;
public BinaryIndexedTree ( int n ) {
this . n = n ;
c = new int [ n + 1 ] ;
}
public void update ( int x , int v ) {
while ( x <= n ) {
c [ x ] = Math . max ( c [ x ] , v );
x += x & - x ;
}
}
public int query ( int x ) {
int mx = 0 ;
while ( x > 0 ) {
mx = Math . max ( mx , c [ x ] );
x -= x & - x ;
}
return mx ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 class BinaryIndexedTree {
public :
BinaryIndexedTree ( int _n )
: n ( _n )
, c ( _n + 1 ) {}
void update ( int x , int v ) {
while ( x <= n ) {
c [ x ] = max ( c [ x ], v );
x += x & - x ;
}
}
int query ( int x ) {
int mx = 0 ;
while ( x ) {
mx = max ( mx , c [ x ]);
x -= x & - x ;
}
return mx ;
}
private :
int n ;
vector < int > c ;
};
class Solution {
public :
int lengthOfLIS ( vector < int >& nums ) {
vector < int > s = nums ;
sort ( s . begin (), s . end ());
s . erase ( unique ( s . begin (), s . end ()), s . end ());
BinaryIndexedTree tree ( s . size ());
for ( int x : nums ) {
x = lower_bound ( s . begin (), s . end (), x ) - s . begin () + 1 ;
int t = tree . query ( x - 1 ) + 1 ;
tree . update ( x , t );
}
return tree . query ( s . size ());
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 type BinaryIndexedTree struct {
n int
c [] int
}
func newBinaryIndexedTree ( n int ) * BinaryIndexedTree {
return & BinaryIndexedTree { n , make ([] int , n + 1 )}
}
func ( bit * BinaryIndexedTree ) update ( x , v int ) {
for x <= bit . n {
bit . c [ x ] = max ( bit . c [ x ], v )
x += x & - x
}
}
func ( bit * BinaryIndexedTree ) query ( x int ) int {
mx := 0
for x > 0 {
mx = max ( mx , bit . c [ x ])
x -= x & - x
}
return mx
}
func lengthOfLIS ( nums [] int ) int {
n := len ( nums )
s := make ([] int , n )
copy ( s , nums )
sort . Ints ( s )
m := 0
for i , x := range s {
if i == 0 || x != s [ i - 1 ] {
s [ m ] = x
m ++
}
}
tree := newBinaryIndexedTree ( m )
for _ , x := range nums {
x = sort . SearchInts ( s [: m ], x ) + 1
t := tree . query ( x - 1 ) + 1
tree . update ( x , t )
}
return tree . query ( m )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 class BinaryIndexedTree {
private n : number ;
private c : number [];
constructor ( n : number ) {
this . n = n ;
this . c = new Array ( n + 1 ). fill ( 0 );
}
update ( x : number , v : number ) {
while ( x <= this . n ) {
this . c [ x ] = Math . max ( this . c [ x ], v );
x += x & - x ;
}
}
query ( x : number ) : number {
let mx = 0 ;
while ( x ) {
mx = Math . max ( mx , this . c [ x ]);
x -= x & - x ;
}
return mx ;
}
}
function lengthOfLIS ( nums : number []) : number {
const s = [... new Set ( nums )]. sort (( a , b ) => a - b );
const m = s . length ;
const tree = new BinaryIndexedTree ( m );
for ( let x of nums ) {
x = search ( s , x );
const t = tree . query ( x - 1 ) + 1 ;
tree . update ( x , t );
}
return tree . query ( m );
}
function search ( nums : number [], x : number ) : number {
let l = 0 ,
r = nums . length - 1 ;
while ( l < r ) {
const mid = ( l + r ) >> 1 ;
if ( nums [ mid ] >= x ) {
r = mid ;
} else {
l = mid + 1 ;
}
}
return l + 1 ;
}