Some Front End Algorithms are detailed - (Updates from time to time)

Keywords: Javascript shell less C

Preface

I don't share the algorithm by difficulty because I'm more casual, so you can see that sometimes the order changes because when I publish, I modify the position by difficulty so that you can start with simplicity as much as possible.
Add an update time for each update in the future to let you know my progress.Add a timer function to visually compare efficiency
And because of the miscellaneous data, many of them are my own understandings. If you find problems and welcome criticism for the benefit of more people, it is better to mistake others for yourself.
The idea is the same. Writing is not unique, so you don't have to memorize code.
After all this time, I have finished all ten algorithms, but I am not familiar with them, I have not thoroughly understood some ideas, I think some writing methods should be better, and I will certainly find time to optimize them in the future, but it may not be recent. I am a bit anxious to want to summarize other aspects of knowledge, such as the east of the request side.West, there are many short boards to learn

PS:
New typesetting, code optimization, 2018/12/18

Basic Test Functions

PS: In order to improve the rigorousness, I add type judgment and filter to the test methods, and add parameters to the generating function to determine whether the values can be repeated or not.
First, introduce some ways to generate test examples so that you don't need to add them manually.

To improve the test visualization, we use a fixed random array.

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === '[object Array]';
  !bol && alert('Current Input Non-Array Type!');
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}


/**
 * @param {*} num Generation Length
 * @param {*} isRepetition Whether or not to repeat values is not possible by default
 * @param {*} min Minimum range default 0
 * @param {*} max Maximum range default 1000
 * @returns
 */
function randomAry(num, isRepetition, min, max) {
  var ary = [],
    i = 0,
    min = min || 0,
    max = max || 1000;

  if (!isRepetition && num > max - min) {
    throw ('The current range of values exceeds the range between the minimum and maximum values and cannot be repeated!');
  }

  for (; i < num; i++) {
    const random = Math.ceil(Math.random() * (max - min) + min);

    if (!isRepetition && ary.indexOf(random) > -1) {
      --i;
      continue;
    }
    ary.push(random);
  }

  console.log('Before Sorting: ', ary)
  return ary;
}

Summary, algorithm efficiency

