The third tier

Bubble sort

The idea of bubble sort is to compare adjacent elements in pairs. When an element is larger than the adjacent element on the right, they are swapped. When an element is less than or equal to the adjacent element on the right, the position does not change

The implementation code

Function bubbleSort(list:Array<number>) {for (let I = 0; i < list.length - 1; For (let j = 0; let j = 0; let j = 0; j < list.length - i -1; j++) { if( list[j] > list[j+1]) { let temp = list[j]; list[j] = list[j+1]; list[j+1] = temp } } } return list }Copy the code

Optimization 1: Whether tags are ordered

Function bubbleSortTwo(list:Array<number>) {for (let I = 0; i < list.length - 1; For (let j = 0; let j = 0; let j = 0; j < list.length - i -1; j++) { if( list[j] > list[j+1]) { let temp = list[j]; list[j] = list[j+1]; List [j+1] = temp = false}} if(isSorted) {break}} return list}Copy the code

Optimization 3: ordered area optimization

Function bubbleSortThree(list:Array<number>) {let sortBorder = list.length-1; // Let lastExcChangeIndex = 0; For (let I = 0; let I = 0; i < list.length - 1; i++) { let isSorted = true; For (let j = 0; let j = 0; j < sortBorder; j++) { if( list[j] > list[j+1]) { let temp = list[j]; list[j] = list[j+1]; list[j+1] = temp; // If an exchange occurs, then isSorted = false; LastExcChangeIndex = j}} sortBorder = lastExcChangeIndex if(isSorted) {break}} return list}Copy the code

Cocktail optimization

Each round of bubble sort compares elements from left to right, making a one-way comparison, whereas cocktail sort compares and swaps elements in both directions

Function bubbleSortFour(list:Array<number>) {let outLength = math.floor (list.length/2) for (let i = 0; i < outLength; i++) { let sorted = true; For (let j = I; j < list.length - 1 - i; j++) { if(list[j] > list[j + 1]){ let temp = list[j]; list[j] = list[j + 1]; list[j + 1] = temp sorted = false } } if(sorted){ break } sorted = true; For (let j = list.length-1-i; j > i ; j--) { if(list[j] < list[j - 1]){ let temp = list[j]; list[j] = list[j - 1]; list[j - 1] = temp; sorted = false; } } if(sorted) { break } } return list }Copy the code

Cocktail + ordered area optimization

Function bubbleSortFive(list:Array<number>) {let leftSortBorder = 0; let leftLastSortIndex= 0; let rightSortBorder = list.length - 1; let rightLastSortIndex = 0; Let outLength = math.floor (list.length/2) for (let I = 0; i < outLength; i++) { let sorted = true; For (let j = leftSortBorder; j < rightSortBorder; j++) { if(list[j] > list[j + 1]){ let temp = list[j]; list[j] = list[j + 1]; list[j + 1] = temp sorted = false; RightLastSortIndex = j; } // Assign the right boundary rightSortBorder = rightLastSortIndex if(sorted){break} sorted = true; For (let j = rightSortBorder; j > leftSortBorder ; j--) { if(list[j] < list[j - 1]){ let temp = list[j]; list[j] = list[j - 1]; list[j - 1] = temp; sorted = false; LeftLastSortIndex = j-1; If (sorted) {break}} return list}Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

Selection sort

The idea of selection sort: each round to select the smallest, directly switched to the left of the array

The implementation code

      function sort(arr:Array<number>) {
          for (let index = 0; index < arr.length - 1; index++) {
              let minIndx = index;
              for (let j = index + 1; j < arr.length; j++) {
                  if(arr[j] < arr[minIndx]) {
                      minIndx = j
                  }            
              };
              let temp = arr[minIndx];
              arr[minIndx] = arr[index];
              arr[index] = temp
                      
          }
          return arr
      }
Copy the code

Selection sort solves the code efficiency problem caused by the frequent element exchange operation of bubble sort, but also becomes unstable sort

Best explanation: my personal blog, need TZ, here is the nugget version

Insertion sort

The idea of insertion sort is to maintain an ordered area and insert the elements one by one into the appropriate place in the ordered area until all the elements are ordered

The implementation code

function insertSort(arr:Array<number>) { for (let index = 1; index < arr.length; index++) { let temp = arr[index]; let j = index - 1; // Compare elements from the right and shift for (; j >= 0 && arr[j] > temp; J --) {arr[j + 1] = arr[j]} insertValue arr[j + 1] = temp} return arr};Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

summary

The average time complexity of the sorting algorithms in the third tier is O(n2).

Differences and differences

1. Performance

The number of comparisons and exchanges of elements in bubble and insert sorts depends on how ordered the original array is

But overall, insertion sort performs slightly better than bubble sort

Because in bubble sort, each of the two elements are exchanged independently of each other

Insertion sort is continuous

Obviously insertion sort eliminates a lot of unnecessary swapping

In addition, selection sort is different from the previous two. It has a fixed number of commutations of elements, regardless of the order of the original array

Therefore, when the original array is nearly ordered, the performance of insertion sort is the best, and when most elements of the original array are unordered, the performance of selection sort is the best

2. Whether it is stable

Bubble sort and insertion sort are stable, and selection sort is unstable

The second tier

Heap sort

Heapsort idea: Heapsort is a sorting algorithm based on binary heap, which has the ability of self-adjustment. It can be summarized as: to build the unordered array into binary heap, if the order needs to be ascending, the maximum binary heap will be built; if the order needs to be descending, the minimum binary heap will be built

The implementation code

/** * @param {Array<number>} arr Specifies the heap to be adjusted * @param {number} parentIndex Specifies the parent node to be dropped * @param {number} Length Specifies the valid heap size * @description */ function downAdjust(arr: Array<number>, parentIndex: Number, length:number) {// temp Stores the value of the parent node and uses it as the final assignment. Let temp = arr[parentIndex]; // Let childrenIndex = parentIndex * 2 + 1; While (childrenIndex < length) {// if right node is present, If (childrenIndex + 1 < length && ARR [childrenIndex + 1] > ARR [childrenIndex]) {childrenIndex = childrenIndex + 1; If (temp >= arr[childrenIndex]) {break} arr[parentIndex] = arr[childrenIndex]; parentIndex = childrenIndex childrenIndex = childrenIndex * 2 + 1 } arr[parentIndex] = temp; } /** * @param {Array<number>} * @description */ function heapSort(arr:Array<number>) {// 1. For (let index = arr.length-2; index >= 0; Index --) {downAdjust(arr, index, arr.length - 1)} console.log(' maximum heap: %j', arr) // 2. For (let index = arr.length-1; index >= 0; Index --) {// swap the last element with the top of the heap let temp = arr[0]; arr[0] = arr[index]; arr[index] = temp; / / sink to adjust the maximum heap downAdjust (arr. Zero, index) / / the index value of effective length of pile Very clever here}} the function main () {let arr = [1, 3, 2, 6, 5, 7, 8, 9, 10, 0]; heapSort(arr); console.log(arr) }Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

Merge sort

Merge sort idea: split the array into two groups, ordered merge, and recursion, classic divide and conquer idea

The implementation code

Function merge(arr:Array<number>, start:number, end:number, middle:number) { Let tempArray = new Array(end-start + 1); let p1 = start; let p2 = middle + 1; let p = 0; while(p1 <= middle && p2 <= end) { if(arr[p1] <= arr[p2] ) { tempArray[p++] = arr[p1++] } else { tempArray[p++] = While (p1 <= middle) {tempArray[p++] = arr[p1++]} while(p2 <= end) { TempArray [p++] = arr[p2++]} for (let index = 0; index < tempArray.length; index++) { arr[start + index] = tempArray[index] } } function sort(arr:Array<number>, start:number, End :number) {if(start < end) {/* start = 5; end = 10; The middle = 7; Note that we're rounding down: 5,6,7,8,9,10, so middle=7; 5,6,7 as a group; If 8, 9, 10 is a group of 6,7,8,9,10, middle=8; 6,7,8 as a group; 9 and 10 are a set of formulas: Math. Floor ((end-start)/2) + start Math.floor(A / Math.pow(2, B)) = > Math. Floor (A/(2 * * B)) = > (A > > B) so: Math. The floor ((end - start) / 2) + start = = = ((end - start) > > 1) + start * / / / Let middle = ((end-start)>>1) + start; let middle = ((end-start)>>1) + start; sort(arr, start, middle); sort(arr, middle + 1, end); Function main() {const arr = [5, 8, 6, 3, 9, 2, 1, 7]; const arr = [5, 8, 6, 3, 9, 2, 1, 7]; console.log(sort(arr, 0, arr.length - 1)) }Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

Quick sort

The idea of quicksort: Quicksort is also a classic representation of the idea of divide and conquer, namely: select a reference element, larger than it on one side, smaller than it on the other side, and recurse

Implementation code:

Bilateral method:

/** * @param {Array<number>} arr Array to be exchanged * @param {number} startIndex startIndex * @param {number} endIndex endIndex * @returns */ function partition(arR: Array<number>, startIndex: number, endIndex: number) Const pivot = arr[startIndex]; const pivot = arr[startIndex]; // Left pointer let left = startIndex; // Let right = endIndex; while(left ! = right) {// While (left < right && arr[right] > pivot) {right--} // While (left < right && arr[right] > pivot) {left -- Arr [left] <= pivot) {left++} if(left < right) {let p = arr[left]; arr[left] = arr[right]; arr[right] = p; } // Arr [startIndex] = arr[left]; arr[left] = pivot; /* How to ensure that the swapped left element is either less than or greater than the base element? If either left or right stops, If left <= right, then right must be an element less than = pivot */ return left} function quickSort(arr: Array<number>, startIndex: number, endIndex: Number) {if(startIndex >= endIndex) {return} let pivotIndex = partition(arR, startIndex, endIndex); QuickSort (ARR, startIndex, pivotIndex - 1); quickSort(arr, pivotIndex + 1, endIndex); } function main() { let arr = [4, 2, 6, 2, 2, 2, 2, 8]; quickSort(arr, 0, arr.length - 1 ); console.log(arr) }Copy the code

Unilateral method:

/** * @param {Array<number>} arr Array to be exchanged * @param {number} startIndex start position * @param {number} endIndex End position * @description Function partition(arR: Array<number>, startIndex: number, endIndex: number); Let pivot = arr[startIndex]; let pivot = arr[startIndex]; // Let mark = startIndex; for (let index = startIndex; index <= endIndex; index++) { if(arr[index] < pivot) { mark++; let p =arr[mark]; arr[mark] = arr[index]; arr[index] = p } } arr[startIndex] = arr[mark]; arr[mark] = pivot; return mark } function quickSort(arr: Array<number>, startIndex: number, endIndex: number){ if(startIndex >= endIndex) { return } const pivotIndex = partition(arr, startIndex, endIndex); quickSort(arr, startIndex, pivotIndex - 1); quickSort(arr, pivotIndex + 1, endIndex) } function main() { let arr = [4, 4, 6, 5, 3, 2, 8, 1]; quickSort(arr, 0, arr.length - 1); console.log(arr) }Copy the code

Non-recursive implementation:

/** * @param {Array<number>} arr Array to be sorted * @param {number} startIndex Start position * @param {number} endIndex End position * @returns Function partition(arR: Array<number>, startIndex: number, endIndex: number) Number) {// let pivot = arr[startIndex] // Let mark = startIndex for (let index = startIndex; index <= endIndex; index++) { if(arr[index] < pivot) { mark++; let p = arr[mark]; arr[mark] = arr[index]; arr[index] = p } }; arr[startIndex] = arr[mark]; arr[mark] = pivot; return mark } interface StackItem { startIndex: number; endIndex: number; } function quickSort(arr: Array<number>, startIndex: number, endIndex: Number) {// use a stack instead of a recursive call stack let quickSortStack:Array<StackItem> = []; Let obj:StackItem = {startIndex, endIndex}; quickSortStack.push(obj); While (quickSortStack.length) {let item = quickSortStack.pop() as StackItem; let { startIndex, endIndex } = item let pivotIndex = partition(arr, startIndex, endIndex); If (startIndex < pivotIndex - 1) {quickSortStack.push({startIndex, endIndex: pivotIndex - 1 }) } if(endIndex > pivotIndex + 1) { quickSortStack.push({ startIndex : pivotIndex + 1, endIndex: endIndex }) } } } function main() { let arr = [4, 4, 6, 5, 3, 2, 8, 1]; quickSort(arr, 0, arr.length - 1); console.log(arr) }Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

Hill sorting

Hill sort is an updated version of insertion sort, which does some “preprocessing” of the original array to make most of the elements of the original array ordered

The implementation code

    function sort(arr: Array<number>) {
        let d = arr.length;
        while(d > 1) {
            d = Math.floor(d / 2);
            for (let x = 0; x < d; x++) {
                for (let index = x + d; index < arr.length; index+=d) {
                    let temp = arr[index];
                    let j = index - d;
                    for (; j >= 0 && arr[j] > temp; j-= d) {
                        arr[index] = arr[j];
                    }
                    arr[j+ d] = temp;
                }
            }
        }
        return arr
    }
Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

summary

The second tier is an order of magnitude better than the third tier

The average time complexity of hill sort is the fastest to reach O(n1.3), and the average time complexity of quicksort, merge sort and heapsort is in O(nlogn).

Differences between the latter three:

1. Quicksort has an average time of O(nlogn), but in the extreme case it is O(n2), while heapsort and merge sort are stable at O(nlogn).

2. In terms of average complexity, heapsort performance is slightly lower than quicksort and merge sort, mainly because the parent and child nodes of binary heap are not contiguous in memory

When access to memory data, order to store data, reading and writing is often the highest efficiency, according to the principle of the space limitations of CPU, each time the CPU access memory, the memory in the adjacent data is also stored in cache, read the data, the adjacent next time need not read from the memory, but directly read from the CPU cache

3. Merge sort is stable, heap sort and quicksort are unstable

4. Quicksort and heapsort do not require additional storage space, while merge sort requires the creation of merge array

The first tier

Count sorting

The idea of counting sort is to determine the position of elements based on their subscripts, without comparing or swapping elements

The implementation code

Simple version of

function countSort(arr: Array<number>):Array<number> { // 1. Let Max = arr[0]; for (let index = 1; index < arr.length; index++) { if(arr[index] > max) { max = arr[index] } } // 2. Let countArray = (new Array(Max + 1)).fill(0); For (let index = 0; // 3. index < arr.length; Index ++) {countArray[arr[index]]++} let sortedArray = new Array(); for (let index = 0; index <= countArray.length; index++) { for (let j = 0; j < countArray[index]; j++) { sortedArray.push(index) } } return sortedArray }Copy the code

Optimized version

function countSort(arr: Array<number>) { // 1. Let Max = arr[0]; let min = arr[0]; for (let index = 1; index < arr.length; index++) { if(arr[index] > max) { max = arr[index] } if(arr[index] < min) { min = arr[index] } } // 2. Let length = max-min + 1; let countArray = (new Array(length)).fill(0) // 3. For (let index = 0; index < arr.length; index++) { countArray[arr[index] - min]++ } // 4. Let sortedArray = [] for (let index = 0; index < countArray.length; index++) { for (let j = 0; j < countArray[index]; j++) { sortedArray.push(index + min) } } return sortedArray }Copy the code

Stable version

function countSort(arr: Array<number>) { // 1. Let Max = arr[0]; let min = arr[0]; for (let index = 1; index < arr.length; index++) { if(arr[index] > max) { max = arr[index] } if(arr[index] < min) { min = arr[index] } } // 2. Let length = max-min + 1; let countArray = (new Array(length)).fill(0) // 3. For (let index = 0; index < arr.length; index++) { countArray[arr[index] - min]++ } // 4. For (let index = 1; let index = 1; index < countArray.length; index++) { countArray[index] += countArray[index - 1] } // 5. SortedArray = new Array(arr.length); sortedArray = new Array(arr.length); for (let index = arr.length - 1; index >= 0; SortedArray [countArray[arr[index] -min] -1] = arr[index] countArray[arr[index] -min] -1] = arr[index] countArray[arr] index  - min]-- } return sortedArray }Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

Bucket sort

Bucket sort is an updated version of counting sort, making up for the limitations of counting sort (counting sort is not applicable to non-integer sort).

The implementation code

function bucketSort(arr:Array<number>) { // 1. D let Max = arr[0]; let min = arr[0]; for (let index = 0; index < arr.length; index++) { if(arr[index] > max) { max = arr[index] } if(arr[index] < min) { min = arr[index] } } const d = max - min; // 2. Const bucketNum = arr.length; / * small pit: Let bucketList = new Array(bucketNum).fill([]) */ let bucketList = new Array(bucketNum) for (let index = 0; index < bucketList.length; index++) { bucketList[index] = [] } // 3. Let area = d/(bucketnum-1); let area = d/(bucketnum-1); for (let index = 0; index < arr.length; index++) { let num = Math.floor((arr[index] - min)/area); BucketList [num].push(arr[index])} /* bucketList[num].push(arr[index])} (element -min)/area = the number of buckets in the array, which in theory should be round up, but the array index is from 0, so it is math.floor 2. Because (Max. -min)/area must be the last bucket and an integer, that is:  d = max - min area = d/num result = (max - min)/area = (max - min)/d * num = (max - min)/(max - min) * num result === */ // 4. */ // 4. */ // 4. For (let index = 0; index < bucketList.length; index++) { bucketList[index] = bucketList[index].sort((a:number, B :number) => (a-b))} return bucketList.flat()}Copy the code

Best explanation: my personal blog, need TZ, here is the nugget version

Radix sort

Radix sort is also to solve the limitations of counting sort, such as sorting English words, the idea is: to split the work into multiple rounds, each round of a single character using counting sort

The implementation code

// Value range of ASCII code let ASCII = 128; function getCharIndex(str:string,index:number):number { if(str.length <= index) { return 0 } return str.charCodeAt(index) } function radixSort(arr:Array<string>, maxLength:number) { let sortedArray = new Array(arr.length); for (let k = maxLength - 1; k >= 0; K --) {// let countArray = new Array(ASCII).fill(0); for (let index = 0; index < arr.length; index++) { let i = getCharIndex(arr[index], k) countArray[i]++ } // 2. For (let index = 1; let index = 1; index < countArray.length; index++) { countArray[index] += countArray[index-1] } // 3. For (let index = arr.length-1; let index = arr. index >= 0; index--) { let i = getCharIndex(arr[index], k); let sortedIndex = countArray[i] - 1; sortedArray[sortedIndex] = arr[index]; countArray[i]-- } arr=[...sortedArray] } return arr } function main() { const arr = ['qd', 'abc', 'qwe', 'hhh', 'a', 'cws', 'ope']; console.log(radixSort(arr, 3)) }Copy the code

summary

The first echelon of sorting algorithms are linear complexity sorting algorithms

The counting sort algorithm has a time complexity of O(n + m) where m is the range of the original array

The bucket sort algorithm has a time complexity of O(n) where n is the number of buckets

The time complexity of radix sorting algorithm is O(k(n + m)), where K is the maximum number of bits of an element, and m is the value range of each bit

In addition, these three sorting algorithms are stable sorting

Great summary

Summary from: comic algorithm little grey algorithm journey + programmer little grey princess article