4
头图

Preface

Recently, I have re-entered the pick-up (door) algorithm. I can only take notes in exchange for a bit of comfort for the algorithm scum. I will also record and share it here, and I will summarize it into a series later. Such as "recursion and backtracking", "depth and breadth first", "dynamic programming", "binary search" and "greedy".

Bubble Sort

The basic idea of bubble sort

Given an array, we pour all the elements in the array into the pool, and these elements will be compared with each other and surfaced like bubbles one by one in order of size.

Bubble sort implementation

In each round, starting from the chaotic array head, every two elements are compared and exchanged until the largest or smallest element in this round is placed at the end of the array, and then this process is repeated continuously until all elements are Arrange the position. Among them, the core operation is to compare elements with each other.

Analysis of Bubbling Sorting Examples

Given the array [2, 1, 7, 9, 5, 8] , it is required to sort from left to right and from small to large.

Bubble sorting problem solving ideas

Bubbles from left to right, moving the larger number to the right.

bubble-sort

  • First, the pointer points to the first number, and compare the size of the first number and the second number. Since 2 larger than 1 , it is exchanged in [1, 2, 7, 9, 5, 8] , 06190de683b89b.
  • Next, the pointer moves one step forward and compare 2 and 7 . Since 2 smaller than 7 , the two remain unchanged, [1, 2, 7, 9, 5, 8] . So far, 7 is the largest number.
  • The pointer continues to move forward, compare 7 and 9 , because 7 smaller than 9 , the two remain unchanged, [1, 2, 7, 9, 5, 8] . Now, 9 become the largest number.
  • Later, comparing 9 and 5 , it is obvious that 9 larger than 5 , swap their positions, [1, 2, 7, 5, 9, 8] .
  • Finally, compare 9 and 8 , 9 larger than 8 , exchange their positions, [1, 2, 7, 5, 8, 9] . After the first round of pairwise comparison, 9 bubbled up to the end of the array.
  • Next, perform the second round of comparison, re-point the pointer to the first element, repeat the above operation, and finally, the array becomes: [1, 2, 5, 7, 8, 9] .
  • In the new round of comparison, judge whether there was a pairwise exchange during the previous round of comparison. If no exchange occurs at a time, it proves that the array is already sorted.

Bubble sort code example

// 冒泡排序算法
const bubbleSort = function (arr) {
  const len = arr.length
  // 标记每一轮是否发生来交换
  let hasChange = true

  // 如果没有发生交换则已经是排好序的,直接跳出外层遍历
  for (let i = 0; i < len && hasChange; i++) {
    hasChange = false
    for (let j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        let temp = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = temp
        hasChange = true
      }
    }
  }
}

const arr = [2, 1, 7, 9, 5, 8]
bubbleSort(arr)
console.log('arr: ', arr)

Analysis of Bubble Sorting Algorithm

Bubble sort space complexity

Assuming that the number of elements in the array is n , since in the entire sorting process, we directly exchange elements in the given array, so the space complexity is O(1) .

Bubble sort time complexity

The given array has been arranged in order

  • In this case, we only need to n−1 times, the number of 0 exchanges is 06190de683baf0, and the time complexity is O(n) . This is the best case.
  • The given array is arranged in reverse order. In this case, we need to perform n(n-1)/2 comparisons, and the time complexity is O(n2) . This is the worst case.
  • The given array is messy. In this case, the average time complexity is O(n2) .

It can be seen that the time complexity of bubble sort is O(n2) . It is a stable sorting algorithm. (Stable means that if two equal numbers are in the array, the relative positions of the two equal numbers remain unchanged before and after sorting.)

Insertion Sort (Insertion Sort)

The basic idea of insertion sort

Continuously insert the unsorted numbers into the sorted parts.

Insertion sort features

In bubble sorting, after each round of sorting, the numbers at the back of the array are sorted; for insertion sort, after each round of sorting, the numbers at the front of the array are sorted. of.

Analysis of Insert Sorting Examples

Insertion sort is performed on the array [2, 1, 7, 9, 5, 8]