(Friendly Links, What's called log Many people, like me, should have forgotten what to call a log! (~)

Sorting method Average Best case Worst case Spatial Complexity sort order stability
Select Sort O(n²) O(n²) O(n²) O(1) In-place Instable
Insert Sort O(n²) O(n) O(n²) O(1) In-place Stable
Bubble sort O(n²) O(n) O(n²) O(1) In-place Stable
Quick Sort O(n log n) O(n log n) O(n²) O(log n) In-place Instable
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Out-place Stable
Shell Sort O(n log n) O(n log² n) O(n log² n) O(1) In-place Instable
Count Sort O(n + k) O(n + k) O(n + k) O(k) Out-place Stable
Bucket sorting O(n + k) O(n + k) O(n²) O(n + k) Out-place Stable
Cardinality sorting O(n * k) O(n * k) O(n * k) O(n + k) Out-place Stable
Heap Sorting O(n log n) O(n log n) O(n log n) O(1) In-place Instable

Noun Interpretation

term describe
n Data Size
k Number of buckets
In-place Constant memory, no extra memory
Out-place Take up extra memory
stability Will their relative positions change before and after sorting if the same elements exist

Select Sorting Algorithm

Ideas:

  1. The first loop, starting from zero, assumes it is the minimum (i)
  2. The second cycle starts at the minimum + 1 (j) position, and all subsequent data are compared to the minimum one by one, replacing it when a smaller value is found
  3. If the minimum value has changed after the second layer loops have been traversed, it will be swapped with the original minimum value (i. e. the first layer loops i)
  4. Continue repeating steps until complete

A double-layer for loop is required to implement the above rules

Advantage:

1. The simplest and toughest, without taking up extra memory space

Disadvantages:

(1) Most nauseous, computationally expensive, longer arrays traverse more times, and extremely inefficient. No matter how disordered the data is, it must traverse all the data

//Select Sort
function selectIn(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var len = ary.length,
    i = 0,
    j,
    min; //minimum value

  //Interchange location
  var swap = function(a, b) {
    ary[a] = [ary[b], (ary[b] = ary[a])][0];
  };

  //split
  var select = (function() {
    for (; i < len; i++) {
      //Assume current minimum
      min = i;

      for (j = i + 1; j < len; j++) {
        //Find Minimum
        if (ary[min] > ary[j]) min = j;
      }

      //Conditional interchange
      if (i != min) swap(i, min);
    }

    return ary;
  })();

  //Return a new array
  return select;
}

useTime("Select Sorting Algorithm", selectIn);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run result:

Before sorting: [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Time-consuming selection of sorting algorithm: 12.634ms

Insert sort algorithm

Ideas:

The insertion algorithm divides the array to be sorted into two parts: the first part contains all the elements of the array except the last one, which gives the array one more space to insert, and the second part contains only one element, which is the element to insert.After sorting the first part, insert the last element into the sorted first part.

  1. The first loop, starting from 1, saves the current value as a reference number
  2. The second loop, starting at the j=i-1 position, moves the numeric position backwards when j>=0 is satisfied and the j position value is greater than the reference value
  3. Break cycle without satisfying condition, reference value assigns current j+1 position

for and while loops are required to implement these rules

Advantage:

(1) For sorting a small amount of data, it is best to say that the sequence is already in ascending order, in which case the comparison needs to be done (n-1) times.

Disadvantages:

(1) The computational process consumes a lot of performance, and the more iterations you traverse in the future, it is related to the degree of data disorder. In the worst case, the sequence is in descending order. There are n(n-1)/2 comparisons needed at this time.The assignment to insert a sort is the number of comparison operations plus (n-1) times

//Insert Sort
function insertIn(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  //Double traversal, i to start from 1
  var len = ary.length,
    i = 1,
    j,
    tmp, //Temporary variable
    copy = ary.slice(0); // Set array copy

  for (; i < len; i++) {
    tmp = copy[i];
    j = i - 1;

    //Find the appropriate location and insert
    while (j >= 0 && copy[j] > tmp) {
      //Where i is fixed and j is decreasing, so use j+1
      copy[j + 1] = copy[j];
      j--;
    }

    //Assignment break position, where the order does not change is equivalent to reassigning itself, so it is a stabilization algorithm
    copy[j + 1] = tmp;
  }
  return copy;
}

useTime("Insert Sort", insertIn);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run result:

Before sorting: [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Insert Sort Time: 10.002ms

Bubble sort algorithm

Ideas:

Traversing through two adjacent data, the smaller one comes first, and if the previous data is larger than the latter one swaps positions, the algorithm's name comes from the larger elements that slowly "float" to the top of the column through the swap.

  1. Outer loop, decreasing from array length
  2. Internal loop, incremental starting at i=0 and not exceeding the length of the array, comparing I and i+1 position number ordering
  3. Repeat one or two steps

Advantage:

The best case is that the sequence is already in ascending order, in which case the comparison needs to be done (n-1) times.

Disadvantages:

(1) Comparisons are more frequent and less efficient, and only two adjacent data can be moved at a time.In the worst case, the sequence is in descending order, so the comparison needed at this point is n(n-1)/2 times.

//Bubble sort
function bubbleSort(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var len = ary.length,
    i;

  //Interchange location
  var swap = function(a, b) {
    ary[a] = [ary[b], (ary[b] = ary[a])][0];
  };

  while (len > 0) {
    for (i = 0; i < len - 1; i++) {
      if (ary[i] > ary[i + 1]) swap(i, i + 1);
    }
    len--;
  }
  return ary;
}

useTime("Bubble sort algorithm", bubbleSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run result:

Before sorting: [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Bubble sort algorithm time-consuming: 12.200ms

Quick sorting algorithm

Ideas:

The data to be sorted is divided into two separate parts by one-time sorting, in which all the data in one part is smaller than all the data in the other part. Then the two parts are sorted quickly by this method, and the whole sorting process can be done recursively, so that the whole data becomes an ordered sequence.

  1. Declare two new arrays and select an intermediate element in the original as the reference number
  2. The other elements that are smaller than the reference number and larger than the reference number are put into two new arrays and reassembled into an array
  3. Then repeat steps one or two until you're done

A for loop and recursion are required to implement the above rules.

Advantage:

(1) is an improved version of bubble sorting, which is used most widely and faster.

Disadvantages:

When the initial sequence is ordered or basically ordered, the time complexity decreases to O(n*n), and the deeper the data is, the larger the stack space is occupied.

//Quick Sort
function quickSort(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var middleIndex = Math.ceil(ary.length / 2), //Find the middle
    middle = ary.splice(middleIndex, 1), //Extracting intermediate values
    left = [],
    right = [],
    i = 0,
    len = ary.length; //There is a deletion step above, so you can't put it in front

  for (; i < len; i++) {
    (ary[i] < middle ? left : right).push(ary[i]);
  }

  //Recursive execution
  return quickSort(left).concat(middle, quickSort(right));
}

useTime("Quick sorting algorithm", quickSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run result:

Before sorting: [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Fast sorting algorithm time-consuming: 13.195ms

One thing to note is that the filtered array part must be placed in the declared variable before extracting the reference number, and later if the length of the array is 2, deleting the reference number will be returned directly to the remaining array because it meets the criteria, so you can see the instance. (If you can't see the effect at one time, add or subtract spaces and view againThe effect changes)

//Quick Sort
function quickSort(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var middleIndex = Math.ceil(ary.length / 2),
    middle = ary.splice(middleIndex, 1),
    left = [],
    right = [],
    i = 0,
    len = ary.length;

  //Filter Partial Array
  if (len <= 1) return ary;

  for (; i < len; i++) {
    (ary[i] < middle ? left : right).push(ary[i]);
  }

  return quickSort(left).concat(middle, quickSort(right));
}

useTime("Quick sorting algorithm", quickSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Merge sort algorithm

Ideas:

  1. The first step splits, splitting data from the middle until it cannot continue (top to bottom)
    [12,56,23,2] => [12,56] + [23,2] => [12] + [56] + [23] + [2]
  2. The second step compares and merges, sorting and merging each two sets of data until they merge to only one set of data (bottom to top)
    [12] + [56] + [23] + [2] => [12,56] + [2,23] => [2,12,23,56]

To implement these rules, while loops and nested recursion are required.

Advantage:

(1) Speed is next only to quick sorting, for example, if the number of sorting items is 1000, merge sorting takes 3 seconds, and insert sorting takes about 16.7 minutes (the gap increases as the sorting items increase).

Disadvantages:

1 Compare memory usage, slightly more complex

//Merge Sort
function excreteAndMerge(ary) {
  //Type Check
  if (!isArray(ary)) return false;

  //split
  var excrete = function(_ary) {
    //Without interruption, it will run until the stack overflows, key!!
    if (_ary.length <= 1) return _ary;

    //Split two arrays
    var middleIndex = Math.ceil(_ary.length / 2),
      left = _ary.slice(0, middleIndex),
      right = _ary.slice(middleIndex);

    //Merge Array
    return merge(excrete(left), excrete(right));
  };

  //Sort and merge
  var merge = function(left, right) {
    var _ary = [];

    //This is a key step!!!
    while (left.length > 0 && right.length > 0) {
      //Compare the array headers separately and push smaller values into the new array
      _ary.push((left[0] < right[0] ? left : right).shift());
    }
    //Combine the remaining array items after the comparison
    return _ary.concat(left, right);
  };

  return excrete(ary);
}

useTime("Merge sort algorithm", excreteAndMerge);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run result:

Before sorting: [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Merge sort algorithm time-consuming: 13.403ms

Hill sorting algorithm

Basis: Hill ordering is an improvement based on the following two properties of insertion ordering:

  1. Insert Sorting is efficient when it operates on data that is almost sorted, thus achieving linear sorting efficiency.
  2. However, insert sorting is generally inefficient because insert sorting can only move data one bit at a time.
    Shell Sort is one of the insertion sorts.Also known as reduced incremental sorting, it is a more efficient and improved version of the direct insertion sorting algorithm.Hill sorting is an unstable sorting algorithm.This method was named after DL.Shell, which was introduced in 1959.

Ideas:

  1. The first step is to split, fix or dynamically define an interval sequence. The most important thing is that no matter how large the sequence is, it will eventually have to go through the end one by one. (I kneeled for a while at this step before I discovered it.)

    In general, half of the initial sequence is incremental, and then halves each time until the increment is 1.

    For example, length 100, span 2, initial interval sequence = length/span, decrement = self/span is 50,25,22.....1

  2. The second step is to sort by interval until one by one

Multiple looping and insertion algorithms are required to implement the above rules.

Advantage:

Essentially, the Hill sorting algorithm is an improvement of the direct insertion sorting algorithm, which reduces the number of copies and is much faster because
(1) When the initial state of the file is basically ordered, direct insertion of the sort requires fewer comparisons and moves.
(2) When the value of n is small, the difference between N and n_is also small, that is, the best time complexity O(n) and the worst time complexity O(n_) of direct insertion sort are not different.
(3) At the beginning of Hill sorting, the increment is large, the grouping is large, and the number of records in each group is small. Therefore, the insertion is fast within each group, and then the increment decreases and the number of groupings decreases gradually, while the number of records in each group increases gradually. However, because the files have been sorted at incremental distances, making them closer to the ordered state, the new file isA single sorting process is also faster.

Disadvantages:

Sorting very large data is not optimal
An example is shown below, assuming the original array [663, 949, 916, 393, 470, 26, 649, 256, 901, 705, 519, 803, 334, 861, 83, 70, 230, 588, 9, 346] has a span of 3.
(1) First cycle increment: 20/3_7, i.e. difference of comparison position 7

Second cycle increment: 7/3_3, i.e. 3 differences in comparison positions

Third cycle increment: 3/3 = 1, ending one by one

You can take a closer look at mind maps because thinking alone can be confusing.
First let's see how Baidu Encyclopedia is written

var arr = [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312],
  len = arr.length;
for (
  var fraction = Math.floor(len / 2); fraction > 0; fraction = Math.floor(fraction / 2)
) {
  console.log("fraction : " + fraction);
  for (var i = fraction; i < len; i++) {
    for (
      var j = i - fraction; j >= 0 && arr[j] > arr[fraction + j]; j -= fraction
    ) {
      var temp = arr[j];
      arr[j] = arr[fraction + j];
      arr[fraction + j] = temp;
    }
  }
}
console.log(arr);

Run Results

fraction : 7
fraction : 3
fraction : 1
[ 246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970 ]

At first glance, it looks like it's understandable and simple, but there's a small problem. Actually, this is a fixed increment code. If you swap the sequence of intervals for something else, that is, Math.floor(len / 4), you might end up sorting. Here's how:

var arr = [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312],
  len = arr.length;
for (
  var fraction = Math.floor(len / 4); fraction > 0; fraction = Math.floor(fraction / 4)
) {
  console.log("fraction : " + fraction);
  for (var i = fraction; i < len; i++) {
    for (
      var j = i - fraction; j >= 0 && arr[j] > arr[fraction + j]; j -= fraction
    ) {
      var temp = arr[j];
      arr[j] = arr[fraction + j];
      arr[fraction + j] = temp;
    }
  }
}
console.log(arr);

Run Results

fraction : 3
[ 246, 380, 312, 317, 446, 722, 403, 561, 751, 614, 663, 917, 616, 907, 970 ]

Fortunately, I'm curious to practice it, or I'll plant it here to understand. The key is that no matter how big the sequence is, the end has to be traversed one by one.

I continue to dig down the code on the web, and the other basic code is written here

var arr = [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312],
  len = arr.length,
  temp,
  fraction = 1;

while (fraction < len / 5) {
  //Dynamic Definition of Interval Sequences
  fraction = fraction * 5 + 1;
}
for (fraction; fraction > 0; fraction = Math.floor(fraction / 5)) {
  console.log("fraction: " + fraction);
  for (var i = fraction; i < len; i++) {
    temp = arr[i];
    for (var j = i - fraction; j >= 0 && arr[j] > temp; j -= fraction) {
      arr[j + fraction] = arr[j];
    }
    arr[j + fraction] = temp;
  }
}

console.log(arr);

Run Results

fraction: 6
fraction: 1
[ 246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970 ]

The code that dynamically defines the sequence of intervals solves the above problem
But I'm not satisfied with it. It's not scalable enough. Here's a Hill algorithm I've written as I understand it. It only uses three loops plus a break condition. I didn't find any problems on my own test. If there's something wrong, point it out.

//Shell Sort
function shellSort(ary, limit) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var len = ary.length,
    limit = limit || 3, //span
    gap = Math.ceil(len / limit), //Dynamic Interval Sequence
    i,
    j;

  //Interchange location
  var swap = function (a, b) {
    ary[a] = [ary[b], (ary[b] = ary[a])][0];
  };

  //Interval Sequence Cycle Decreasing
  for (; gap > 0; gap = Math.ceil(gap / limit)) {
    for (i = gap; i < len; i++) {
      //Interval Sorting
      for (j = i - gap; j >= 0 && ary[j] > ary[j + gap]; j -= gap) {
        swap(j, j + gap);
      }
    }
    //Another key point is that a dead loop without interruption causes memory overflow
    if (gap == 1) break;
  }

  return ary;
}

useTime("Hill sorting algorithm", shellSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run Results

Before sorting: [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Hill sorting algorithm time-consuming: 8.988ms

Count sorting algorithm

Base: Count sorting is a non-comparison-based sorting algorithm whose core is to convert the input data values into keys stored in an extra open array space.As a sort of linear time complexity, count sort requires that the input data must be a range of integers.Count sorting has additional restrictions on the data entered:

  1. The elements of the input linear table belong to the finite partial set S;
  2. Set the length of the input linear table to n, |S|=k (which means the total number of elements in set S is k), then k=O(n).
    Under these two conditions, the complexity of count ordering is O(n).

Ideas:

  1. Statistical array counts the number of occurrences of each value and finds the largest and smallest of them
  2. The statistics array begins at the min position and adds up all counts (current and previous) until the maximum position
  3. Depending on the size of the values in the statistical array, you can know the sort order, and then reverse populate the target array

Advantage:

(1) When sorting a range of integers, its complexity is_(n+k) (where K is the range of integers), faster than any comparative sorting algorithm.

Disadvantages:

(1) sacrificing space for time makes O(k)>O(nlog(n)) less efficient than comparison-based sorting (the theoretical lower limit of time complexity for comparison-based sorting is O(nlog(n)), such as merge sorting, heap sorting)
A detailed analysis follows, assuming the original array [27, 24, 23, 27, 21]
Find minimum 21, maximum 27
Count [21:1, 23:1, 24:1, 27:2]
Count [21:1, 22:1, 23:2, 24:3, 25:3, 26:3, 27:5] after every position is filled from minimum to maximum
Backfill target array: [21, 23, 24, 27, 27]

(2) Sorting where the range of values differs too widely, such as [1, 2, 1000], is not applicable because the statistical arrays add up from 0 to 1000, which means there is too much redundancy in the middle to fill in the data.
(Only values will be printed here, not corresponding locations, but this is the key to the entire algorithm, so it is recommended that you copy to run locally.)

Because of the particularity of the algorithm, we test it with a random array with a smaller difference range.

//Count Sort
function countingSort(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var len = ary.length,
    Result = [], //Sort Array
    Count = [], //Statistics Array
    min = (max = ary[0]),
    i = 0,
    j,
    k;

  //Statistics in Count and find the maximum and minimum
  for (; i < len; i++) {
    //Remember that Count[ary[i]] cannot be saved with a variable!!, details http://www.qdfuns.com/notes/40831/dd2b82537d74065b8a53b75e2eb85715.html
    Count[ary[i]]++ || (Count[ary[i]] = 1); //There may be duplicate values
    min > ary[i] && (min = ary[i]);
    max < ary[i] && (max = ary[i]);
  }
  console.log("After the first cycle Count: ", Count);

  //Fill every position from the minimum to the maximum (this is critical)
  for (j = min; j < max; j++) {
    //Make the current item value larger than the previous one, which will be used to determine the order
    Count[j + 1] = (Count[j + 1] || 0) + (Count[j] || 0);
  }
  console.log("After the second cycle Count: ", Count);

  //Algorithmic essence, reverse fill array!!
  for (k = len - 1; k >= 0; k--) {
    console.log('position: ', Count[ary[k]] - 1, 'value: ', ary[k])
    //Result [position] = art value, -1 because the new array should be sorted from position 0
    Result[Count[ary[k]] - 1] = ary[k];
    //Decrease the count saved in the Count array, skip subsequent repetitive sorting if there are duplicate values in the array if commented out
    Count[ary[k]]--;
  }
  return Result;
}

useTime("Count sorting algorithm", countingSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [18, 19, 5, 15, 20, 2, 17, 13, 8, 6, 7, 16, 3, 10, 4];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run Results

Before sorting: (15) [18, 19, 5, 15, 20, 2, 17, 13, 8, 6, 7, 16, 3, 10, 4]

Count after the first cycle: (21) [undefined, undefined, 1, 1, 1, 1, 1, 1, undefined, 1, undefined, undefined, 1, undefined, 1, undefined, 1, 1, 1]

Count after the second cycle: (21) [undefined, undefined, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 12, 13, 14, 15]

Location: 2 value: 4
 Location: 7 value: 10
 Location: 1 value: 3
 Location: 10 value: 16
 Location: 5 value: 7
 Location: 4 value: 6
 Location: 6 value: 8
 Location: 8 value: 13
 Location: 11 Value: 17
 Location: 0 value: 2
 Location: 14 Values: 20
 Location: 9 value: 15
 Location: 3 value: 5
 Location: 13 value: 19
 Location: 12 value: 18

After sorting: (15) [2, 3, 4, 5, 6, 7, 8, 10, 13, 15, 16, 17, 18, 19, 20]
Count sort algorithm time-consuming: 14.28499999997ms

Bucket sorting algorithm

Base: Bucket sorting is an upgraded version of count sorting.It utilizes the mapping relationship of a function, and the key to its efficiency is the determination of the mapping function.Divide the array into a limited number of buckets.Each bucket is sorted individually (possibly using a different sorting algorithm or continuing to use bucket sorting recursively).

  1. Rationally plan the number of buckets with enough extra space
  2. The mapping function used can reasonably allocate the input N data into the corresponding bucket

Ideas:

  1. Find the Minimum and Maximum
  2. Find the range of values for each bucket
  3. Load values in buckets and sort them
  4. Start merging arrays

Advantage:

1 Time complexity is O(M+N), which is said to be the fastest and easiest sort

Disadvantages:

(1) If the input data is very large and the number of buckets is very large, the space cost is expensive, or if the data lengths differ too much, such as [1,255,366,120,156], the efficiency becomes lower.
An example diagram is shown below, assuming that the initial array [17, 42, 45, 44, 30, 21, 22, 1, 38, 33, 36, 39, 47, 5, 50, 31, 34, 27, 29, 11] has bucketCounts (5).
Step 1: Find a minimum of 1 and a maximum of 50
Step 2: The range of values for each bucket is Math.floor((max - min) / bucketCount) + 1 = 10
Step 3: Load the values in the appropriate buckets and sort them

Step 4: Merge arrays ['1','5','11','17','21','22','27','29','30','31','33','34','36','38','39','42','44','47','50']

//Bucket sorting
function bucketSort(ary, bucketCount) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  //Initialization variable parameters
  bucketCount = bucketCount || 5;
  var len = ary.length,
    buckets = [], //Bucket Array
    max = min = ary[0], //Remove first by default
    space, //Numeric range
    i;

  //Find the Minimum and Maximum
  for (i = 1; i < len; i++) {
    min > ary[i] && (min = ary[i]);
    max < ary[i] && (max = ary[i]);
  }

  //Find the range of values for each bucket (down and add one) and range (min -> >=max)
  space = Math.floor((max - min) / bucketCount) + 1;

  //Put values in buckets
  for (i = 0; i < len; i++) {
    var item = ary[i], //value
      index = Math.floor((item - min) / space), //Difficulty one, find the corresponding bucket sequence
      bucket = buckets[index]; //Bucket Sequence Array

    //Determine if there is a corresponding bucket
    if (bucket) {
      //Because arrays are arranged from small to large
      var bucketLen = bucket.length - 1;

      //Difficulty two, reverse comparison size, conform to the condition value backward by one, k minus one
      while (bucketLen >= 0 && bucket[bucketLen] > item) {
        bucket[bucketLen + 1] = bucket[bucketLen];
        bucketLen--;
      }

      //Insert Array at Corresponding Position
      bucket[bucketLen + 1] = item;
    } else {
      //Add a new value to the bucket (variable bucket cannot be referenced here, details http://www.qdfuns.com/notes/40831/dd2b82537d74065b8a53b75e2eb85715.html)
      buckets[index] = [item];
    }
  }

  //Start merging arrays
  // Method one, returns a number format array, but the step performance is poor
  /* var n = 0, result = [];
  while(n < bucketCount) {
      //There may be an array of faults in the middle that match the range
      if(buckets[n]) result = result.concat(buckets[n]);
      n++;
  }
  return result */

  //Method 2, returns an array of string formats, which is simple and convenient (average time is several milliseconds faster)
  return buckets.join(',').split(',');
}

useTime("Bucket sorting algorithm", bucketSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run Results

Before sorting: (15) [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: (15) [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Bucket sorting algorithm time-consuming: 2.379999999955ms

Cardinality sorting algorithm

Base: Unify all values to be compared (positive integers) into the same number length, with the shorter digits preceded by zeros.Then, start with the lowest bit and sort one by one.In this way, the sequence becomes an ordered sequence from the lowest to the highest.

Ideas:

  1. Find out the unit length of the maximum value (other people's writings I see online don't do this step, but they know the unit length and pass it in. I've changed to automatic judgment instead)
  2. Based on the sorting method LSD/MSD, allocation starts from the low or high digits to the base level.
  3. Merge arrays until all digits have been allocated

LSD(Least significant digital): Begins on the rightmost side of a key value, applies to a sequence of digits that are small, and is allocated from the lowest digit to the base, but is not immediately merged back into an array after allocation. Instead, a "bucket" is created in each bucket to assign the values in each bucket to the values in the previous digit.In the Bucket.Merge back into a single array after assigning the highest number of digits.
MSD(Most significant digital): Beginning from the leftmost side of the key value, applies to columns with large digits, and starting from the high digits as the base, but not immediately after the allocation merges back into an array. Instead, a "bucket" is created in each bucket to assign the values in each bucket to the values in the next digitIn the Bucket.After the allocation of the lowest number of digits, it is merged back into a single array.

Advantage:

(1) stable ordering; time complexity can exceed O(n)

Disadvantages:

(1) It is only suitable for cases with cardinality, such as non-numeric string sorting, which is too narrow to use and takes up too much memory.
An example diagram is shown below, assuming the initial array [537, 674, 474, 21, 60, 692, 728, 911, 322, 949]

Sort by number of digits

Get the array [60, 21, 911, 692, 322, 674, 474, 537, 728, 949]

Sort by ten digits

Get the array [911, 21, 322, 728, 537, 949, 60, 674, 474, 692]

Sort by 100 digits

Get the array [21, 60, 322, 474, 537, 674, 692, 728, 911, 949]

//Cardinality sorting
function radixSort(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  var maxLen = (Math.max.apply(null, ary) + '').length, //Find the maximum unit length
    len = ary.length, // Array length
    counter = [], //Cardinal array
    mod = 10, //Remainder Unit
    dev = 1, //Divisor unit
    i = 0,
    j,
    index, //Sequence value
    val;

  //Traversal Numeric Units
  for (; i < maxLen; i++, dev *= 10, mod *= 10) {
    //Traversal Array Length
    for (j = 0; j < len; j++) {
      //Difficulty one, get the number of the specified unit of value, start with the number of digits, and make up for the default value by zero, such as (561 => 1, 6, 5, 0)
      index = parseInt((ary[j] % mod) / dev);
      //Put the value in the corresponding specified unit sequence bucket
      !counter[index] && (counter[index] = []);
      // //Put the value in the corresponding specified unit sequence bucket
      counter[index].push(ary[j]);
    }

    //Statistics the length of the array, whichever is the maximum, even if there is an empty array in the middle.
    var counterLen = counter.length, // Statistics Array Length
      pos = 0; //Location

    //Traverse bucket arrays, modify the order of the original array
    for (j = 0; j < counterLen; j++) {
      //Filter empty arrays
      if (counter[j]) {
        while (val = counter[j].shift()) {
          //pos instead of j because there may be a fault in the middle of counter
          //Modifying the original array instead of using the new one is because the loop executes multiple times, not once to complete the sort
          ary[pos++] = val;
        }
      }
    }
  }
  return ary;
}

useTime("Cardinality sorting algorithm", radixSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run Results

Before sorting: (15) [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: (15) [907, 380, 722, 403, 614, 616, 317, 907, 970, 614, 446, 917, 403, 663, 312]
Base sorting algorithm time-consuming: 12.1699999999987ms

10. Heap sorting algorithm

Basics:

Because js simulates binary tree more troublesome, the advantages of heap sorting can not be reflected in js language. In contrast, C language's chain table can better represent heap sorting in implementation. Heap sorting may be more suitable for pointer class's computer language.( Binary Tree Algorithm)

  1. A binary tree has at most two subtrees at each node
  2. A binary tree has at most 2^(i_1) nodes in its layer I
  3. A binary tree with a depth of K has at most 2^k_1 nodes

The time required to sort a heap consists mainly of the time overhead of building the initial heap and rebuilding the heap repeatedly

Ideas:

The heap sorting process is to take out the maximum number of heap tops, continue to adjust the remaining heaps to the maximum heap, and then take out the maximum number of heap tops again until the remaining number is only one hour.The following operations are defined in the heap:

  1. Max-Heapify: Adjusts the end child node of the heap so that the child node is always smaller than the parent node
  2. Create maximum heap (Build-Max-Heap): Reorder all the data in the heap to make it the largest heap
  3. Heap-Sort: Remove the root node that is positioned at the first data and do a recursive operation for maximum heap adjustment

Advantage:

1. Superior performance for larger sequences

Disadvantages:

1 Heap sorting is not appropriate for files with fewer records because more comparisons are required to build the initial heap.Heap sorting is stable time, the sort time of heap sorting is independent of data means that data of the same size may already be sorted, heap sorting still needs to take the same time to sort, and the memory consumption is twice that of fast sorting
An example diagram is shown below, assuming the initial array [31, 16, 73, 42, 51, 28, 71, 80, 61, 39, 60, 10, 85, 51, 21], the first traversal sort of a binary tree starts at the last parent node

After the first traversal is complete, swap the first and last positions, then the last number does not participate in the next sort, and then traverse the sort from the heap until all sorts are finished

You can take a closer look at mind maps because thinking alone can be confusing.

//Heap algorithm
function maxHeapSort(ary) {
  //Type Check
  if (!isArray(ary)) return false;
  if (ary.length <= 1) return ary;

  //Interchange location
  var swap = function (a, b) {
    ary[a] = [ary[b], (ary[b] = ary[a])][0];
  };

  //Find Parent Node
  function buildMaxHeap(len) {
    var i = Math.floor(len / 2) - 1; //Difficulty One, Last Parent End

    //Traverse through the parent nodes, sorting all the data in the heap layer by layer to find the largest node
    for (; i >= 0; i--) {
      maxHeapify(i, len);
    }
  }

  //Adjust the end child node of the heap so that the child node is always smaller than the parent node
  function maxHeapify(_index, _len) {
    var left, right, max = _index;

    //It can be written recursively and is more readable, but I think traversal will be more efficient.
    while (true) {
      //They are left subtree node, right subtree node, and parent node.
      left = 2 * _index + 1;
      right = 2 * _index + 2;
      max = _index;

      //By comparison, the parent node must be larger than the subtree node, or the location will be changed
      if (left < _len && ary[left] > ary[max]) max = left;
      if (right < _len && ary[right] > ary[max]) max = right;
      if (max != _index) {
        //Incorrect order, sorting
        swap(_index, max);
        //Replace Node Location to Continue Execution
        _index = max;
      } else {
        break;
      }
    }
  }

  //Remove the root node bit in the first data and do a recursive operation for maximum heap adjustment
  function _sort() {

    //After the first sorting, the first largest value is the first node.
    var len = ary.length,
      i = len - 1; //Tail position

    //Initial run of code, first round of traversal sort, find out the maximum number of nodes to promote to the heap head
    buildMaxHeap(len);

    for (; i > 0; i--) {
      //The end-to-end data is exchanged, and the original end number is mentioned first for sorting (the end of the sorting, not the end of the complete array)
      swap(0, i);
      //Ignore tail normal order data and continue sorting
      maxHeapify(0, i);
    }

    return ary;
  }

  return _sort()
}

useTime("Heap algorithm algorithm", maxHeapSort);

//Is Array
function isArray(obj) {
  var bol = Object.prototype.toString.call(obj) === "[object Array]";
  !bol && alert("Current Input Non-Array Type!");
  return bol;
}

//Timing gadget
function useTime(name, fn) {
  var ary = [
    616,
    380,
    751,
    317,
    561,
    722,
    246,
    907,
    970,
    614,
    446,
    917,
    403,
    663,
    312
  ];
  console.time(name + "time consuming");
  console.log("Before Sorting: ", ary);
  console.log("After sorting: ", fn(ary));
  console.timeEnd(name + "time consuming");
}

Run Results

Before sorting: (15) [616, 380, 751, 317, 561, 722, 246, 907, 970, 614, 446, 917, 403, 663, 312]
After sorting: (15) [246, 312, 317, 380, 403, 446, 561, 614, 616, 663, 722, 751, 907, 917, 970]
Heap algorithm algorithm time-consuming: 3.9149999999992ms

Posted by The Phoenix on Mon, 05 Aug 2019 19:05:51 -0700