diff --git a/AUTHORS b/AUTHORS index 943bf804d..8b5220bb7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,3 +11,4 @@ Pratik Goyal Jay Thorat Rajveer Singh Bharadwaj Kishan Ved +Arvinder Singh Dhoul \ No newline at end of file diff --git a/docs/source/pydatastructs/linear_data_structures/algorithms.rst b/docs/source/pydatastructs/linear_data_structures/algorithms.rst index 7ab521cfd..aadcf65c0 100644 --- a/docs/source/pydatastructs/linear_data_structures/algorithms.rst +++ b/docs/source/pydatastructs/linear_data_structures/algorithms.rst @@ -45,4 +45,10 @@ Algorithms .. autofunction:: pydatastructs.jump_search -.. autofunction:: pydatastructs.intro_sort \ No newline at end of file +.. autofunction:: pydatastructs.intro_sort + +.. autofunction:: pydatastructs.shell_sort + +.. autofunction:: pydatastructs.radix_sort + +.. autofunction:: pydatastructs.reverse_array \ No newline at end of file diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py index 057adc169..f4d8ea91a 100644 --- a/pydatastructs/linear_data_structures/__init__.py +++ b/pydatastructs/linear_data_structures/__init__.py @@ -47,6 +47,9 @@ jump_search, selection_sort, insertion_sort, - intro_sort + intro_sort, + shell_sort, + radix_sort, + reverse_array ) __all__.extend(algorithms.__all__) diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index 792bce855..e44ad9000 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -30,7 +30,10 @@ 'jump_search', 'selection_sort', 'insertion_sort', - 'intro_sort' + 'intro_sort', + 'shell_sort', + 'radix_sort', + 'reverse_array' ] def _merge(array, sl, el, sr, er, end, comp): @@ -1850,3 +1853,211 @@ def partition(array, lower, upper): intro_sort(array, start=p+1, end=upper, maxdepth=maxdepth-1, ins_threshold=ins_threshold) return array + +def shell_sort(array, *args, **kwargs): + """ + Implements shell sort algorithm. + + Parameters + ========== + + array: Array + The array which is to be sorted. + start: int + The starting index of the portion + which is to be sorted. + Optional, by default 0 + end: int + The ending index of the portion which + is to be sorted. + Optional, by default the index + of the last position filled. + comp: lambda/function + The comparator which is to be used + for sorting. If the function returns + False then only swapping is performed. + Optional, by default, less than or + equal to is used for comparing two + values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + + Returns + ======= + + output: Array + The sorted array. + + Examples + ======== + + >>> from pydatastructs.linear_data_structures.algorithms import OneDimensionalArray, shell_sort + >>> arr = OneDimensionalArray(int, [3, 2, 1]) + >>> out = shell_sort(arr) + >>> str(out) + '[1, 2, 3]' + >>> out = shell_sort(arr, comp=lambda u, v: u > v) + >>> str(out) + '[3, 2, 1]' + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Shellsort + """ + start = int(kwargs.get('start', 0)) + end = int(kwargs.get('end', len(array) - 1)) + comp = kwargs.get('comp', lambda u, v: u <= v) + + n = end - start + 1 + gap = n // 2 + while gap > 0: + for i in range(start + gap, end + 1): + temp = array[i] + j = i + while j >= start + gap and not _comp(array[j - gap], temp, comp): + array[j] = array[j - gap] + j -= gap + array[j] = temp + gap //= 2 + + if _check_type(array, (DynamicArray, _arrays.DynamicOneDimensionalArray)): + array._modify(True) + + return array + +def radix_sort(array, *args, **kwargs): + """ + Implements radix sort algorithm for non-negative integers. + + Parameters + ========== + + array: Array + The array which is to be sorted. Must contain non-negative integers. + start: int + The starting index of the portion + which is to be sorted. + Optional, by default 0 + end: int + The ending index of the portion which + is to be sorted. + Optional, by default the index + of the last position filled. + comp: lambda/function + The comparator which is to be used + for sorting. If the function returns + False then only swapping is performed. + Optional, by default, less than or + equal to is used for comparing two + values. + backend: pydatastructs.Backend + The backend to be used. + Optional, by default, the best available + backend is used. + + Returns + ======= + + output: Array + The sorted array. + + Examples + ======== + + >>> from pydatastructs.linear_data_structures.algorithms import OneDimensionalArray, radix_sort + >>> arr = OneDimensionalArray(int, [170, 45, 75, 90, 802, 24, 2, 66]) + >>> out = radix_sort(arr) + >>> str(out) + '[2, 24, 45, 66, 75, 90, 170, 802]' + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Radix_sort + """ + start = int(kwargs.get('start', 0)) + end = int(kwargs.get('end', len(array) - 1)) + + n = end - start + 1 + max_val = array[start] + for i in range(start + 1, end + 1): + if array[i] is not None and array[i] > max_val: + max_val = array[i] + exp = 1 + while max_val // exp > 0: + count = [0] * 10 + output = [None] * n + + for i in range(start, end + 1): + if array[i] is not None: + digit = (array[i] // exp) % 10 + count[digit] += 1 + + for i in range(1, 10): + count[i] += count[i - 1] + + for i in range(end, start - 1, -1): + if array[i] is not None: + digit = (array[i] // exp) % 10 + count[digit] -= 1 + output[count[digit]] = array[i] + + for i in range(n): + array[start + i] = output[i] + + exp *= 10 + + if _check_type(array, (DynamicArray, _arrays.DynamicOneDimensionalArray)): + array._modify(True) + + return array + +def reverse_array(array, *args, **kwargs): + """ + Reverses the specified portion of the array in-place. + + Parameters + ========== + + array: Array + The array to be reversed (DynamicOneDimensionalArray or OneDimensionalArray). + start: int + The starting index of the portion to reverse. + Optional, by default 0 + end: int + The ending index of the portion to reverse. + Optional, by default the index of the last position filled. + backend: pydatastructs.Backend + The backend to be used (PYTHON or CPP). + Optional, by default, the best available backend is used. + + Returns + ======= + + output: Array + The array with the specified portion reversed. + """ + start = int(kwargs.get('start', 0)) + end = int(kwargs.get('end', len(array) - 1)) + + if start >= end: + return array + n = end - start + 1 + + elements = [(array[i], i) for i in range(start, end + 1) if array[i] is not None] + elem_values = [e[0] for e in elements] + elem_values.reverse() + + k = 0 + for i in range(start, end + 1): + if array[i] is not None: + array[i] = elem_values[k] + k += 1 + + if _check_type(array, (DynamicArray, _arrays.DynamicOneDimensionalArray)): + array._modify(True) + + return array diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 46609544b..9c27507c0 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -5,7 +5,7 @@ cocktail_shaker_sort, quick_sort, longest_common_subsequence, is_ordered, upper_bound, lower_bound, longest_increasing_subsequence, next_permutation, prev_permutation, bubble_sort, linear_search, binary_search, jump_search, - selection_sort, insertion_sort, intro_sort, Backend) + selection_sort, insertion_sort, intro_sort, shell_sort, radix_sort, reverse_array, Backend) from pydatastructs.utils.raises_util import raises import random @@ -200,15 +200,15 @@ def _test_inner_ordered(*args, **kwargs): assert output == expected_result expected_result = True - arr3 = ODA(int, [0, -1, -2, -3, -4, 4]) - output = is_ordered(arr3, start=1, end=4, + arr2 = ODA(int, [0, -1, -2, -3, -4, 4]) + output = is_ordered(arr2, start=1, end=4, comp=lambda u, v: u > v, **kwargs) assert output == expected_result expected_result = True - arr4 = DODA(int, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - arr4.delete(0) - output = is_ordered(arr4, **kwargs) + arr3 = DODA(int, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + arr3.delete(0) + output = is_ordered(arr3, **kwargs) assert output == expected_result _test_inner_ordered() @@ -227,13 +227,13 @@ def test_upper_bound(): expected_result = 2 assert expected_result == output - arr3 = ODA(int, [6, 6, 7, 8, 9]) - output = upper_bound(arr3, 5, start=2, end=4) + arr2 = ODA(int, [6, 6, 7, 8, 9]) + output = upper_bound(arr2, 5, start=2, end=4) expected_result = 2 assert expected_result == output - arr4 = ODA(int, [3, 4, 4, 6]) - output = upper_bound(arr4, 5, start=1, end=3) + arr3 = ODA(int, [3, 4, 4, 6]) + output = upper_bound(arr3, 5, start=1, end=3) expected_result = 3 assert expected_result == output @@ -242,13 +242,13 @@ def test_upper_bound(): expected_result = 5 assert expected_result == output - arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3]) - output = upper_bound(arr6, 2, start=2, comp=lambda x, y: x > y) + arr4 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3]) + output = upper_bound(arr4, 2, start=2, comp=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, 9, start=3, end=7, comp=lambda x, y: x > y) + arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3]) + output = upper_bound(arr5, 9, start=3, end=7, comp=lambda x, y: x > y) expected_result = 3 assert expected_result == output @@ -270,13 +270,13 @@ def test_lower_bound(): expected_result = 3 assert expected_result == output - arr3 = ODA(int, [6, 6, 7, 8, 9]) - output = lower_bound(arr3, 5, end=3) + arr2 = ODA(int, [6, 6, 7, 8, 9]) + output = lower_bound(arr2, 5, end=3) expected_result = 0 assert expected_result == output - arr4 = ODA(int, [3, 4, 4, 4]) - output = lower_bound(arr4, 5) + arr3 = ODA(int, [3, 4, 4, 4]) + output = lower_bound(arr3, 5) expected_result = 4 assert expected_result == output @@ -285,13 +285,13 @@ def test_lower_bound(): expected_result = 5 assert expected_result == output - arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3]) - output = lower_bound(arr6, 2, start=4, comp=lambda x, y: x > y) + arr4 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3]) + output = lower_bound(arr4, 2, start=4, comp=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, 9, end=5, comp=lambda x, y: x > y) + arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3]) + output = lower_bound(arr5, 9, end=5, comp=lambda x, y: x > y) expected_result = 0 assert expected_result == output @@ -313,13 +313,13 @@ def test_longest_increasing_subsequence(): expected_result = [-1, 2, 3, 7, 9, 10] assert str(expected_result) == str(output) - arr3 = ODA(int, [6, 6, 6, 19, 9]) - output = longest_increasing_subsequence(arr3) + arr2 = ODA(int, [6, 6, 6, 19, 9]) + output = longest_increasing_subsequence(arr2) expected_result = [6, 9] assert str(expected_result) == str(output) - arr4 = ODA(int, [5, 4, 4, 3, 3, 6, 6, 8]) - output = longest_increasing_subsequence(arr4) + arr3 = ODA(int, [5, 4, 4, 3, 3, 6, 6, 8]) + output = longest_increasing_subsequence(arr3) expected_result = [3, 6, 8] assert str(expected_result) == str(output) @@ -414,3 +414,32 @@ def test_binary_search(): def test_jump_search(): _test_common_search(jump_search) _test_common_search(jump_search, backend=Backend.CPP) + +def test_shell_sort(): + _test_common_sort(shell_sort) + +def test_radix_sort(): + _test_common_sort(radix_sort) + +def test_reverse_array(): + arr1 = DynamicOneDimensionalArray(int, [1, 2, 3, 4, 5]) + reverse_array(arr1) + assert arr1._data == [5, 4, 3, 2, 1] + + arr2 = DynamicOneDimensionalArray(int, [1, 2, 3]) + original_data = arr2._data.copy() + reverse_array(arr2, start=2, end=1) + assert arr2._data == original_data + + arr3 = DynamicOneDimensionalArray(int, [42]) + reverse_array(arr3, start=0, end=0) + assert arr3._data == [42] + + arr4 = DynamicOneDimensionalArray(int, [5, -3, 1, -10, 7]) + reverse_array(arr4, start=1, end=3) + assert arr4._data == [5, -10, 1, -3, 7] + + arr5 = OneDimensionalArray(int, 5) + arr5._data = [1, 2, 3, 4, 5] + reverse_array(arr5, start=1, end=3) + assert arr5._data == [1, 4, 3, 2, 5]