diff --git a/src/algorithms/sorting/cocktail_shaker_sort/CocktailShakerSort.js b/src/algorithms/sorting/cocktail_shaker_sort/CocktailShakerSort.js new file mode 100644 index 00000000..84698465 --- /dev/null +++ b/src/algorithms/sorting/cocktail_shaker_sort/CocktailShakerSort.js @@ -0,0 +1,32 @@ +import Sort from '../Sort'; + +export default class SelectionSort extends Sort { + sort(originalArray) { + // Clone original array to prevent modification. + const array = [...originalArray]; + + for (let i = 0; i < array.length - 1; i += 1) { + let minIndex = i; + + // Perform visiting callback for the current element being processed. + this.callbacks.visitingCallback(array[i]); + + // Find the index of the minimum element in the remaining unsorted part of the array. + for (let j = i + 1; j < array.length; j += 1) { + // Perform visiting callback for the element being compared. + this.callbacks.visitingCallback(array[j]); + + if (this.comparator.lessThan(array[j], array[minIndex])) { + minIndex = j; + } + } + + // Swap the current element with the minimum element found, if necessary. + if (minIndex !== i) { + [array[i], array[minIndex]] = [array[minIndex], array[i]]; + } + } + + return array; + } +} diff --git a/src/algorithms/sorting/cocktail_shaker_sort/README.md b/src/algorithms/sorting/cocktail_shaker_sort/README.md new file mode 100644 index 00000000..b858c41a --- /dev/null +++ b/src/algorithms/sorting/cocktail_shaker_sort/README.md @@ -0,0 +1,72 @@ +# Counting Sort + +_Read this in other languages:_ +[_Português_](README.pt-BR.md) + +Cocktail shaker sort also known as bidirectional bubble sort cocktail sort, shaker sort +(which can also refer to a variant of selection sort) is an extension of bubble sort. The algorithm extends bubble sort by operating in two directions. While it improves on bubble sort by more quickly moving items to the beginning of the list, it provides only marginal performance improvements. + +Because counting sort uses key values as indexes into an array, +it is not a comparison sort, and the `Ω(n log n)` lower bound for +comparison sorting does not apply to it. Bucket sort may be used +for many of the same tasks as counting sort, with a similar time +analysis; however, compared to counting sort, bucket sort requires +linked lists, dynamic arrays or a large amount of preallocated +memory to hold the sets of items within each bucket, whereas +counting sort instead stores a single number (the count of items) +per bucket. + +Counting sorting works best when the range of numbers for each array +element is very small. + +## Algorithm +**STAGE 1** +The first stage loops through the array from left to right, just like the Bubble Sort. During the loop, adjacent items are compared and if the value on the left is greater than the value on the right, then values are swapped. At the end of the first iteration, the largest number will reside at the end of the array. +**STAGE 2** +The second stage loops through the array in opposite direction- starting from the item just before the most recently sorted item, and moving back to the start of the array. Here also, adjacent items are compared and are swapped if required. +## Example +Example : + +Let us consider an example array (5 1 4 2 8 0 2) + +## First Forward Pass: +(5 1 4 2 8 0 2) ? (1 5 4 2 8 0 2), Swap since 5 > 1 +(1 5 4 2 8 0 2) ? (1 4 5 2 8 0 2), Swap since 5 > 4 +(1 4 5 2 8 0 2) ? (1 4 2 5 8 0 2), Swap since 5 > 2 +(1 4 2 5 8 0 2) ? (1 4 2 5 8 0 2) +(1 4 2 5 8 0 2) ? (1 4 2 5 0 8 2), Swap since 8 > 0 +(1 4 2 5 0 8 2) ? (1 4 2 5 0 2 8), Swap since 8 > 2 +After the first forward pass, the greatest element of the array will be present at the last index of the array. + +## First Backward Pass: +(1 4 2 5 0 2 8) ? (1 4 2 5 0 2 8) +(1 4 2 5 0 2 8) ? (1 4 2 0 5 2 8), Swap since 5 > 0 +(1 4 2 0 5 2 8) ? (1 4 0 2 5 2 8), Swap since 2 > 0 +(1 4 0 2 5 2 8) ? (1 0 4 2 5 2 8), Swap since 4 > 0 +(1 0 4 2 5 2 8) ? (0 1 4 2 5 2 8), Swap since 1 > 0 +After the first backward pass, the smallest element of the array will be present at the first index of the array. + +## Second Forward Pass: +(0 1 4 2 5 2 8) ? (0 1 4 2 5 2 8) +(0 1 4 2 5 2 8) ? (0 1 2 4 5 2 8), Swap since 4 > 2 +(0 1 2 4 5 2 8) ? (0 1 2 4 5 2 8) +(0 1 2 4 5 2 8) ? (0 1 2 4 2 5 8), Swap since 5 > 2 + +## Second Backward Pass: +(0 1 2 4 2 5 8) ? (0 1 2 2 4 5 8), Swap since 4 > 2 +Now, the array is already sorted, but our algorithm doesn’t know if it is completed. The algorithm needs to complete this whole pass without any swap to know it is sorted. +(0 1 2 2 4 5 8) ? (0 1 2 2 4 5 8) +(0 1 2 2 4 5 8) ? (0 1 2 2 4 5 8) + +## Complexity + +| Name | Best | Average | Worst | Memory | Stable | Comments | +| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- | +| **Counting sort** | n | n ^ 2 | n ^ 2 | 1 | Yes | n - array length | + +## References + +- [Wikipedia](https://en.wikipedia.org/wiki/Cocktail_shaker_sort) +- [GeeksForGeeks](https://www.geeksforgeeks.org/cocktail-sort/) +## explaining sort algorithm +-[GeeksForGeeks](https://www.geeksforgeeks.org/sorting-algorithms/) \ No newline at end of file diff --git a/src/algorithms/sorting/cocktail_shaker_sort/__test__/CocktailShakerSort.test.js b/src/algorithms/sorting/cocktail_shaker_sort/__test__/CocktailShakerSort.test.js new file mode 100644 index 00000000..5eaea0c2 --- /dev/null +++ b/src/algorithms/sorting/cocktail_shaker_sort/__test__/CocktailShakerSort.test.js @@ -0,0 +1,60 @@ +import CocktailShakerSort from '../CocktailShakerSort'; +import { + equalArr, + notSortedArr, + reverseArr, + sortedArr, + SortTester, +} from '../../SortTester'; + +// Complexity constants. +const SORTED_ARRAY_VISITING_COUNT = 209; +const NOT_SORTED_ARRAY_VISITING_COUNT = 209; +const REVERSE_SORTED_ARRAY_VISITING_COUNT = 209; +const EQUAL_ARRAY_VISITING_COUNT = 209; + +describe('CocktailShakerSort', () => { + it('should sort array', () => { + SortTester.testSort(CocktailShakerSort); + }); + + it('should sort array with custom comparator', () => { + SortTester.testSortWithCustomComparator(CocktailShakerSort); + }); + + it('should sort negative numbers', () => { + SortTester.testNegativeNumbersSort(CocktailShakerSort); + }); + + it('should visit EQUAL array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + CocktailShakerSort, + equalArr, + EQUAL_ARRAY_VISITING_COUNT, + ); + }); + + it('should visit SORTED array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + CocktailShakerSort, + sortedArr, + SORTED_ARRAY_VISITING_COUNT, + ); + }); + + it('should visit NOT SORTED array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + CocktailShakerSort, + notSortedArr, + NOT_SORTED_ARRAY_VISITING_COUNT, + ); + }); + + it('should visit REVERSE SORTED array element specified number of times', () => { + SortTester.testAlgorithmTimeComplexity( + CocktailShakerSort, + reverseArr, + REVERSE_SORTED_ARRAY_VISITING_COUNT, + ); + }); +});