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:
- The first loop, starting from zero, assumes it is the minimum (i)
- 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
- 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)
- 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.
- The first loop, starting from 1, saves the current value as a reference number
- 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
- 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.
- Outer loop, decreasing from array length
- Internal loop, incremental starting at i=0 and not exceeding the length of the array, comparing I and i+1 position number ordering
- 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.
- Declare two new arrays and select an intermediate element in the original as the reference number
- 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
- 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:
- 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] - 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:
- Insert Sorting is efficient when it operates on data that is almost sorted, thus achieving linear sorting efficiency.
- 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:
- 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
- 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:
- The elements of the input linear table belong to the finite partial set S;
- 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:
- Statistical array counts the number of occurrences of each value and finds the largest and smallest of them
- The statistics array begins at the min position and adds up all counts (current and previous) until the maximum position
- 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).
- Rationally plan the number of buckets with enough extra space
- The mapping function used can reasonably allocate the input N data into the corresponding bucket
Ideas:
- Find the Minimum and Maximum
- Find the range of values for each bucket
- Load values in buckets and sort them
- 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:
- 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)
- Based on the sorting method LSD/MSD, allocation starts from the low or high digits to the base level.
- 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)
- A binary tree has at most two subtrees at each node
- A binary tree has at most 2^(i_1) nodes in its layer I
- 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:
- Max-Heapify: Adjusts the end child node of the heap so that the child node is always smaller than the parent node
- Create maximum heap (Build-Max-Heap): Reorder all the data in the heap to make it the largest heap
- 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