Skip to content

Longest Increasing Subsequence added #363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
5 changes: 4 additions & 1 deletion pydatastructs/linear_data_structures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
cocktail_shaker_sort,
quick_sort,
longest_common_subsequence,
is_ordered
is_ordered,
upper_bound,
lower_bound,
longest_increasing_subsequence
)
__all__.extend(algorithms.__all__)
180 changes: 178 additions & 2 deletions pydatastructs/linear_data_structures/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
'cocktail_shaker_sort',
'quick_sort',
'longest_common_subsequence',
'is_ordered'
'upper_bound',
'lower_bound',
'is_ordered',
'longest_increasing_subsequence'
]

def _merge(array, sl, el, sr, er, end, comp):
Expand Down Expand Up @@ -789,6 +792,7 @@ def longest_common_subsequence(seq1: OneDimensionalArray, seq2: OneDimensionalAr

return OneDimensionalArray(seq1._dtype, check_mat[row][col][-1])


def is_ordered(array, **kwargs):
"""
Checks whether the given array is ordered or not.
Expand Down Expand Up @@ -823,7 +827,6 @@ def is_ordered(array, **kwargs):

Examples
========

>>> from pydatastructs import OneDimensionalArray, is_ordered
>>> arr = OneDimensionalArray(int, [1, 2, 3, 4])
>>> is_ordered(arr)
Expand All @@ -843,3 +846,176 @@ def is_ordered(array, **kwargs):
if comp(array[i], array[i - 1]):
return False
return True

def upper_bound(array, start, end, value, comp):
"""
Finds the index of the first occurence of an element greater than value according
to an order defined,in the given sorted OneDimensionalArray

Parameters
========
array: OneDimensionalArray
The sorted array (sorted according to a custom comparator function) in which the
upper bound has to be found

start: int
The staring index of the portion of the array in which the upper bound
of a given value has to be looked for

end: int
The ending index of the portion of the array in which the upper bound
of a given value has to be looked for

Returns
=======

output: int
Index of the upper bound of the given value in the given sorted OneDimensionalArray

Examples
========
>>> from pydatastructs import upper_bound, OneDimensionalArray as ODA
>>> arr = ODA(int, [4, 5, 5, 6, 7])
>>> upperBound = upper_bound(arr, 0, 4, 5, None)
>>> upperBound
3
>>> arr = ODA(int, [7, 6, 5, 5, 4])
>>> upperBound = upper_bound(arr, 0, 4, 5, lambda x, y: x > y)
>>> upperBound
4

Note
====

The OneDimensionalArray must be sorted beforehand
"""
if comp is None:
comp = lambda a, b: (a < b)
index = end
inclusive_end = end - 1
if comp(value, array[start]):
index = start
while start <= inclusive_end:
mid = (start + inclusive_end)//2
if not comp(value, array[mid]):
start = mid + 1
else:
index = mid
inclusive_end = mid - 1
return index

def lower_bound(array, start, end, value, comp):
"""
Finds the the index of the first occurence of an element which is not
less than value according to an order defined, in the given OneDimensionalArray

Parameters
========
array: OneDimensionalArray
The sorted array (sorted according to a custom comparator function)
in which the lower bound has to be found

start: int
The staring index of the portion of the array in which the lower
bound of a given value has to be looked for

end: int
The ending index of the portion of the array in which the lower
bound of a given value has to be looked for

Returns
=======
output: int
Index of the lower bound of the given value in the given sorted OneDimensionalArray

Examples
========

>>> from pydatastructs import lower_bound, OneDimensionalArray as ODA
>>> arr = ODA(int, [4, 5, 5, 6, 7])
>>> lowerBound = lower_bound(arr, 0, 4, 5, lambda x, y : x < y)
>>> lowerBound
1
>>> arr = ODA(int, [7, 6, 5, 5, 4])
>>> lowerBound = lower_bound(arr, 0, 4, 5, lambda x, y : x > y)
>>> lowerBound
2

Note
====

The OneDimensionalArray must be sorted beforehand
"""
if comp is None:
comp = lambda a, b: (a < b)
index = end
inclusive_end = end - 1
if not comp(array[start], value):
index = start
while start <= inclusive_end:
mid = (start + inclusive_end)//2
if comp(array[mid], value):
start = mid + 1
else:
index = mid
inclusive_end = mid - 1
return index


def longest_increasing_subsequence(array):
"""
Returns the longest increasing subsequence (as a OneDimensionalArray) that
can be obtained from a given OneDimensionalArray. A subsequence
of an array is an ordered subset of the array's elements having the same
sequential ordering as the original array. Here, an increasing
sequence stands for a strictly increasing sequence of numbers.

Parameters
========
array: OneDimensionalArray
The given array in the form of a OneDimensionalArray

Returns
=======
output: OneDimensionalArray
Returns the longest increasing subsequence that can be obtained
from the given array

Examples
========
>>> from pydatastructs import lower_bound, OneDimensionalArray as ODA
>>> from pydatastructs import longest_increasing_subsequence as LIS

>>> array = ODA(int, [2, 5, 3, 7, 11, 8, 10, 13, 6])
>>> longestIncreasingSubsequence = LIS(array)
>>> longestIncreasingSubsequence
[2, 3, 7, 8, 10, 13]
>>> array2 = ODA(int, [3, 4, -1, 5, 8, 2, 2 ,2, 3, 12, 7, 9, 10])
>>> longestIncreasingSubsequence = LIS(array2)
>>> longestIncreasingSubsequence
[-1, 2, 3, 7, 9, 10]

"""
n = len(array)
dp = [0]*n
parent = [-1]*n
length = 0
for i in range(1, n):
if array[i] <= array[dp[0]]:
dp[0] = i
elif array[dp[length]] < array[i]:
length += 1
dp[length] = i
parent[i] = dp[length - 1]
else:
curr_array = [array[dp[i]] for i in range(length)]
ceil = lower_bound(curr_array, 0, length, array[i], None)
dp[ceil] = i
parent[i] = dp[ceil - 1]
ans = []

last_index = dp[length]
while last_index != -1:
ans[:0] = [array[last_index]]
last_index = parent[last_index]
return ans
119 changes: 116 additions & 3 deletions pydatastructs/linear_data_structures/tests/test_algorithms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from pydatastructs import (
merge_sort_parallel, DynamicOneDimensionalArray,
OneDimensionalArray, brick_sort, brick_sort_parallel,
heapsort, matrix_multiply_parallel, counting_sort, bucket_sort,
cocktail_shaker_sort, quick_sort, longest_common_subsequence, is_ordered)

heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort, quick_sort, longest_common_subsequence,
is_ordered, upper_bound, lower_bound, longest_increasing_subsequence)

from pydatastructs.utils.raises_util import raises
import random
Expand Down Expand Up @@ -128,6 +127,7 @@ def test_longest_common_sequence():
output = longest_common_subsequence(Y, Z)
assert str(output) == '[]'


def test_is_ordered():
ODA = OneDimensionalArray
DODA = DynamicOneDimensionalArray
Expand Down Expand Up @@ -157,3 +157,116 @@ def test_is_ordered():
arr4.delete(0)
output = is_ordered(arr4)
assert output == expected_result

def test_upper_bound():
ODA = OneDimensionalArray
arr1 = ODA(int, [3, 3, 3])
output = upper_bound(arr1, 0, len(arr1), 3, None)
expected_result = 3
assert expected_result == output

arr2 = ODA(int, [4, 4, 5, 6])
output = upper_bound(arr2, 0, 3, 4, None)
expected_result = 2
assert expected_result == output

arr3 = ODA(int, [6, 6, 7, 8, 9])
output = upper_bound(arr3, 2, 4, 5, None)
expected_result = 2
assert expected_result == output

arr4 = ODA(int, [3, 4, 4, 6])
output = upper_bound(arr4, 1, 3, 5, None)
expected_result = 3
assert expected_result == output

arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr5, 0, len(arr5), 6, lambda x, y: x > y)
expected_result = 5
assert expected_result == output

arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr6, 2, len(arr6), 2, lambda x, y: x > y)
expected_result = 8
assert expected_result == output

arr7 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr7, 3, 7, 9, lambda x, y: x > y)
expected_result = 3
assert expected_result == output

arr8 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr8, 0, 3, 6, lambda x, y: x > y)
expected_result = 3
assert expected_result == output


def test_lower_bound():
ODA = OneDimensionalArray
arr1 = ODA(int, [3, 3, 3])
output = lower_bound(arr1, 1, len(arr1), 3, None)
expected_result = 1
assert expected_result == output

arr2 = ODA(int, [4, 4, 4, 4, 5, 6])
output = lower_bound(arr2, 0, 3, 5, None)
expected_result = 3
assert expected_result == output

arr3 = ODA(int, [6, 6, 7, 8, 9])
output = lower_bound(arr3, 0, 3, 5, None)
expected_result = 0
assert expected_result == output

arr4 = ODA(int, [3, 4, 4, 4])
output = lower_bound(arr4, 0, 4, 5, None)
expected_result = 4
assert expected_result == output

arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr5, 0, len(arr5), 5, lambda x, y: x > y)
expected_result = 5
assert expected_result == output

arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr6, 4, len(arr6), 2, lambda x, y: x > y)
expected_result = 8
assert expected_result == output

arr7 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr7, 0, 5, 9, lambda x, y: x > y)
expected_result = 0
assert expected_result == output

arr8 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr8, 0, 3, 6, lambda x, y: x > y)
expected_result = 1
assert expected_result == output

def test_longest_increasing_subsequence():
ODA = OneDimensionalArray

arr1 = ODA(int, [2, 5, 3, 7, 11, 8, 10, 13, 6])
output = longest_increasing_subsequence(arr1)
expected_result = [2, 3, 7, 8, 10, 13]
assert expected_result == output

arr2 = ODA(int, [3, 4, -1, 5, 8, 2, 2, 2, 3, 12, 7, 9, 10])
output = longest_increasing_subsequence(arr2)
expected_result = [-1, 2, 3, 7, 9, 10]
assert expected_result == output

arr3 = ODA(int, [6, 6, 6, 19, 9])
output = longest_increasing_subsequence(arr3)
expected_result = [6, 9]
assert expected_result == output

arr4 = ODA(int, [5, 4, 4, 3, 3, 6, 6, 8])
output = longest_increasing_subsequence(arr4)
expected_result = [3, 6, 8]
assert expected_result == output

arr5 = ODA(int, [7, 6, 6, 6, 5, 4, 3])
output = longest_increasing_subsequence(arr5)
expected_result = [3]
assert expected_result == output