Skip to content
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

Bidirectional conditional insertion sort #68

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -84,15 +84,19 @@ a set of rules that precisely define a sequence of operations.
* `B` [Linear Search](src/algorithms/search/linear-search)
* `B` [Binary Search](src/algorithms/search/binary-search)
* **Sorting**
* `B` [Bubble Sort](src/algorithms/sorting/bubble-sort)
* `B` [Selection Sort](src/algorithms/sorting/selection-sort)
* `B` [Insertion Sort](src/algorithms/sorting/insertion-sort)
* `B` [Heap Sort](src/algorithms/sorting/heap-sort)
* `B` [Merge Sort](src/algorithms/sorting/merge-sort)
* `B` [Quicksort](src/algorithms/sorting/quick-sort) - in-place and non-in-place implementations
* `B` [Shellsort](src/algorithms/sorting/shell-sort)
* `A` [Counting Sort](src/algorithms/sorting/counting-sort)
* `A` [Radix Sort](src/algorithms/sorting/radix-sort)


* `B` [Bubble Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/bubble-sort)
* `B` [Selection Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/selection-sort)
* `B` [Insertion Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/insertion-sort)
* `B` [Heap Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/heap-sort)
* `B` [Merge Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/merge-sort)
* `B` [Quicksort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/quick-sort) - in-place and non-in-place implementations
* `B` [Shellsort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/shell-sort)
* `A` [Counting Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/counting-sort)
* `A` [Radix Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/radix-sort)
* `A` [Bidirectional Conditional Insertion Sort](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sorting/bidirectional-conditional-insertion-sort)

* **Trees**
* `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
* `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
2,770 changes: 1,385 additions & 1,385 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions src/algorithms/sorting/SortTester.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,30 @@ export const sortedArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
export const reverseArr = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
export const notSortedArr = [15, 8, 5, 12, 10, 1, 16, 9, 11, 7, 20, 3, 2, 6, 17, 18, 4, 13, 14, 19];
export const equalArr = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
export const largeArr = [5, 53, 38, 93, 200, 140, 210, 73, 178, 231, 145, 91, 240,
250, 108, 121, 238, 55, 233, 46, 136, 74, 40, 202, 86, 43, 42, 218, 193, 141,
232, 95, 236, 75, 128, 209, 180, 80, 88, 104, 111, 216, 13, 179, 191, 157, 126,
14, 150, 90, 134, 27, 142, 11, 33, 123, 51, 139, 20, 49, 198, 138, 65, 82, 22,
243, 241, 21, 135, 132, 79, 107, 154, 12, 189, 100, 203, 59, 89, 4, 130, 133,
223, 6, 190, 162, 156, 118, 148, 67, 163, 181, 225, 153, 244, 47, 129, 57, 92,
177, 185, 68, 81, 64, 17, 246, 39, 201, 161, 2, 30, 26, 41, 45, 101, 114, 222,
35, 164, 195, 78, 146, 155, 173, 98, 23, 215, 213, 37, 187, 60, 247, 149, 212,
15, 63, 70, 32, 171, 248, 62, 221, 94, 109, 224, 28, 226, 9, 61, 184, 152, 217,
34, 115, 31, 56, 158, 36, 228, 208, 245, 183, 77, 72, 160, 25, 205, 169, 96, 113,
102, 10, 122, 199, 69, 220, 76, 125, 175, 24, 188, 159, 168, 7, 182, 234, 50, 174,
112, 229, 151, 99, 197, 85, 186, 230, 127, 227, 1, 167, 124, 48, 206, 18, 116, 166,
131, 137, 117, 110, 66, 192, 165, 83, 219, 19, 84, 176, 144, 172, 211, 119, 214, 103,
235, 196, 204, 97, 8, 87, 44, 249, 170, 194, 120, 237, 105, 147, 16, 242, 71, 58, 106,
3, 239, 207, 52, 29, 54, 143];
export const mostlySortedlargeArr = [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, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
134, 135, 136, 137, 138, 139, 140, 237, 105, 147, 16, 242, 71, 58, 106, 3, 239, 207,
52, 29, 54, 143];

