I recently started learning algorithms based on the book Data Structures and Algorithms with JavaScript from O'Reilly.
I stopped on Chapter 12 - Sorting Algorithms.
I can not understand how Insertion Sort works.
Here is the code I am working with: pasteBin - Insertion Sort
Below is the part that is confusing to me:
function insertionSort() {
var temp, inner;
for (var outer = 1; outer <= this.dataStore.length - 1; ++outer) {
temp = this.dataStore[outer];
inner = outer;
while (inner > 0 && (this.dataStore[inner-1] >= temp)) {
this.dataStore[inner] = this.dataStore[inner-1];
--inner;
}
this.dataStore[inner] = temp;
}
console.log(this.toString());
}
Could anyone help and comment on this code?
It's a sorting algorithm which starts at the beginning of the array and passes through until the end. For the item at each index, it goes back through the items at earlier indices and checks to see if it should be placed before them. If so, it swaps indices with the larger value until it settles into the index it should have.
Here's the code with some commentary, hopefully it is helpful to you.
function insertionSort() {
/* Set up local vars */
var temp, inner;
/* Start at index 1, execute outer loop once per index from 1 to the last index */
for (var outer = 1; outer <= this.dataStore.length - 1; ++outer) {
/* Store the value at the current index */
temp = this.dataStore[outer];
/* Set up temporary index to decrement until we find where this value should be */
inner = outer;
/* As long as 'inner' is not the first index, and
there is an item in our array whose index is less than
inner, but whose value is greater than our temp value... */
while (inner > 0 && (this.dataStore[inner-1] >= temp)) {
/* Swap the value at inner with the larger value */
this.dataStore[inner] = this.dataStore[inner-1];
/* Decrement inner to keep moving down the array */
--inner;
}
/* Finish sorting this value */
this.dataStore[inner] = temp;
}
console.log(this.toString());
}
Here is a jsfiddle with lots of console printouts so you can step through it and see what happens at each step.
The main concept behind insertion sort is to sort elements by comparison.
The comparison occurs in your case for a dataStore array, containing what we assume to be comparable elements such as numbers.
In order to compare element by element, this insertion sort algorithm starts at the beginning of the dataStore array and will continue to run until the end of the array has been reached. This is accomplished by a for loop:
for (var outer = 1; outer <= this.dataStore.length - 1; ++outer)
As the algorithm goes through each element in order, it will:
Store the current element we are visiting in the array in a variable called temp.
Keep track of the location we are in the array via the inner and outer variables, where:
outer is our counter.
inner is a flag to determine whether or not we are visiting the first element in the array. Why is this important? Because there is no point in doing a comparison on the first element, on the first try.
It will compare the current element temp with each element that came before it in the dataStore array. This is accomplished by an inner while loop as seen here:
while (inner > 0 && (this.dataStore[inner-1] >= temp))
This tells you that, as long as all previous visited elements in the dataStore array are greater than or equal to temp, our temporary variable used to store the current element; we want to swap these values.
Swapping them will accomplish the following:
Assume all elements before this.dataStore[inner] are greater than 10, and the currently visited element this.dataStore[inner] equals 5. This logically means that 5 needs to be at the beginning of the array. In such case we would continue to pass 5 all the way down to this.datastore[0] thanks to the while loop. Thus making 5 the first element in the array.
At the end of this swapping, the value in temp is placed accordingly to the current position we are in the array, just to remind you which position this is, it's stored the variable outer.
TLDR: I also like Justin Powell's answer as it sticks with the code, but I thought a walk through would be more useful depending on your level of understanding. I hope it helps!
Starting from the inner loop check if the current element is greater than the previous if yes, exit the loop since everything is sorted for the iteration, if not swap elements because the current needs to be moved to the left because it's smaller than the previous. The inner loop makes sure to swap elements until you encounter a sorted element which results with the break exiting the loop.
After the swap occurs decrease the outer index (i) since you are going downwards to check if the upcoming element is lesser than the previous, you cannot keep the outer index (i) static.
At last, the memIndex variable serves as a reset index because at the end of your inner loop you want to move to the next index. Index (i) should always be placed at the last element of the sorted array so that the inner loop can start the comparison again.
function insertionSort(arr) {
let memIndex = 0
for (let i = 0; i < arr.length; i++) {
memIndex = i;
for (let j = i + 1; j >= 0; --j) {
if (arr[j] >= arr[i]) {
break;
}
if (arr[j] < arr[i]) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i = i - 1;
}
}
i = memIndex;
}
return arr;
}
const arr = [5, 1, 6, 2, 4, 9, 9, 3, 1, 1, 1];
console.log('Unsorted array', arr);
console.log('Sorted array:', insertionSort(arr));
const insertionSort = array => {
const arr = Array.from(array); // avoid side effects
for (let i = 1; i < arr.length; i++) {
for (let j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
[arr[j], arr[j - 1]] = [arr[j - 1], arr[j]];
}
}
return arr;
};
insertion sort means first step we are going to take one value from unsorted sublets and second step we are going to find out appropriate place for that in sorted sublet after find out right place we will insert that value in sorted sublet.
you can get deep understanding from this video links:-
Insertion Sort Algorithm | Data Structure
function insertionSort(arr) {
for (let i = 1; i < arr.length; i++) {
// first step
let currentValue = arr[i]
let j
for (j = i - 1; j >= 0 && arr[j] > currentValue; j--) {
// second step
arr[j + 1] = arr[j]
}
arr[j + 1] = currentValue
}
return arr
}
console.log(insertionSort([5,2,6,3,1,4]))
Related
I know generally speaking that inserting values into an array are constant time. However, I'm struggling to find the time complexity for inserting elements into an array by index for the first time (i.e., initializing element at index[15] on an empty array).
For example:
// input of [-3,5,-2,10]
const result = [];
let left = 0;
let right = array.length - 1;
for(let i = right; i >= 0; i--) {
if(array[left] < array[right]) {
result[i] = array[right];
right--;
} else {
result[i] = array[left];
left++;
}
}
Based on the fact that javascript arrays are dynamically sized, they do not behave like arrays in languages I've used in the past. Is this still constant time?
can someone please tell me what I'm missing in solving this algorithm? One problem I have is that my first if statement inside the nested loop is not evaluating, but I don't know why it wouldn't evaluate.
Here is the description of the problem:
You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of nums2. Find all the next greater numbers for nums1's elements in the corresponding places of nums2.
The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. If it does not exist, output -1 for this number.
Example 1:
Input: nums1 = [4,1,2], nums2 = [1,3,4,2].
Output: [-1,3,-1]
Explanation:
For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1.
For number 1 in the first array, the next greater number for it in the second array is 3.
For number 2 in the first array, there is no next greater number for it in the second array, so output -1.
link to original description
And here is my code so far:
var nums1 = [4,1,2];
var nums2 = [1,3,4,2];
var nextGreaterElement = function(findNums, nums) {
var holder = [];
for (var i = 0; i < findNums.length; i++) {
//loop through the 2nd array starting at the index of the first loop's current item.
for (var j = nums.indexOf(findNums[i]); i < nums.length - j; i++) {
if (nums[j+1] > nums[j]) {
holder.push(nums[j+1]);
break;
}
if (nums[nums.length]) {
holder.push(-1);
}
}
}
return holder;
};
nextGreaterElement(nums1, nums2)
Thanks for any help.
Problem: Updating variant i, but not variant j in inner loop (j-loop)
Missing: Debugging Effort
Problem Description
Theoretically, your code design should compare each value in nums1 to related parts of nums2. So, it would turn to a outer for-loop to loop on nums1 and an inner for-loop to loop related parts of nums2 for each iteration of the outer for-loop.
In your code, variant i is the index pointer for findNums (i.e. nums1) while variant j is the index pointer for nums (i.e. nums2). Variant i is always updating in both inner for-loop and outer for-loop while variant j is set once for every iteration of outer for-loop. This contradict to what you are suppose to do.
Debugging (Your Missing Work)
Find a piece of paper and a pen. Sit down, dry run the program and keep recording related info (variant i, variant j, findNums[i], nums[j], ...), you could figure out why your code is not working.
Possible Solution
var nextGreaterElement = function(findNums, nums) {
var holder = [];
for (var i = 0; i < findNums.length; i++) {
var hasNextGreaterElement = false;
// try to serach for next greater element
for (var j = nums.indexOf(findNums[i])+1; j < nums.length; j++) {
// handle case for next greater element is found
if (nums[j] > findNums[i]) {
holder.push(nums[j]);
hasNextGreaterElement = true;
break;
}
}
// handle case for next greater element is not found
if (!hasNextGreaterElement) {
holder.push(-1);
}
}
return holder;
};
var findNums=[4,1,2];
var nums=[1,3,4,2];
console.log(nextGreaterElement(findNums, nums));
You need to sort the array you are looking in to make it easier to find the number. If the array get big you might want a search algorithm to find the index in the array faster. With the array that is going to be looked in sorted you can grab the next number as the number that is one larger and check to see if you are at the end of the array. If you don't do this check the function will error when you can't find the number or when there is no number larger. Finally your second if statement didn't make sense. So I am checking to make sure that we are at the end of the array before outputting the -1 in the array.
var nextGreaterElement = function(findNums, nums) {
var holder = [];
//Should sort the array to make sure you get the next largest number
nums = nums.sort();
for (var i = 0; i < findNums.length; i++) {
//loop through the 2nd array starting at the index of the first loop's current item.
//for (var j = nums.indexOf(findNums[i]); i < nums.length - j; i++) {
for(var j = 0; j < nums.length; j++){
//check for value in array and make sure the value is not at the end
if (findNums[i] == nums[j] && j != nums.length - 1) {
holder.push(nums[j+1]);
break;
}
//check for the last element in array if so output -1
if (j == nums.length - 1) {
holder.push(-1);
}
}
}
return holder;
};
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Can someone explain how this solution works for an exercise that makes you find the most frequent element in an array, returning the number of times its been counted? I'm new to JS and just need a bit of help with understanding the logic!
function mostFrequentItemCount(array) {
var freq = 0;
var mostFreq = 0;
for (var i = 0; i <= array.length; i++) {
for (var j = i; j < array.length; j++) {
if (array[i] === array[j]) {
freq++;
}
if (freq >= mostFreq) {
mostFreq = freq;
}
if (array.length === 0) {
return 0;
}
}
freq = 0;
}
return mostFreq;
}
mostFrequentItemCount([4, 3, 4, 4, 5, 5, 5, 5, 4, 3])
Apart from the error mentioned on comments, the logic that it takes each element of the array in the first loop, and compares it to the other elements placed after (second loop starts at first loop current index), and counts the occurrences.
IMO there must be a more optimized way to do this, because elements are counted many times in situations where it is not relevant (no use to count the 4 again with omitting the first when we meet the second one)
It also doesn't cope with same frequence elements..
if you are really interested in a REAL JS solution:
var hashMap = {}; // in js any object can be also used as a map
var array = [4, 3, 4, 4, 5, 5, 5, 5, 4, 3];
for (var i = 0; i < array.length; i++) // there are better ways of doing this with js es6
{
if (!hashMap[array[i]]) hashMap[array[i]] = 0; // if this is the first time of this value in the map - initialize it with zero
hashMap[array[i]]++; // increase the count of each value
}
for (var value in hashMap)
{
console.log(value + ' ' + hashMap[value]); // print each value with the correct amount of instances
}
Nested loops should be avoided performance-wise, and I'm sure there is a better solution, but still I'd like to give you an understanding of how the provided snippet works:
function mostFrequentItemCount(array) {
// initialize variables
var freq = 0; // variable that will hold frequency count of the currently checked element
var mostFreq = 0; // variable that will hold the highest frequency count
// iterate over all elements of the array
for (var i = 0; i <= array.length; i++) {
// from the current index i, iterate over the array again,
// so all "following" elements will be checked
for (var j = i; j < array.length; j++) {
if (array[i] === array[j]) {
// if one of the following elements equals
// the current element of the first for loop,
// increase frequency count
freq++;
}
// if the frequency of this element is higher then the
// currently highest frequency, set the mostFreq variable
// to the frequency of the current element
if (freq >= mostFreq) {
mostFreq = freq;
}
// if the array has no elements, return 0
if (array.length === 0) {
return 0;
}
}
// reset freq to 0 so we can start fresh with the next element
freq = 0;
}
// return the most frequent:
return mostFreq;
}
Please also note that this only works for an array containing only numbers (and will return the frequency, not the most frequent number, as mentioned in the comments). Adaptions would have to be made to to return the actual element when strings or objects are to be compared.
And another solution:
function mostFrequentItemCount(array) {
return array.reduce(function(p,c){
if(p[c] === undefined)
p[c] = 0;
p[c]++;
if(p.mostFrequent == undefined || p[c]>p[p.mostFrequent])
p.mostFrequent = c;
return p;
},{}).mostFrequent;
}
This function does not find the most frequent element, only the amount of times the most frequent element showed up.
It works by counting the amount of times each element show up.
The outer for loop makes sure you check every element in the array. The inner loop counts how many times this element showed up. Every time the inner loop finds an element more frequent than the previous elements, it updates mostFreq.
Its worth noticing that this code can be optimized by using an auxiliar array, that counts how many times each element showed up. Also, as stated in comments, the loop conditions are incorrect, since array.length returns the first empty position in the array.
Here is the code in question:
var L1 = [];
var Q1 = [];
function populateListOne() {
var limit = prompt("please enter a number you would like to fill L1 to.");
for (i = 2; i <= limit; i++) {
L1[i] = i;
}
for (n = 2; n <= L1.length; n++) {
var count = 2;
if (n == count) {
var index = L1.indexOf(n);
L1.splice(index, 1);
Q1[n] = n;
count = count + 1;
}
for (j = 0; j <= L1.length; j++) {
if (L1[j] % 2 == 0) {
var secondIndex = L1.indexOf(j);
L1.splice(secondIndex, 1);
}
}
}
document.getElementById("demo").innerHTML = "iteration " + "1" + ": " + L1 + " Q1 = " + Q1;
}
I’m currently working on a homework assignment where I have to setup a queue. All is explained in my JSFiddle.
Problem description
Essentially, the part I’m stuck on is iterating through each instance of the array and then taking the value out if the modulus is identical to 0. However, as you can see when I run the program, it doesn’t work out that way. I know the problem is in the second for loop I just don’t see what I’m doing wrong.
The way I read it is, if j is less than the length of the array, increment. Then, if the value of the index of L1[j] modulus 2 is identical to 0, set the value of secondIndex to whatever the index of j is. Then splice it out. So, theoretically, only numbers divisible by two should be removed.
Input
A single number limit, which will be used to fill array L1.
L1 will be initialized with values 2, 3, ... limit.
Process
Get the starting element of array L1 and place it in array Q1.
Using that element, remove all values in array L1 that are divisible by that number.
Repeat until array L1 is empty.
You're going to have issues with looping over an array if you're changing the array within the loop. To help with this, I tend to iterate from back to front (also note: iterate from array.length - 1 as the length element does not exist, arrays are key'd from 0):
for(j = L1.length - 1; j >=0 ; j--)
For your first loop, you miss the elements L1[0] and L1[1], so I would change the first loop to:
L1 = [];
for(i = 2; i <= limit; i++)
{
L1.push(i);
}
In this section:
for(j = 0; j <= L1.length; j++){
if(L1[j] % 2 == 0)
{
var secondIndex = L1.indexOf(j);
L1.splice(secondIndex, 1);
}
}
you should splice with j instead of secondIndex.
Change L1.splice(secondIndex, 1); to L1.splice(j, 1);
Array indices and putting entries
You initial code used an array that was initialized to start at index 2. To avoid confusion, of what index to start at, start with index 0 and iterate until array.length instead of a predefined value limit to ensure that you go through each element.
The following still works but will be more of a headache because you need remember where to start and when you will end.
for (i = 2; i <= limit; i++) {
L1[i] = i; // 'i' will begin at two!
}
Here's a better way:
for (i = 2; i <= limit; i++) {
// 'i' starts at 2 and since L1 is an empty array,
// pushing elements into it will start index at 0!
L1.push(i);
}
Use pop and slice when getting values
When you need to take a peek at what value is at the start of your array, you can do so by using L1[0] if you followed my advice above regarding array keys.
However, when you are sure about needing to remove the starting element of the array, use Array.slice(idx, amt). idx specifies which index to start at, and amt specifies how many elements to remove beginning at that index (inclusive).
// Go to 1st element in L1. Remove (1 element at index 0) from L1.
var current = L1.splice(0, 1);
Use the appropriate loops
To make your life easier, use the appropriate loops when necessary. For loops are used when you know exactly how many times you will iterate. Use while loops when you are expecting an event.
In your case, 'repeat until L1 is empty' directly translates to:
do {
// divisibility checking
} while (L1.length > 0);
JSFiddle
Here's a complete JS fiddle with in-line comments that does exactly what you said.
// Contains a list of items in each set.
var sets[0] = [1,2,3,4,5,6,7,8,9],
sets[1] = [10,11,12,13,14,15,16,17,18],
sets[2] = [19,20,21,22,23,25,26,27]
// Contains the mins associated to each set item.
var setTimes[0] = [15,15,15,15,15,15,15,15,15],
setTimes[1] = [16,12,11,15,13,15,15,15,14],
setTimes[2] = [16,12,11,15,13,12,11,15,13]
I've got a set of arrays as given above. The sets array has a data set of values. This array can have n number of items in it. Ex, sets[n].
Each sets array has an equivalent setTimes array that has minutes stored in it. setTimes[0][0] is 15min and is the number of minutes for sets[0][0].
Given a set item(ex 12), I'd like to:
Find out which set array does the given number belong to? In our case, since 12 was the item, it belongs to sets[1].
Once I have this, I'd like to get the sum of all mins from the setTimes array for the current sets index and also the next index. In our case, that would be the sum of setTimes[1] and setTimes[2].
In the event we reach the end of sets array, I'd like to get the sum of the first set array.
For ex,
- if I pass 12, I'll need to get the sum of setTimes[1] and setTimes[2]
- If I pass 23, I'll need to get the sum of setTimes[2] and setTimes[0]
Here is the loop I've been thinking, would like to know if there is a better way of doing this.
function computeTotalMin(givenItem)
{
// represents how many sets to loop thorough. I need 2.
for (x = 0; x <= 1; x++)
{
for(i = 0; i < sets.length; i++)
{
// checking to see if i value is on the last index of the sets array.
if(i === sets.length - 1)
{
i = 0;
var item = sets[i].indexOf(givenItem);
if(item !== -1)
{
// Loops through all mins from setTimes[i] array
for(j = 0; j < setTimes[i].length; j++)
{
var total = total + setTimes[j];
}
}
}
}
}
}
You don't need two nested loops for continuing at the end. You should have a single loop that iterates the number of sets you're interested in (2), and has an index (starting at the one set you've found). Inside that loop, you'd make a modulo operation on the index; to get back to the start when you've reached the end. By only looping over the count, not the (resettable) index, you won't get into an infinite loop.
You also should divide your program in just those tasks that you've textually described (find this, then do that), instead of munching everything in one huge nested control structure.
function computeTotalMin(givenItem) {
var setIndex = -1;
for (; setIndex < sets.length; setIndex++)
if (sets[setIndex].indexOf(givenItem) > -1)
break;
if (setIndex == sets.length)
return null; // givenItem found in none of the sets
var sum = 0;
for (var count = 0; count < 2; count++) {
for (var i=0; i<setTimes[setIndex].length; i++)
sum += setTimes[setIndex][i];
setIndex++; // go to next set
setIndex %= sets.length; // which might be 0
// alternatively: if (setIndex == sets.length) setIndex = 0;
}
return sum;
}