From 149444b47cc0664934afc3ce5829f36e5e17a20d Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Wed, 18 Jul 2018 14:35:02 +0300 Subject: [PATCH] Add Interpolation Search. --- README.md | 1 + .../search/interpolation-search/README.md | 40 ++++++++++++++ .../__test__/interpolationSearch.test.js | 24 +++++++++ .../interpolationSearch.js | 52 +++++++++++++++++++ src/algorithms/search/jump-search/README.md | 2 +- .../search/jump-search/jumpSearch.js | 2 +- 6 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/algorithms/search/interpolation-search/README.md create mode 100644 src/algorithms/search/interpolation-search/__test__/interpolationSearch.test.js create mode 100644 src/algorithms/search/interpolation-search/interpolationSearch.js diff --git a/README.md b/README.md index 5302c78f..2bd47587 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ a set of rules that precisely define a sequence of operations. * `B` [Linear Search](src/algorithms/search/linear-search) * `B` [Jump Search](src/algorithms/search/jump-search) (or Block Search) - search in sorted array * `B` [Binary Search](src/algorithms/search/binary-search) - search in sorted array + * `B` [Interpolation Search](src/algorithms/search/interpolation-search) - search in uniformly distributed sorted array * **Sorting** * `B` [Bubble Sort](src/algorithms/sorting/bubble-sort) * `B` [Selection Sort](src/algorithms/sorting/selection-sort) diff --git a/src/algorithms/search/interpolation-search/README.md b/src/algorithms/search/interpolation-search/README.md new file mode 100644 index 00000000..e91154a8 --- /dev/null +++ b/src/algorithms/search/interpolation-search/README.md @@ -0,0 +1,40 @@ +# Interpolation Search + +**Interpolation search** is an algorithm for searching for a key in an array that +has been ordered by numerical values assigned to the keys (key values). + +For example we have a sorted array of `n` uniformly distributed values `arr[]`, +and we need to write a function to search for a particular element `x` in the array. + +**Linear Search** finds the element in `O(n)` time, **Jump Search** takes `O(√ n)` time +and **Binary Search** take `O(Log n)` time. + +The **Interpolation Search** is an improvement over Binary Search for instances, +where the values in a sorted array are _uniformly_ distributed. Binary Search +always goes to the middle element to check. On the other hand, interpolation +search may go to different locations according to the value of the key being +searched. For example, if the value of the key is closer to the last element, +interpolation search is likely to start search toward the end side. + +To find the position to be searched, it uses following formula: + +``` +// The idea of formula is to return higher value of pos +// when element to be searched is closer to arr[hi]. And +// smaller value when closer to arr[lo] +pos = lo + ((x - arr[lo]) * (hi - lo) / (arr[hi] - arr[Lo])) + +arr[] - Array where elements need to be searched +x - Element to be searched +lo - Starting index in arr[] +hi - Ending index in arr[] +``` + +## Complexity + +**Time complexity**: `O(log(log(n))` + +## References + +- [GeeksForGeeks](https://www.geeksforgeeks.org/interpolation-search/) +- [Wikipedia](https://en.wikipedia.org/wiki/Interpolation_search) diff --git a/src/algorithms/search/interpolation-search/__test__/interpolationSearch.test.js b/src/algorithms/search/interpolation-search/__test__/interpolationSearch.test.js new file mode 100644 index 00000000..7ddc548b --- /dev/null +++ b/src/algorithms/search/interpolation-search/__test__/interpolationSearch.test.js @@ -0,0 +1,24 @@ +import interpolationSearch from '../interpolationSearch'; + +describe('interpolationSearch', () => { + it('should search elements in sorted array of numbers', () => { + expect(interpolationSearch([], 1)).toBe(-1); + expect(interpolationSearch([1], 1)).toBe(0); + expect(interpolationSearch([1], 0)).toBe(-1); + expect(interpolationSearch([1, 1], 1)).toBe(0); + expect(interpolationSearch([1, 2], 1)).toBe(0); + expect(interpolationSearch([1, 2], 2)).toBe(1); + expect(interpolationSearch([10, 20, 30, 40, 50], 40)).toBe(3); + expect(interpolationSearch([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 14)).toBe(13); + expect(interpolationSearch([1, 6, 7, 8, 12, 13, 14, 19, 21, 23, 24, 24, 24, 300], 24)).toBe(10); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 600)).toBe(-1); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 1)).toBe(0); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 2)).toBe(1); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 3)).toBe(2); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 700)).toBe(3); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 800)).toBe(4); + expect(interpolationSearch([0, 2, 3, 700, 800, 1200, 1300, 1400, 1900], 1200)).toBe(5); + expect(interpolationSearch([1, 2, 3, 700, 800, 1200, 1300, 1400, 19000], 800)).toBe(4); + expect(interpolationSearch([0, 10, 11, 12, 13, 14, 15], 10)).toBe(1); + }); +}); diff --git a/src/algorithms/search/interpolation-search/interpolationSearch.js b/src/algorithms/search/interpolation-search/interpolationSearch.js new file mode 100644 index 00000000..a7bacc53 --- /dev/null +++ b/src/algorithms/search/interpolation-search/interpolationSearch.js @@ -0,0 +1,52 @@ +/** + * Interpolation search implementation. + * + * @param {*[]} sortedArray - sorted array with uniformly distributed values + * @param {*} seekElement + * @return {number} + */ +export default function interpolationSearch(sortedArray, seekElement) { + let leftIndex = 0; + let rightIndex = sortedArray.length - 1; + + while (leftIndex <= rightIndex) { + const rangeDelta = sortedArray[rightIndex] - sortedArray[leftIndex]; + const indexDelta = rightIndex - leftIndex; + const valueDelta = seekElement - sortedArray[leftIndex]; + + // If valueDelta is less then zero it means that there is no seek element + // exists in array since the lowest element from the range is already higher + // then seek element. + if (valueDelta < 0) { + return -1; + } + + // If range delta is zero then subarray contains all the same numbers + // and thus there is nothing to search for unless this range is all + // consists of seek number. + if (!rangeDelta) { + // By doing this we're also avoiding division by zero while + // calculating the middleIndex later. + return sortedArray[leftIndex] === seekElement ? leftIndex : -1; + } + + // Do interpolation of the middle index. + const middleIndex = leftIndex + Math.floor(valueDelta * indexDelta / rangeDelta); + + // If we've found the element just return its position. + if (sortedArray[middleIndex] === seekElement) { + return middleIndex; + } + + // Decide which half to choose for seeking next: left or right one. + if (sortedArray[middleIndex] < seekElement) { + // Go to the right half of the array. + leftIndex = middleIndex + 1; + } else { + // Go to the left half of the array. + rightIndex = middleIndex - 1; + } + } + + return -1; +} diff --git a/src/algorithms/search/jump-search/README.md b/src/algorithms/search/jump-search/README.md index 86dc933d..b711a773 100644 --- a/src/algorithms/search/jump-search/README.md +++ b/src/algorithms/search/jump-search/README.md @@ -23,5 +23,5 @@ minimum when `m = √n`. Therefore, the best step size is `m = √n`. ## References -- [Wikipedia](https://en.wikipedia.org/wiki/Jump_search) - [GeeksForGeeks](https://www.geeksforgeeks.org/jump-search/) +- [Wikipedia](https://en.wikipedia.org/wiki/Jump_search) diff --git a/src/algorithms/search/jump-search/jumpSearch.js b/src/algorithms/search/jump-search/jumpSearch.js index 37562b06..b8c8c24e 100644 --- a/src/algorithms/search/jump-search/jumpSearch.js +++ b/src/algorithms/search/jump-search/jumpSearch.js @@ -3,7 +3,7 @@ import Comparator from '../../../utils/comparator/Comparator'; /** * Jump (block) search implementation. * - * @param {*[]} sortedArray. + * @param {*[]} sortedArray * @param {*} seekElement * @param {function(a, b)} [comparatorCallback] * @return {number}