export class SortTester {
static testSort(SortingClass) {
@@ -62,4 +86,10 @@ export class SortTester {

expect(visitingCallback).toHaveBeenCalledTimes(numberOfVisits);
}
static testAlgorithmTimeWithoutArray(SortingClass, arrayToBeSorted) {
const visitingCallback = jest.fn();
const callbacks = { visitingCallback };
const sorter = new SortingClass(callbacks);
expect(() => sorter.sort(arrayToBeSorted)).toThrow();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import Sort from '../Sort';
import Comparator from '../../../utils/comparator/Comparator';

export default class BCIS extends Sort {
/**
* This method will swap the values at the given indexs
* @param {Object[]} _array array to be swapped
* @param {number} j index j to be swapped with index i
* @param {number} i index i to be swapped with index j
* @returns {Array} Swapped array will be returned
*/
SWAP(_array = [], i, j) {
const arr = [..._array];
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
return arr;
} // end SWAP
/**
* This will move down the sorted right array and shift the elements down with it until
* the proper index is found.
* @param {Object[]} _array
* @param {*} currentItem
* @param {number} SR
* @param {number} right
* @param {Object} comp
* @returns {Array}
*/
INSRIGHT(_array, currentItem, SR, right, comp) {
const arr = [..._array];
let j = SR;
do {
arr[j - 1] = arr[j];
j += 1;
}
while (comp.lessThanOrEqual(j, right) && comp.greaterThan(currentItem, arr[j]));
arr[j - 1] = currentItem;
return arr;
} // end INSRIGHT
/**
* The will insert a new value into the left sorted array.
* @param {Object[]} _array
* @param {*} currentItem
* @param {number} SL
* @param {number} left
* @param {Object} comp
* @returns {Array}
*/
INSLEFT(_array, currentItem, SL, left, comp) {
const arr = [..._array];
let j = SL;
do {
arr[j + 1] = arr[j];
j -= 1;
}
while (comp.greaterThanOrEqual(j, left) && comp.lessThan(currentItem, arr[j]));
arr[j + 1] = currentItem;
return arr;
} // end INSLEFT
/**
* @param {Object[]} _array
* @param {number} SL
* @param {number} SR
* @param {Object} comp
*/
ISEQUAL(_array, SL, SR, comp) {
let arr = [..._array];
for (let k = SL + 1; k <= SR - 1; k += 1) {
if (comp.compare(arr[k], arr[SL] !== 0)) {
arr = this.SWAP(arr, k, SL);
return arr;
} // end if
} // end for
return -1;
} // end ISEQUAL
/**
* @param {Object[]} _array array to be sorted
* @param {number} left index of left sorted array
* @param {number} right index of right sorted array
*/
SORT(_array = [], left = null, right = null) {
let arr = [..._array];
let SL = left;
let SR = right;
let i;
const comp = new Comparator(((a, b) => {
let x = a;
let y = b;
if (typeof a === 'string') {
x = a.length;
}
if (typeof b === 'string') {
y = b.length;
}
if (x === y) {
return 0;
}
return x < y ? -1 : 1;
}));

do {
const calc = Math.round(SL + ((SR - SL) / 2));
arr = this.SWAP(arr, SR, calc);
if (comp.equal(arr[SL], arr[SR])) {
const copy = [...arr];
arr = this.ISEQUAL(arr, SL, SR, comp);
if (arr === -1) {
arr = [...copy];
break;
} // end if
} // end if
if (comp.greaterThan(arr[SL], arr[SR])) {
arr = this.SWAP(arr, SL, SR);
} // end if
if (SR - SL >= 100) {
for (i = (SL + 1); i <= parseInt((SR - SL) ** 0.5, 10); i += 1) {
if (comp.lessThan(arr[SR], arr[i])) {
arr = this.SWAP(arr, SR, i);
} else if (comp.greaterThan(arr[SL], arr[i])) {
arr = this.SWAP(arr, SL, i);
} // end if
} // end for
} else {
i = SL + 1;
} // end if
const LC = arr[SL];
const RC = arr[SR];
do {
const currentItem = arr[i];
this.callbacks.visitingCallback(arr[i]);
if (comp.greaterThan(currentItem, RC)) {
arr[i] = arr[SR - 1];
arr = this.INSRIGHT(arr, currentItem, SR, right, comp);
SR -= 1;
} else if (comp.lessThan(currentItem, LC)) {
arr[i] = arr[SL + 1];
arr = this.INSLEFT(arr, currentItem, SL, left, comp);
SL += 1; i += 1;
} else {
i += 1;
} // end if
}
while (i < SR);
SL += 1;
SR -= 1;
}
while (SL < SR);
return arr;
} // end Sort
sort(array) {
if (Array.isArray(array) === false) {
throw new Error('An Array object is required.');
}
const len = array.length;
if (len <= 1) return array;
const returnArr = this.SORT(array, 0, array.length - 1);
return returnArr;
} // end sort
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Bidirectional Conditional Insertion Sort

This is a javascript implementation of the Bidirectional Conditional Insertion Sort algorithm published by Future Computer Systems Volume 71, June 2017, Pages 102-112. Read about it here:

[Journal](https://www.sciencedirect.com/science/article/pii/S0167739X17301711?via%3Dihub)

## References
- [Science Direct](https://www.sciencedirect.com/science/article/pii/S0167739X17301711?via%3Dihub)
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import BCIS from '../BCIS';
import {
equalArr,
notSortedArr,
reverseArr,
sortedArr,
largeArr,
mostlySortedlargeArr,
SortTester,
} from '../../SortTester';

// Complexity constants.
const SORTED_ARRAY_VISITING_COUNT = 26;
const NOT_SORTED_ARRAY_VISITING_COUNT = 20;
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 26;
const EQUAL_ARRAY_VISITING_COUNT = 90;
const LARGE_ARRAY_VISITING_COUNT = 705;
const MOSTLY_LARGE_ARRAY_VISITING_COUNT = 293;

describe('BCIS', () => {
it('should sort array', () => {
SortTester.testSort(BCIS);
});

it('should sort array with custom comparator', () => {
SortTester.testSortWithCustomComparator(BCIS);
});
it('should visit EQUAL array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
BCIS,
equalArr,
EQUAL_ARRAY_VISITING_COUNT,
);
});

it('should visit SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
BCIS,
sortedArr,
SORTED_ARRAY_VISITING_COUNT,
);
});

it('should visit NOT SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
BCIS,
notSortedArr,
NOT_SORTED_ARRAY_VISITING_COUNT,
);
});

it('should visit REVERSE SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
BCIS,
reverseArr,
REVERSE_SORTED_ARRAY_VISITING_COUNT,
);
});
it('should visit NOT SORTED LARGE array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
BCIS,
largeArr,
LARGE_ARRAY_VISITING_COUNT,
);
});
it('should visit MOSTLY SORTED LARGE array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
BCIS,
mostlySortedlargeArr,
MOSTLY_LARGE_ARRAY_VISITING_COUNT,
);
});
it('An Array object is required', () => {
SortTester.testAlgorithmTimeWithoutArray(
BCIS,
null,
0,
);
});
});