Insertion sorting problem solving ideas

  • First, divide the array into left and right parts. The left part is the sorted part, and the right part is not sorted. At the beginning, the sorted part on the left has only the first element 2 . Next, we process the elements on the right one by one and place them on the left.

insertion-sort

  • First look 1 , due 1 than 2 small, need to 1 inserted into 2 front, the approach is very simple pairwise exchange position can, [1, 2, 7, 9, 5, 8] .
  • Then, we have to 7 into the left part, because 7 already larger than 2 , indicating that it is the largest element at present, keeping the position unchanged, [1, 2, 7, 9, 5, 8] .
  • In the same way, 9 does not need to change the position, [1, 2, 7, 9, 5, 8 ].
  • Next, how to 5 into the appropriate position. First compare 5 and 9 , since 5 smaller than 9 , exchange in [1, 2, 7, 5, 9, 8 , 06190de683bd1e], continue, because 5 smaller than 7 , exchange in [1, 2, 5, 7, 9, 8] 5 larger than 2 , finally, because 06190de683bd1c is larger than 06190de683bd1d.
  • The last number is 8 . Since 8 smaller than 9 , exchange it in [1, 2, 5, 7, 8, 9] , 06190de683bd80, and then compare 7 and 8 , and find that 8 larger than 7 . This round ends. At this point, the insertion sort is complete.

Insert sort code example

// 插入排序
const insertionSort = function (arr) {
  const len = arr.length
  for (let i = 1; i < len; i++) {
    let current = arr[i]
    for (let j = i - 1; j >= 0; j--) {
      // current 小于 j 指向的左侧值,将 j 指向左侧值右移一位
      if (current < arr[j]) {
        arr[j + 1] = arr[j]
      } else {
        // 否则将 current 插入到 j 位置,跳出内循环
        arr[j] = current
        break
      }
    }
  }
}

const arr = [2, 1, 7, 9, 5, 8]
insertionSort(arr)
console.log('arr: ', arr)

Insertion sort algorithm analysis

Insertion sort space complexity

Assuming that the number of elements in the array is n , since in the entire sorting process, the pair of elements are exchanged directly in the given array, the space complexity is O(1) .

Insertion sort time complexity

  • The given array has been arranged in order. Only need to n-1 times, the number of 0 exchanges is 06190de683be53, and the time complexity is O(n) . This is the best case.
  • The given array is arranged in reverse order. In this case, we need to n(n-1)/2 times, and the time complexity is O(n2) . This is the worst case.
  • The given array is messy. In this case, the average time complexity is O(n2) .

It can be seen that, like bubble sort, the time complexity of insertion sort is O(n2) , and it is also a stable sorting algorithm.

Merge Sort (Merge Sort)

The basic idea of merge sort

The core is divide and conquer, which is to divide a complex problem into two or more identical or similar sub-problems, and then divide the sub-problems into smaller sub-problems, until the sub-problems can be solved simply and directly. The solution to the most original problem is Combination of solutions to sub-problems. Merging and sorting embodies the idea of divide and conquer incisively and vividly.

Merge sort implementation

At the beginning, the array is divided into two sub-arrays from the middle, and the sub-array is recursively divided into smaller sub-arrays until there is only one element in the sub-array before sorting starts.

The sorting method is to merge two elements in the order of size, and then follow the recursive return order, and continuously merge the sorted sub-arrays, until the order of the entire array is arranged.

Merge sort code example

// 归并排序
const mergeSort = function (arr, lo, hi) {
  if (lo === undefined) {
    lo = 0
  }
  if (hi === undefined) {
    hi = arr.length - 1
  }

  // 判断是否剩下最后一个元素
  if (lo >= hi) return

  // 从中间将数组分成两部分
  let mid = lo + Math.floor((hi - lo) / 2)
  console.log('mid', mid)

  // 分别递归将左右两边排好序
  mergeSort(arr, lo, mid)
  mergeSort(arr, mid + 1, hi)

  // 将排好序的左右两半合并
  merge(arr, lo, mid, hi)
}

