I have this working insertion sort javascript code.
Array.prototype.insertionSort = function() {
var A = this;
var time = 0;
var n = A.length;
for (var i = 1; i < n; i++) {
var v = A[i];
var j = i-1;
while (j >= 0 && A[j] > v) {
A[j+1] = A[j];
j--;
var temp = new Array(this);
setTimeout(callback(JSON.stringify(temp)), 500*(time++));
}
A[j+1] = v;
var temp = new Array(this);
setTimeout(callback(JSON.stringify(temp)), 500*(time++));
}
return time;
}
I need something similar for quciksort, but I don't know how to do it. Can someone help me, please?
This might be the late posting, but I hope this may help somebody. QuickSort is a Divide and Conquer algorithm. It picks an element as pivot and partitions the given array around the picked pivot. You can select pivot in the following ways.
a)pick the first element as the pivot(Implemented below) b)pick the last element as pivot c)Pick a random element as pivot.d)pick median as a pivot.
if your array is already sorted then time complexity would be O(n^2) but for the average and best case it is O(N log N). It is in place algorithm and nonstable(as partitioning does one of the long-range exchanges). In some cases quicksort might take quadratic time. Quicksort has more overhead even for tiny arrays for sizes less than 10-20. If you use insertion sort for the small size of arrays then you might reduce the processing time by 15-20 %. Sharing my code below, also if you want to reduce more processing time then try to estimate partitioning element to be middle of an array.
class QuickSort{
constructor(){
this.cutoff = 10;
}
partition(arr, lo, hi){
let i = lo, j = hi+1;
debugger;
while(true){
while(arr[++i] < arr[lo]) if(i == hi) break;
while(arr[lo] < arr[--j]) if(j == lo) break;
if(i >= j) break;
this.swap(arr, i, j);
}
this.swap(arr, lo, j);
return j;
}
swap(arr, i, j){
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
sort(arr, lo, hi){
//if(hi <= lo) return;
if((this.cutoff + lo) -1 >= hi){
let insertionSort = new InsertionSort();
insertionSort.sort(arr); return;
}
console.log('Inside Quick Sort');
let j = this.partition(arr, lo, hi);
this.sort(arr, lo, j-1);
this.sort(arr, j+1, hi);
}
shuffle(arr) {
let n = arr.length;
for (let i = 0; i < n; i++) {
// choose index uniformly in [0, i]
let r = Math.floor(Math.random() * (i + 1));
this.swap(arr, i, r);
}
console.log('After uniform random Shuffle');
console.log(arr);
this.sort(arr, 0, n-1);
}
}
class InsertionSort{
constructor(){
this.quickSort = new QuickSort();
}
sort(arr){
console.log("Inside Insertion sort");
for(let i = 1; i < arr.length; i++){
for(let j = i; j > 0; j--){
if(arr[j-1] > arr[j]){
this.quickSort.swap(arr, j, j-1);
}
}
}
}
}
let arr = [101, 22,4,22,2,7,9,10,49,101,-1,4,1,6,99];
console.log('Before sorting an array');
console.log(arr);
let quickSort = new QuickSort();
quickSort.shuffle(arr);
console.log('After Sorting an array');
console.log(arr);
you can get to know the time complexity of from this post time complexity
The Wikipedia article on Quick Sort is pretty thorough and even includes a few pseudocode implementations.
Wikipedia summarizes the problem like so:
Quicksort is a divide and conquer algorithm. Quicksort first divides a
large array into two smaller sub-arrays: the low elements and the high
elements. Quicksort can then recursively sort the sub-arrays.
The steps are:
Pick an element, called a pivot, from the array.
Partitioning: reorder the array so that all elements with values less than the pivot come before the pivot, while all elements with values greater than the pivot come after it (equal values can go either way). After this partitioning, the pivot is in its final position. This is called the partition operation.
Recursively apply the above steps to the sub-array of elements with smaller values and separately to the sub-array of elements with greater values.
The base case of the recursion is arrays of size zero or one, which are in order by definition, so they never need to be sorted.
The pivot selection and partitioning steps can be done in several
different ways; the choice of specific implementation schemes greatly
affects the algorithm's performance.
And just a heads-up, I remember off-by-one errors being the biggest headache with this problem when I did it in school, so if you don't get the right answer right away, double-check your pivot selection and partitioning logic.
You should check google before posting a question: Quicksort is a very common algorithm and you have plenty of resources describing it.
function swap(items, firstIndex, secondIndex){
var temp = items[firstIndex];
items[firstIndex] = items[secondIndex];
items[secondIndex] = temp;
}
function partition(items, left, right) {
var pivot = items[Math.floor((right + left) / 2)],
i = left,
j = right;
while (i <= j) {
while (items[i] < pivot) {
i++;
}
while (items[j] > pivot) {
j--;
}
if (i <= j) {
swap(items, i, j);
i++;
j--;
}
}
return i;
}
function quickSort(items, left, right) {
var index;
if (items.length > 1) {
left = typeof left != "number" ? 0 : left;
right = typeof right != "number" ? items.length - 1 : right;
index = partition(items, left, right);
if (left < index - 1) {
quickSort(items, left, index - 1);
}
if (index < right) {
quickSort(items, index, right);
}
}
return items;
}
// first call
var result = quickSort(items);
Code credits: N.C.Zakas
Furthermore, note that Quicksort is used by V8 when calling Array.prototype.sort() on arrays > 10 items (V8 source). On smaller arrays, InsertionShort is actually faster.
heres a short version of quick sort written in pure JS!
as seen in Intro To Algorithms
var partition = function (arr, low, high) {
var x = arr[high]
var i = low - 1
for (var j = low; j <= high - 1; j++) {
if (arr[j] <= x) {
i++
var temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
}
var temp = arr[i + 1]
arr[i + 1] = arr[high]
arr[high] = temp
return i + 1
}
var quickSort = function (arr, low, high) {
if (low < high) {
index = partition(arr,low,high)
if (low < index-1) quickSort(arr,low,index-1)
if (index+1 < high) quickSort(arr,index+1,high)
}
}
var list2 = [1000,13,12,1001,82,1,2,4,3,0]
console.log(quickSort(list2,0,list2.length))
also on my GitHub
Related
This question is specific to the hacker rank problem Largest Permutation
I wrote a solution in javascript that works for most cases (A), but there are some select test cases where it times out. Ok... so then it must not be very efficient. I found someone else's solution to the problem (B) which is highly iterative. This loops through the given array at least once total, and then through the rest of the array at least K times. This seems very inefficient to me but this approach did not cause a timeout and I can't figure out why that is... one of these seems to be A: O(n) vs B: O(n^2) in my analysis but I guess im wrong... so I am asking here to see if anyone can help me figure out why this is or what the correct algorithm speed analysis is
These code snippets are the function body of a function that takes arr as an argument
A) My solution
const indices = {}
arr.forEach((n,i) => {
indices[n] = i
})
let count = 0
while(count < k){
// swap to earliest index (count)
const targetValue = arr.length - count
const targetIndex = indices[targetValue]
let tmp = arr[count]
arr[count] = arr[targetIndex]
arr[targetIndex] = tmp
//update indices
indices[targetValue] = count
indices[tmp] = targetIndex
count++
}
return arr
B) an alternative that to me should be slower but seems to be faster...
let n = arr.length
for(var i = 0; i < n - 1; i++) {
if( k > 0) {
var max = i;
for(var j = i + 1; j < n; j++){
if(arr[j] > arr[max])
max = j;
}
if(max != i) {
var temp = arr[max];
arr[max] = arr[i];
arr[i] = temp;
k--;
}
}
}
return arr
There are these issues with your implementation:
When k is larger than arr.length, then the arr.length - count will become 0 or less and thus targetValue will be a value that is not in the array. And so targetIndex will be undefined.
Your loop represents O(๐), and as ๐ can be much larger than ๐, this can make a huge difference in running time.
When the swap is a none-operation because the value happens to already be in its optimal position, then count is still incremented, reducing the number of actual swaps that are done.
Not an issue, but a for loop seems more appropriate here than a while.
Here is a correction of your main loop:
for (let count = 0; count < k && count < arr.length; count++) {
const targetValue = arr.length - count
if (arr[count] != targetValue) { // target not at optimal index?
// swap to earliest index (count)
const targetIndex = indices[targetValue]
let tmp = arr[count]
arr[count] = arr[targetIndex]
arr[targetIndex] = tmp
//update indices
indices[targetValue] = count
indices[tmp] = targetIndex
}
else k++; // Allow one more swap, as this was a no-op
}
I'm a frontend dev looking into learning some basic CS fundamentals. I've been doing some leetcode and came across the maximum sub array problem.
https://leetcode.com/problems/maximum-subarray/
I'm try to implement a D/C solution after watching a few videos and came up with the following solution. However, this is not turning out the correct response.
For the following input I am supposed to be returning 6 but I keep returning 4 instead.
Input:
[-2,1,-3,4,-1,2,1,-5,4]
Expected:
6
Actual:
4
Solution:
var maxSubArray = function(nums) {
if (nums.length === 1) {
return nums[0];
}
const mid = Math.ceil(nums.length / 2);
let LSS = maxSubArray(nums.slice(0, mid));
let RSS = maxSubArray(nums.slice(mid));
let leftSum = 0;
let rightSum = 0;
let sum = 0;
nums.slice(0, mid).forEach(num => {
sum += num;
leftSum = Math.max(sum, leftSum);
});
sum = 0;
nums.slice(mid).forEach(num => {
sum += num;
rightSum = Math.max(sum, rightSum);
});
return Math.max(RSS, LSS, (leftSum + rightSum));
};
can someone please explain what I am missing in this solution?
This is a dynamic programming solution, which'll pass:
var maxSubArray = function(nums) {
for (let index = 1; index < nums.length; index++) {
nums[index] = Math.max(nums[index], nums[index] + nums[index - 1]);
}
return Math.max.apply(Math, nums);
};
Or:
var maxSubArray = function(nums) {
for (let index = 1; index < nums.length; index++) {
nums[index] = Math.max(nums[index], nums[index] + nums[index - 1]);
}
return Math.max(...nums);
};
By looking at this Python version, you can see how the algorithm works:
class Solution:
def maxSubArray(self, nums):
for index in range(1, len(nums)):
if nums[index - 1] > 0:
nums[index] += nums[index - 1]
return max(nums)
and here is a LeetCode's divide and conquer solution (similar to your method) based on Java, there was no JavaScript snippet in there:
class Solution {
public int crossSum(int[] nums, int left, int right, int p) {
if (left == right) return nums[left];
int leftSubsum = Integer.MIN_VALUE;
int currSum = 0;
for(int i = p; i > left - 1; --i) {
currSum += nums[i];
leftSubsum = Math.max(leftSubsum, currSum);
}
int rightSubsum = Integer.MIN_VALUE;
currSum = 0;
for(int i = p + 1; i < right + 1; ++i) {
currSum += nums[i];
rightSubsum = Math.max(rightSubsum, currSum);
}
return leftSubsum + rightSubsum;
}
public int helper(int[] nums, int left, int right) {
if (left == right) return nums[left];
int p = (left + right) / 2;
int leftSum = helper(nums, left, p);
int rightSum = helper(nums, p + 1, right);
int crossSum = crossSum(nums, left, right, p);
return Math.max(Math.max(leftSum, rightSum), crossSum);
}
public int maxSubArray(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
}
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
For interviews, we'd like to write bug-free and clean codes based on standards and conventions (e.g., c1, 2, c++1, 2, java1, 2, c#1, 2, python1, javascript1, go1, rust1).
use Kadaneโs algorithm , in my opinion is best way with O(n) :
var maxSequence = function(arr){
var curr_max = 0, max_so_far = 0;
for(var i = 0; i < arr.length; i++){
curr_max = Math.max(0, curr_max + arr[i]);
max_so_far = Math.max(curr_max, max_so_far);
}
return max_so_far;
}
I believe that since leftSum in your code is accumulating from the left-bound towards the right, it is not forming a best sum that's contiguous in the middle. The solution offered in Emma's answer correctly iterates outward from the middle both to the left and to the right, therefore finding the best sum that "crosses" the middle as one of our options to consider.
Hi I did not pass the all the tests, only 9/16. so I want to know what is the problem in my code
problem link:https://www.hackerrank.com/challenges/electronics-shop/problem
function getMoneySpent(keyboards, drives, b) {
let arr = [];
let lenOfArr1 = keyboards.length;
let lenOfArr2 = drives.length;
let j = drives.length;
arr = keyboards.slice(0);
for (let number of drives) {
arr.push(number);
}
return (challenge(arr, lenOfArr1, b));
}
function challenge(arr, lenOfArr1, m) {
let result = [];
let j = lenOfArr1;
for (let i = 0; i < lenOfArr1; i++) {
if (arr[i] >= m || arr[j] >= m) return -1;
if (arr[i] + arr[j] < m) {
result.push(arr[i] + arr[j]);
}
i--;
j++;
if (j == arr.length) {
i++;
j = lenOfArr1;
}
}
if (result.length == 0) return -1;
return result.sort()[result.length - 1];
}
From the given constraints in the problem statement, you don't need such a complex solution.
Here is the Algorithm:
Initialize a variable maxValue to have value as -1.
Start two for loops over drives and keyboards and take all combinations and sum the value of each drive with each keyboard.
Inside the for loops, check if the sum of drive + keyboard and it should be less than or equal to the money Monica has and keep track of the maximum value you get from any combination in maxValue.
After the code computation, return maxValue.
Code:-
function getMoneySpent(keyboards, drives, b) {
let maxValue = -1;
for (let drive of drives) {
for(let keyboard of keyboards) {
let cost = drive + keyboard;
if(cost > maxValue && cost <= b) {
maxValue = cost;
}
}
}
return maxValue;
}
Overall Time complexity - O(n^2).
Hope this helps!
The following line causes your code to return -1 when there might be a solution:
if (arr[i] >= m || arr[j] >= m) return -1;
Even though one price may be above budget, you need to consider that there might be cheaper items available. You might even already have solutions in result, or they might still be found in a later iteration.
It is not clear to me why you first create a merged array of both input arrays. It does not seem to bring any benefit.
If you sort the input arrays, you can use a two-pointer system where you decide which one to move depending on whether the current sum is within limits or it is too great:
function getMoneySpent(keyboards, drives, b) {
keyboards = [...keyboards].sort((a, b) => b - a) // descending
drives = [...drives].sort((a, b) => a - b); // ascending
let k = keyboards.length - 1; // cheapest
let d = drives.length - 1; // most expensive
let maxSum = -1;
while (d >= 0 && k >= 0) {
let sum = keyboards[k] + drives[d];
if (sum === b) return b;
if (sum < b) {
if (sum > maxSum) maxSum = sum;
k--; // will increase sum
} else {
d--; // will decrease sum
}
}
return maxSum;
}
I'm a novice programmer who just started learning JavaScript. While I was reading about array sorting, I came across QuickSort and looked into its algorithm. Then I thought I could come up with my own 'algorithm'.
My solution works like this. It starts by looking for the smallest and largest values and swaps them with the corner values simultaneously. It then runs recursively until both ends are equal or pass each other.
So, is this a new algorithm or a rediscovery?
This is my version of a sorting algorithm. My question is :
Do you know of any other sorting algorithm that works like this?
how can I test it's speed vis-a-vis quicksort, bubble sort...?
'use strict';
// + Name : CornerSort function for sorting arrays +
// + Author : Kedyr +
// + Date : Jan 15 , 2020 +
// + Description : The corner sort function is my first shot at sorting +
// + algorithms.After looking into the quick sort algorithm +
// + i tried to come up with my own sorting logic.It works +
// + by finding the lowest and highest values in an array +
// + and then replacing these values with the corner values. +
// + These process continues recursively until all values are +
// + correctly sorted.Now , from what i can see i believe it +
// + to be a stable sorting algorithm.I haven't tested its +
// + sorting speed and would love it if some one did that. +
// + That is , if it is not an already known algorithm which +
// + I haven't come across yet.Whatever the case is , I am +
// + going to elaborately discuss my 'new' discovery step by +
// + step in the comments . So please bear with me . +
// + For the geniuses who created this algorith before me , +
// + (if there are any), I bow down to you dear masters. +
// + please forgive my insolence. You know I wouldn't do it +
// + intentionally . It is just that genius minds think +
// + alike . :) +
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function cornerSort(array, startIndex = 0, endIndex = array.length - 1) {
// These checks if the function is already ordered .
if (checkOrder(array, startIndex, endIndex)) {
return;
}
// the lowest and highest values.initialised to corner values.
let lowest = array[startIndex],
highest = array[endIndex];
// Indices for the lowest and highest numbers in each recursion.
let lowIndex = startIndex,
highIndex = endIndex;
let temp;
if (startIndex >= endIndex) {
return;
}
for (let i = startIndex; i <= endIndex; i++) {
if (array[i] >= highest) {
//'or-equal-to' is added to ensure the sorting doesn't change the
//order of equal numbers.Thus , a stable sorting algorithm.
highest = array[i];
highIndex = i;
}
if (array[i] < lowest) {
lowest = array[i];
lowIndex = i;
}
}
// A PRECAUTION 'IF' : for when the highest number is at the beginning of the array.
if (highIndex == startIndex) {
highIndex = lowIndex;
}
// The opposite case , lowest number at the end of array ; doesn't cause any issues.
if (lowIndex != startIndex) {
temp = array[startIndex];
array[startIndex] = array[lowIndex];
array[lowIndex] = temp;
}
if (endIndex != highIndex) {
temp = array[endIndex];
array[endIndex] = array[highIndex];
array[highIndex] = temp;
}
cornerSort(array, ++startIndex, --endIndex);
return array;
}
// The CHECKORDER function checks if the array is actually sorted so as to avoid
// unnecessary rounds of recursion.
function checkOrder(array, startIndex, endIndex) {
let unsortedCount = 0; // count of cases where array[i] > array[i+1].
for (let i = startIndex; i <= endIndex; i++) {
if (array[i] > array[i + 1]) {
unsortedCount++;
}
}
if (!unsortedCount) {
return true;
}
return false;
}
//..............................................................................
It's certainly not impossible to invent a genuinely new sorting algorithm, since there are so many different possible ways to sort. However, your algorithm exists: it is called double-ended selection sort.
Selection sort works by iteratively selecting the smallest element in the unsorted part of the array, and swapping it into place. Your algorithm is a variation of selection sort because it also selects the largest element in the unsorted part of the array and swaps that into place in the same iteration of the outer loop. This means the unsorted part is a middle segment rather than an end segment of the array.
Your implementation is also quite a lot more complicated than it needs to be: here's a simplified version which should be very slightly more efficient due to the else if in the inner loop. An element cannot be both lower than lowest and higher than highest. Since we're scanning for both the lowest and highest elements, it's also possible to detect when the "unsorted" part of the array is constant (i.e. all its values are the same) and break early.
function doubleEndedSelectionSort(arr) {
for(var i = 0, j = arr.length - 1; i < j; i++, j--) {
var minIdx = i, maxIdx = i;
var lowest = arr[minIdx], highest = arr[maxIdx];
for(var k = i + 1; k <= j; k++) {
var value = arr[k];
if(value < lowest) {
minIdx = k;
lowest = value;
} else if(value > highest) {
maxIdx = k;
highest = value;
}
}
if(minIdx === maxIdx) { break; } // the "unsorted" part of the array is constant
var iValue = arr[i], jValue = arr[j];
arr[minIdx] = iValue;
arr[i] = lowest;
arr[maxIdx] = jValue;
arr[j] = highest;
}
}
Regarding performance, the time complexity of the algorithm is the same as selection sort's O(nยฒ), because it still has nested loops which iterate O(n) times each. In your implementation, the outer loop is replaced with recursion, but it still recurses O(n) times, so the effect is the same. That means for large enough inputs it will perform worse than O(n log n) sorting algorithms such as quicksort, merge sort, or heap sort.
Before I start, I'd suggest that this is probably more appropriate for computer science
I can't really say whether or not this is a new sorting algorithm; I think for someone to answer such a question they'd need to have a fairly comprehensive knowledge of sorting algorithms, as there are quite a few.
What I can demonstrate is how to compare performance. You'd need to do something similar in other languages, if you want to test those, and this only tests the first implementations of each I could find on the internet.
To properly understand the speed of the algorithm you have, you'd probably need to find a way to describe how it performs in different circumstances; I understand this normally involves a relatively mathematical description of the algorithm; perhaps someone with more computer science knowledge could fill in that gap.
But, despite those provisos, here's something that can work in stack overflow:
function cornerSort(array, startIndex = 0, endIndex = array.length - 1) {
// These checks if the function is already ordered .
if (checkOrder(array, startIndex, endIndex)) {
return;
}
// the lowest and highest values.initialised to corner values.
let lowest = array[startIndex],
highest = array[endIndex];
// Indices for the lowest and highest numbers in each recursion.
let lowIndex = startIndex,
highIndex = endIndex;
let temp;
if (startIndex >= endIndex) {
return;
}
for (let i = startIndex; i <= endIndex; i++) {
if (array[i] >= highest) {
//'or-equal-to' is added to ensure the sorting doesn't change the
//order of equal numbers.Thus , a stable sorting algorithm.
highest = array[i];
highIndex = i;
}
if (array[i] < lowest) {
lowest = array[i];
lowIndex = i;
}
}
// A PRECAUTION 'IF' : for when the highest number is at the beginning of the array.
if (highIndex == startIndex) {
highIndex = lowIndex;
}
// The opposite case , lowest number at the end of array ; doesn't cause any issues.
if (lowIndex != startIndex) {
temp = array[startIndex];
array[startIndex] = array[lowIndex];
array[lowIndex] = temp;
}
if (endIndex != highIndex) {
temp = array[endIndex];
array[endIndex] = array[highIndex];
array[highIndex] = temp;
}
cornerSort(array, ++startIndex, --endIndex);
return array;
}
// The CHECKORDER function checks if the array is actually sorted so as to avoid
// unnecessary rounds of recursion.
function checkOrder(array, startIndex, endIndex) {
let unsortedCount = 0; // count of cases where array[i] > array[i+1].
for (let i = startIndex; i <= endIndex; i++) {
if (array[i] > array[i + 1]) {
unsortedCount++;
}
}
if (!unsortedCount) {
return true;
}
return false;
}
function bubbleSort(a) {
var swapped;
do {
swapped = false;
for (var i=0; i < a.length-1; i++) {
if (a[i] > a[i+1]) {
var temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
swapped = true;
}
}
} while (swapped);
}
function swap(items, leftIndex, rightIndex){
var temp = items[leftIndex];
items[leftIndex] = items[rightIndex];
items[rightIndex] = temp;
}
function partition(items, left, right) {
var pivot = items[Math.floor((right + left) / 2)], //middle element
i = left, //left pointer
j = right; //right pointer
while (i <= j) {
while (items[i] < pivot) {
i++;
}
while (items[j] > pivot) {
j--;
}
if (i <= j) {
swap(items, i, j); //sawpping two elements
i++;
j--;
}
}
return i;
}
function quickSort(items, left, right) {
var index;
if (items.length > 1) {
index = partition(items, left, right); //index returned from partition
if (left < index - 1) { //more elements on the left side of the pivot
quickSort(items, left, index - 1);
}
if (index < right) { //more elements on the right side of the pivot
quickSort(items, index, right);
}
}
return items;
}
const testData = [...Array(10000).keys()].map(x => Math.random() * 1000)
console.time('corner')
const x = cornerSort(testData);
console.timeEnd('corner')
console.time('bubble')
const y = bubbleSort(testData);
console.timeEnd('bubble')
console.time('quick')
const z = quickSort(testData);
console.timeEnd('quick')
I have a huge array of length over 200,000. I need to get top 10 values. I know sort is not the best solution. I have tried the following solution:
const sortBy = 'key';
const results = [];
const m = 10;
const N = array.length;
while (array.length && results.length < m) {
let currentMax = 0;
let currentMaxIndex = 0;
array.forEach((record, i) => {
if (record[sortBy] >= currentMax) {
currentMax = record[sortBy];
currentMaxIndex = i;
}
});
results.push(...array.splice(currentMaxIndex, 1));
}
Here array is an Array of length 200,000.
Problem is, I think if m equals N, then this
is going to take more time than sort itself. I want to know if there
is a better solution, that can handle both the cases.
Thank you for the help, but the actual question is m โ (0, N). m can take any value up to N. So, at which point would it be advisable to switch to in-built sort?
As per my understanding as m reaches N the complexity increases and sort is the best solution when m === N
I have tested with the example provided by #t-j-crowder here. A test to get top 10 from 100 entries.
When testing with different values in m, the faster algorithm is changing to sort at m === 85. So, I want to find out if there is any way to determine when we should switch back to sort, to have optimal performance in all cases.
You don't need to sort the whole array, you just need to insert into your top 10 array in numeric order and drop any additional entries:
var a = Array.from({length:100}, () => Math.floor(Math.random() * 1000000));
var check = a.slice().sort((left, right) => right - left).slice(0, 10);
console.log("check", check);
function checkResult(top10) {
var n;
for (n = 0; n < 10; ++n) {
if (top10[n] !== check[n]) {
console.log("Error in test at #" + n + ", expected " + check[n] + ", got " + top10[n]);
return false;
}
}
return true;
}
var top10 = [];
var cutoff = -Infinity;
var full = false;
var n, len, value;
for (n = 0, len = a.length; n < len; ++n) {
value = a[n];
// If the value may be in the top 10...
if (!full || value >= cutoff) {
// Insert into top10
let found = false;
for (let n = 0; n < top10.length; ++n) {
if (top10[n] <= value) {
top10.splice(n, 0, value);
found = true;
break;
}
}
if (!found) {
top10.push(value);
}
// Trim it
if (top10.length > 10) {
full = true;
top10.length = 10;
}
// Remember the lowest of the top 10 candidates we have now so we can not bother with checking lower ones
cutoff = top10[top10.length - 1];
}
}
console.log("top10", top10);
console.log("Good? " + checkResult(top10));
.as-console-wrapper {
max-height: 100% !important;
}
You can probably tidy that up a bit, optimize it further, but you get the idea. Maintaining a list of just the top 10 highest you've seen, dropping ones off the bottom if others join it.
Benchmark here, easily beats sorting and then grabbing the top 10 on Chrome and Firefox; the converse is true on Edge.
function limitSort(array, sortBy, limit) {
const result = [];
// Iterate over the array *once*
for(const el of array) {
// Exclude all elements that are definetly not in the resulting array
if(result.length < limit || el[sortBy] > result[0]) {
// Determine the position were the element needs to be inserted (Insertion sort)
let insertAt = 0;
while(insertAt < result.length && result[insertAt][sortBy] < el[sortBy]) insertAt++;
// Finally insert it
result.splice(insertAt, 0, el);
// If the results are getting to long, remove the lowest element
if(result.length > limit)
result.splice(0, 1);
}
}
return result;
}
This implements the algorithm, Niet the Dark Absol proposed above. Try it!