Hello, brave friends, hello everyone, I am your little five, the king of the mouth, I am in good health, and my brain is not sick.
I have a wealth of hair loss skills, which can make you a veteran celebrity.
It is my main theme that I will write it when I look at it, and it is my characteristic to pick my feet. There is a trace of strength in the humble. The stupid blessing of a fool is the greatest comfort to me.
Welcome to
fifth of
algorithm Series
sail articles - sorting algorithm.
Preface
This series of articles is based on the two books "Algorithm Graphics" and "Learning JavaScript Algorithms" as the core, and the rest of the materials are supplemented by the author's humble opinion. Strive to use simple and interesting language to lead everyone to the wonder of the algorithm world. Here The series of articles are from shallow to deep, one by one, and then we use the sorting algorithm to open the first chapter of this great route.
This article explains the following five common sorts, which are three simple sorts: "bubble sort", "selective sort", "insertion sort" and two complex sorts: "quick sort", "merge sort".
Each sorting is basic idea,
process diagram,
algorithm analysis,
time and space complexity, and
code implementation.
<Supplementary Notes> recommends to first understand the execution order of the program stack, you can read "Algorithm Diagram-3.3 Stack" , which will make learning fast sorting and merging twice the result with half the effort; this chapter is mainly used to understand the execution order of recursion, I think , If quick sorting and merging are difficult to understand, the bottom line is that recursion is not well understood; this chapter can lay the cornerstone for the following algorithm learning; [The above does not affect the reading of this article, everyone varies from person to person, please choose by
Bubble Sort
👺 Basic idea: <Adjacent elements pairwise contrast to find the maximum >
👺 Process diagram:
👺 Algorithm analysis:
- Outer loop:
length - 1
times - Inner loop:
length-1-The number of elements has been determined (equivalent to the index of the outer loop) times
- Compare the size of adjacent elements, if
arr[j] > arr[j + 1]
, then interchange elements
👺 Complexity:
According to the above analysis, the outer loop is $n-1$ times, and the inner loop is $(n-1)/2$ times, so the total number of executions is $(n-1)^2 / 2$
tips: time complexity and space complexity do not care about low-order variables, coefficients and constants
Time complexity: The highest item above is $n^2$, so the time complexity is $O(n^2)$
space complexity: only temp
variables require memory space, so the space complexity is O(1)$
👺 Code implementation:
const bubbleSort = (arr: number[]) => {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
Select sort
👺 Basic idea: < select the smallest value >
👺 Process diagram:
👺 Algorithm analysis:
- Outer loop:
length - 1
times - Inner loop: loop from
i + 1
, looplength - (i + 1)
times - Record the outer loop
index
asminIndex
, in the inner loop, if a smaller value is found, updateminIndex
- Swap the positions of
arr[index]
andarr[minIndex]
👺 Complexity:
According to the above analysis, the outer loop is $n-1$ times, and the inner loop is $(n-1)/2$ times, so the total number of executions is $(n-1)^2 / 2$
Time complexity: $O(n^2)$
space complexity: temp
and minIndex
variables need memory space, because they do not care about the coefficients, so it is $O(1)$
👺 Code implementation:
const selectSort = (arr: number[]) => {
let minIndex: number;
for (let i = 0; i < arr.length; i++) {
minIndex = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) minIndex = j;
}
let temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
return arr;
}
Insertion sort
👺 Basic idea: <From left to right confirm the position of the element item >
👺 Process diagram:
👺 Algorithm analysis:
- Outer loop: start loop from 1, loop times
length - 1
- Inner loop:
j = i
, compare with the previous item one by one, exchange positions if it is smaller than the previous item, until it is inserted in the correct position
👺 Complexity:
According to the above analysis, the outer loop is $n-1$ times, and the inner loop is $n/2$ times, so the total number of executions is $n * (n-1) / 2$
time complexity: $O(n^2)$
space complexity: only temp
variables need memory space, so it is $O(1)$
👺 Code implementation:
const insertSort = (arr: number[]) => {
for (let i = 1; i < arr.length; i++) {
for (let j = i; j > 0; j--) {
if (arr[j - 1] > arr[j]) {
let temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
Quick sort
👺 Basic idea: < and merge > (we select the middle item of the array as the reference value)
👺 Process diagram:
👺 Algorithm analysis:
- The idea of quick sorting is based on a value, the ones smaller than the benchmark value are pushed into the left array, and those larger than the benchmark value are pushed into the right array; the recursive call until the array is empty; the left and right arrays are spliced with the reference value, and all are spliced After forming the required array;
- By
splice
andMath.floor(arr.length / 2)
acquiresmiddle
value; - Understand the calling sequence of the program stack, you can understand the quick sort well;
👺 Complexity:
The complexity of the recursive process divided by the reference value is $logn$; for
loop is $n$
time complexity: $O(nlogn)$
space complexity: space complexity is the number of recursive layers -> $O(logn)$
👺 Code implementation:
const quickSort = (arr: number[]): number[] => {
if (!arr.length) return [];
let leftArr: number[] = [];
let rightArr: number[] = [];
let middle = arr.splice(Math.floor(arr.length / 2), 1)[0];
arr.forEach(item => {
item < middle ? leftArr.push(item) : rightArr.push(item);
})
return quickSort(leftArr).concat(middle, quickSort(rightArr));
}
Merge sort
👺 Basic idea: < merge the ordered array into a larger ordered array >
👺 Process diagram:
👺 Algorithm analysis:
- Merge sort uses the idea of divide and conquer, that is, the original problem is decomposed into sub-problems, and the sub-problems are merged after solving the sub-problems; therefore, the large array is decomposed into small arrays until there is only one element in the array, at which time the array is all ordered , The ordered arrays are merged and sorted to form a new ordered array, and the sorting is completed after all merges;
- Get the index of the intermediate item through
Math.floor(arr.length / 2)
slice
the array into two through the index and 060ff61a9110b2; - When merging, compare the first item of the left and right arrays, push the smaller value into the new array, and update the left and right arrays
shift
- Understand the calling order of the program stack, you can understand the merge sort well;
👺 Complexity:
[Minutes] Dichotomous operation -> complexity is $logn$; [combined] while loop -> complexity is $n$;
Time complexity: $O(nlogn)$
space complexity: space complexity is the number of recursive layers and the space occupied by temporary arrays -> $logn+n$, so the space complexity is $O(n)$
👺 Code implementation:
const mergeSort = (arr: number[]): number[] => { // 拆分
if (arr.length <= 1) return arr;
let middle = Math.floor(arr.length / 2);
let leftArr = arr.slice(0, middle);
let rightArr = arr.slice(middle);
return merge(mergeSort(leftArr), mergeSort(rightArr));
}
const merge = (leftArr: number[], rightArr: number[]) => { // 合并
let result: number[] = [];
while (leftArr.length || rightArr.length) {
if (leftArr.length && rightArr.length) {
leftArr[0] < rightArr[0]
? result.push(leftArr.shift() as number)
: result.push(rightArr.shift() as number);
} else {
if (leftArr.length) result.push(leftArr.shift() as number);
if (rightArr.length) result.push(rightArr.shift() as number);
}
}
return result;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。