const merge = function (arr, lo, mid, hi) {
  // 复制一份原来的数组
  const copy = [...arr]

  // 定义一个 k 指针表示从什么位置开始修改原来的数组,
  // i 指针表示左边半的起始位置
  // j 指针便是右半边的其实位置
  let k = lo
  let i = lo
  let j = mid + 1

  while (k <= hi) {
    if (i > mid) {
      arr[k++] = copy[j++]
    } else if (j > hi) {
      arr[k++] = copy[i++]
    } else if (copy[j] < copy[i]) {
      arr[k++] = copy[j++]
    } else {
      arr[k++] = copy[i++]
    }
  }
}
const arr = [2, 1, 7, 9, 5, 8]
mergeSort(arr)
console.log('arr: ', arr)

Among them, While statement comparison, a total of four situations may occur.

  • The numbers on the left half have been processed, and only the numbers on the right half are left. You only need to copy the numbers on the right half one by one.
  • The numbers on the right half have been processed, and only the numbers on the left half are left. Just copy the numbers on the left half one by one.
  • The number on the right is smaller than the number on the left, copy the number on the right to the appropriate position, and the j pointer moves one place forward.
  • The number on the left is smaller than the number on the right, copy the number on the left to the appropriate position, and the i pointer moves one place forward.

Analysis of Merge Sorting Examples

Use the merge sort algorithm to sort the array [2, 1, 7, 9, 5, 8] .

The idea of merging and sorting problems

merge-sort

First, continue to split the array until each sub-array contains only one element.
Next, recursively merge and cut the divided sub-arrays in the order of size, the order of recursion is similar to the forward traversal in the binary tree.

  • Combine [2] and [1] into [1, 2] .
  • The sub-arrays [1, 2] and [7] merged.
  • On the right, merge [9] and [5] .
  • Then merge [5, 9] and [8] .
  • Finally, merge [1, 2, 7] and [5, 8, 9] into [1, 2, 5, 8, 9] , you can sort the entire array.

The operation steps for merging the arrays [1, 2, 7] and [5, 8, 9]

merge-array

  • The array [1, 2, 7] is L , and [5, 8, 9] is represented R
  • When merging, create and allocate a new array T save the result. The size of the array should be the sum of the lengths of the two sub-arrays
  • Then the subscripts i , j , k respectively point to the starting point of each array.
  • L[i] and R[j] pointed to by the subscripts i and j, and put them in the place pointed to by the subscript k 1 less than 5 .
  • Move i and k , continue to compare L[i] and R[j] , 2 smaller than 5 .
  • i and k continue to move forward, 5 smaller than 7 .
  • Move j and k , continue to compare L[i] and R[j], 7 smaller than 8 .
  • At this time, the array on the left has been processed, just put the remaining elements of the array on the right into the result array.

For the merge to be successful, the prerequisite must be that the two sub-arrays have been sorted separately.

Analysis of merge sort algorithm

Merge sort space complexity

Since merging n elements needs to allocate an n , after the merging is completed, the space of this array will be released, so the space complexity of the algorithm is O(n) . Merge sort is also a stable sorting algorithm.

Merge sort time complexity

The merge algorithm is a process of constant recursion.

Example: The number of elements in the array is n , and the time complexity is a function T(n)

Solution: n this problem with a n/2 . The time complexity of each sub-problem is T(n/2) , so the complexity of the two sub-problems is 2×T(n/2) . When the two sub-problems have been solved, that is, the two sub-arrays are sorted, and they need to be merged. There are n elements in total, and each n-1 times, so the complexity of the merge is O(n) . From this we get the recursive complexity formula: T(n) = 2×T(n/2) + O(n) .

For the equation to solve, continue to put a scale n break the problem into a size of n/2 problems, it has been broken down to size as 1 . If n is equal to 2 , it only needs to be divided once; if n is equal to 4 , it needs to be divided into 2 times. The times here are categorized according to changes in scale.

By analogy, for n , a total of log(n) layer size segmentation is required. In each layer, we have to merge, and the elements involved are actually all the elements in the array. Therefore, the merge complexity of each layer is O(n) , so the overall complexity is O(nlogn) .

Quick Sort (Quick Sort)

Basic idea of quick sort

Quick sort also uses the idea of divide and conquer.

Quick sort implementation

Filter the original array into two smaller and larger sub-arrays, and then sort the two sub-arrays recursively.

Example : Arrange all the students in the class in a row in order of height.

solution : The teacher randomly selects classmate A first, so that all other classmates and classmate A are taller and shorter than classmate A. Those who are shorter than A stand on the left of A, and those who are taller than A stand on the right of A. Next, the teacher selected classmate B and classmate C from the students on the left to the right, and then kept screening and ranking them.

In the process of dividing into two smaller and larger sub-arrays, how to choose a benchmark value (ie classmate A, B, C, etc.) is particularly critical.

Quick sort implementation example analysis

Sort the array [2,1,7,9,5,8]

Quick sorting of problem-solving ideas

quick-sort

  • According to the idea of quick sort, first filter the array into two smaller and larger sub-arrays.
  • Randomly select a number from the array as the reference value, such as 7 , so the original array is divided into two sub-arrays. Note: Quicksort is to perform various exchange operations directly in the original array, so when the sub-array is split out, the arrangement in the original array is also changed.
  • Next, choose the smaller sub-array 2 as a reference value, the election in the larger sub-array 8 as a reference value, continues to divide sub-array.
  • 1 the sub-array with the number of elements greater than 06190de683c832. When the number of elements in all the sub-arrays is 1 , the original array is also sorted.

Quick sort code example

// 快速排序
const quickSort = function (arr, lo, hi) {
  if (lo === undefined) {
    lo = 0
  }
  if (hi === undefined) {
    hi = arr.length - 1
  }

  // 判断是否只剩下一个元素,是,则直接返回
  if (lo >= hi) return

  // 利用 partition 函数找到一个随机的基准点
  const p = partition(arr, lo, hi)

  // 递归对基准点左半边和右半边的数进行排序
  quickSort(arr, lo, p - 1)
  quickSort(arr, p + 1, hi)
}

// 交换数组位置
const swap = function (arr, i, j) {
  let temp = arr[i]
  arr[i] = arr[j]
  arr[j] = temp
}

// 随机获取位置索引
const randomPos = function (lo, hi) {
  return lo + Math.floor(Math.random() * (hi - lo))
}

const partition = function (arr, lo, hi) {
  const pos = randomPos(lo, hi)
  console.log('pos: ', pos)
  swap(arr, pos, hi)

  let i = lo
  let j = lo

  // 从左到右用每个数和基准值比较,若比基准值小,则放在指针 i 指向的位置
  // 循环完毕后,i 指针之前的数都比基准值小
  while (j < hi) {
    if (arr[j] <= arr[hi]) {
      swap(arr, i++, j)
    }
    j++
  }
  // 末尾的基准值放置到指针 i 的位置, i 指针之后的数都比基准值大
  swap(arr, i, j)

  // 返回指针 i,作为基准点的位置
  return i
}

const arr = [2, 1, 7, 9, 5, 8]
quickSort(arr)
console.log(arr)

Quicksort algorithm analysis

Quick sort time complexity

1. Optimal situation: The selected reference value is the middle number of the current sub-array.
This segmentation can ensure that a n can be evenly decomposed into two n/2 (the same division method is also used for the merge sort), and the time complexity is: T(n)=2xT(n/2) + O(n) .

The size is n the problem into n/2 when two sub-problems, and the reference value of n-1 comparisons, complexity is O(n) . Obviously, in the optimal case, the complexity of quick sort is also O(nlogn) .

2. Worst-case scenario: The base value selects the largest, latter, and smallest value in the sub-array.

Each time, the sub-array is divided into two smaller sub-arrays, one of which has a length of 1 , and the other has a length of only 1 less than the atomic array.

Example: For an array, the benchmark value for each selection is 9、8、7、5、2 .

Solution: The division process is similar to the bubble sort process.

The algorithm complexity is O(n^2) .

Tip: The worst case can be avoided by randomly selecting the reference value.

Quicksort space complexity

Unlike merge sort, in each recursion process, quick sort only needs to open up O(1) to complete the swap operation to modify the array directly, and because the number of recursion is logn , its overall space complexity depends entirely on The number of times the stack is pressed, therefore, its space complexity is O(logn) .


wuwhs
6k 声望2.5k 粉丝

Code for work, write for progress!