Jsliang job series – 44 – algorithm series summary

Time:2021-12-8

One directory II. Preface III. collect question bank Four turn hump Five bubble sorting5.1 solution I5.2 solution II5.3 solution III Six choice sorting6.1 select sort writing method6.2 binary sorting Stability of seven sorting algorithms Eight insert sort IX. quick sort9.1 method 1: basic ideas9.2 method 2: optimization9.3 method 3: three way fast exhaust Ten merge sort Time complexity of Xi sorting algorithm Twelve search12.1 sequential traversal12.2 double pointer12.3 binary search XIII. References

II. Preface

Return to directory

Front end, difficult to get started; Front end, it’s hard to do well.

Now I also brush the questions at the interview. Although there are some defects in dynamic programming and greedy algorithm, I can still cope with string, array, stack, queue, linked list, tree, depth first search, breadth first search, backtracking, sliding window, double pointer and other topics.

Brush one question and one question every day15min~2hGenerally speaking, I don’t think it’s very scientific to review the possible algorithms and data structures after the interview in an instant.

If you want to understand it all night, if you can do it again, I’ll choose Li Bai~

It is recommended to review various sorting algorithms and search algorithms, and then look at the red black tree or AVL tree. Others really follow suit. If you don’t have much contact with algorithms and data structures at ordinary times, it is still difficult to understand so much at once.

Come on!

III. collect question bank

Return to directory

The following is a collection of some topics that have appeared in the market. You can have a look at those who are interested. Some have posted leetcode addresses:

  1. Quick sort
  2. Implement an algorithm to complete string addition, such as'111' + '2222' = '2333'。 (high precision algorithm)
  3. There is one'123456789101112131415....n+1'For a sequence like this, find the second ordermDigit number, e.gM = 11 - > output 0M = 12 - > output 1
  4. There is an orderly increasing sequence. Find how many different numbers there are. such as[1, 5, 7, 7, 8, 9, 9]。 There are five different numbers:[1, 5, 7, 8, 9]
  5. Comparison between red black tree and hash table
  6. How hash tables resolve conflicts
  7. Non recursive implementation of post order traversal of tree
  8. 350 intersection of two arrays II
  9. 611 number of effective triangles
  10. 659 split array into continuous subsequences
  11. Catch the rain. Given array[1, 8, 6, 2, 5, 4, 8, 3, 7], indicating the maximum amount of water the container can hold.
  12. Write an array to remove duplicates.O(n^2)andO(n)Time complexity implementation
  13. I now have an array[1,2,3,4], please implement the algorithm to get the fully arranged array of this array, such as[2,1,3,4][2,1,4,3], what is the time complexity of your algorithm?
  14. I now have a backpack with a capacity ofmAnd then there’snGoods weighingw1,w2,w3...wn, the value of each good isv1,v2,v3...vnwandvIt doesn’t matter. Ask for the maximum value that the backpack can hold.
  15. Traversal mode and characteristics of binary tree
  16. Sorting algorithm and its principle (handwriting)
  17. Maximum depth of 104 binary tree
  18. 572 subtree of another tree
  19. 100 – same tree
  20. 226 flip binary tree
  21. 509 Fibonacci number
  22. 88 – merge two ordered arrays
  23. 384 – scramble array
  24. 56 consolidation interval

Four turn hump

Return to directory

Implement a method to change the underline naming method of incoming objects to hump type (considering recursive scenarios)

const obj = {
  my_name: 'jsliang',
  wo_de_jia: {
    zu_fang: 'guangzhou',
    jia: 'heyuan',
    zu_ji: 'maoming',
  },
};

/*
Convert to:
{
  myName: 'jsliang',
  woDeJia: { jia: 'heyuan', zuFang: 'guangzhou', zuJi: 'maoming' },
}
*/

const getType = arg => Object.prototype.toString.call(arg).slice(8, -1);

const changeCamel = str => str.split('_').map((item, index) => index === 0 ? item : item.slice(0, 1).toUpperCase() + item.slice(1)).join('');

const change = (obj) => {
  for (let i in obj) {
    if (obj.hasOwnProperty(i)) {
      if (getType(obj[i]) === 'Object' && i.includes('_')) {
        const now = changeCamel(i);
        obj[now] = obj[i];
        delete obj[i];
        change(obj[now]);
      } else if (getType(obj[i]) === 'Object') {
        change(obj[i]);
      } else if (i.includes('_')) {
        const now = changeCamel(i);
        obj[now] = obj[i];
        delete obj[i];
      }
    }
  }

  return obj;
};

console.log(change(obj));

Return to directory

Speaking of bubble sorting,jsliangCan shiver.

In the following sorting, we are talking about sequential sorting, that is[1, 2, 3, 4]

The so-called bubbling is to compare the numbers in the array in pairs, move the larger number back each time, and the smaller number moves to the head of the array, so it looks like a small bubble floating to the water surface.

It’s like an array[3, 2, 1]

  1. take3Move back and become[2, 1, 3]
  2. take2Move back and become[1, 2, 3]

Of course, the same bubble sorting method also has different ways.

Here are three methods. I hope you can understand them.

5.1 solution I

Return to directory

  1. Double cycle
  2. iRepresents the current number,j = i + 1
  3. Represents the secondiCompare with the following numbers one by one
Console.log ('solution I ');
const bubbleSortOne = (arr) => {
  let time = 0;

  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      time++;
      if (arr[i] > arr[j]) {
        [arr[i], arr[j]] = [arr[j], arr[i]];
      }
    }
  }

  return [arr, time];
};

console.log(bubbleSortOne([1, 2, 3])); // [ [ 1, 2, 3 ], 3 ]
console.log(bubbleSortOne([1, 3, 2])); // [ [ 1, 2, 3 ], 3 ]
console.log(bubbleSortOne([3, 2, 1])); // [ [ 1, 2, 3 ], 3 ]

Return to directory
  • iIndicates the number of rounds
  • jNumber indicating the comparison between before and after
  • Every timejandj + 1Digital comparison of
Console.log ('solution 2: ');
const bubbleSortTwo = (arr) => {
  let time = 0;

  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      time++;
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }

  return [arr, time];
};

console.log(bubbleSortTwo([1, 2, 3])); // [ [ 1, 2, 3 ], 3 ]
console.log(bubbleSortTwo([1, 3, 2])); // [ [ 1, 2, 3 ], 3 ]
console.log(bubbleSortTwo([3, 2, 1])); // [ [ 1, 2, 3 ], 3 ]

Return to directory
  • General same solution II
  • The optimization point isflag
  • If there is no comparison in a round, the cycle is aborted
Console.log ('solution 3: ');
const bubbleSortThree = (arr) => {
  let time = 0;

  for (let i = 0; i < arr.length; i++) {
    let flag = false;
    for (let j = 0; j < arr.length - i - 1; j++) {
      time++;
      if (arr[j] > arr[j + 1]) {
        flag = true;
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
    if (!flag) {
      return [arr, time];
    }
  }

  return [arr, time];
}

console.log(bubbleSortThree([1, 2, 3])); // [ [ 1, 2, 3 ], 2 ]
console.log(bubbleSortThree([1, 3, 2])); // [ [ 1, 2, 3 ], 3 ]
console.log(bubbleSortThree([3, 2, 1])); // [ [ 1, 2, 3 ], 3 ]

Return to directory

Select sorting, and select the largest or smallest number to replace each traversal.

6.1 select sort writing method

Return to directory

const selectSort = (arr) => {
  for (let i = 0; i < arr.length; i++) {
    let min = i;
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[min]) {
        min = j;
      }
    }
    [arr[min], arr[i]] = [arr[i], arr[min]];
  }

  return arr;
};

console.log(selectSort([7, 1, 3, 2, 5, 4, 7, 6, 1])); // [1, 1, 2, 3, 4, 5, 6, 7, 7]

Return to directory

In the selection sorting, we always select the smallest or largest one at the beginning of the array, which uses an element. If we find the maximum and minimum values, the efficiency will be doubled!

const twoSort = (arr) => {

  const length = arr.length;

  for (let i = 0; i < length / 2; i++) {
    let minIndex = i, maxIndex = i;
    for (let j = i + 1; j < length - i; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
      if (arr[j] > arr[maxIndex]) {
        maxIndex = j;
      }
    }
    [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
    
    //If the subscript of the maximum is equal to I, that is, arr [i] is the maximum
    //Since arr [i] is the first of the current traversal rounds, it has been exchanged with arr [minindex]
    //Therefore, the subscript of the maximum value needs to be tracked to arr[i] the latest subscript minindex.
    if (maxIndex === i) {
      maxIndex = minIndex;
    }

    [arr[length - i - 1], arr[maxIndex]] = [arr[maxIndex], arr[length - i - 1]];
    console.log(arr);
  }

  return arr;
};

console.log(twoSort([7, 3, 2, 4, 6, 5, 1])); // [ 1, 2, 3, 4, 5, 6, 7 ]

Of course, although the efficiency is improved after optimization, the time complexity has not changed.

Time complexity has nothing to do with constants, soO(n^2)be in2stillO(n^2)

Return to directory

After learning about bubble sorting and selection sorting, let’s compare the similarities and differences between them.

Similarities:

  • Both are two-layer loops with a time complexity ofO(n^2)
  • Only a limited number of variables are used, with time complexityO(1)

difference:

  • Bubble sorting keeps exchanging during the comparison process
  • The optional sorting adds a subscript for the minimum / maximum value of the variable to save, and the exchange is only after the traversal is completed, reducing the number of exchanges

In fact, there is another difference between the two:

  • Bubble sorting is stable, and selective sorting is unstable

Suppose there is an array[2, 2, 1]

In bubble sorting, only the number on the left is greater than the number on the right can be exchanged, and the same number will not be exchanged, so it is stable.

In the selection sort, the process of exchanging the minimum value and the first place will destroy the stability. For example, when the array above is exchanged for the first time in the selection sort, two of the original sequence2The relative order of is changed, so it is unstable.

So how to understand stability and instability?

  • It is assumed that there are multiple records with the same keyword in the record sequence to be sorted. If sorted, the relative order of these records remains unchanged, that is, in the original sequence,r[i] = r[j], andr[i]stayr[j]Before, and in the sorted sequence,r[i]Stillr[j]Previously, this sort algorithm was said to be stable; Otherwise, it is called unstable.

What is the significance of the stability of sorting algorithm?

  • When the content to be sorted is multiple numeric attributes of an object, and its original order has meaning, if we need to maintain the original sorting meaning after secondary sorting, we need to use a stable algorithm.

Eight insert sort

Return to directory

The so-called insertion sorting is to insert the current number into the appropriate position in front of the queue every time.

For example, there are arrays:[5, 3, 1, 2, 4], then it behaves as follows in insertion sorting:

  1. 5, there are no other numbers in front, so there is no need to insert
  2. 3, there’s one in front5, and3 < 5So insert it into the previous number and it becomes[3, 5, 1, 2, 4]
  3. 1, ahead[3, 5]So, step by step, insert it into the first bit, and it becomes[1, 3, 5, 2, 4]
  4. 2, ahead[1, 3, 5], becomes after insertion[1, 2, 3, 5, 4]
  5. 4, and finally output as[1, 2, 3, 4, 5]

Shivering is useless. Look at the code:

const twoSort = (arr) => {
  const length = arr.length;

  for (let i = 1; i < length; i++) {
    const currentNumber = arr[i];

    let j = i - 1;

    while (j >= 0 && currentNumber < arr[j]) {
      arr[j + 1] = arr[j];
      j--;
    }

    arr[j + 1] = currentNumber;
  }

  return arr;
};

console.log(twoSort([7, 3, 2, 4, 6, 5, 1])); // [ 1, 2, 3, 4, 5, 6, 7 ]

Return to directory

Quick sort is the quick sort often asked in interviews.

On average, sortnItems toO(nLogn)Second comparison; In the worst case, you needO(n^2)Comparison.

Quick sorting requires about 3 steps:

  1. Select the element as the base point
  2. Sort the array. Those smaller than the benchmark value are placed on the left, those larger than the benchmark value are placed on the right, and the benchmark value is in the middle
  3. Repeat steps 1 and 2 recursively

9.1 method 1: basic ideas

Return to directory

The following method provides an idea, but it is recommended not to answer in this way.

  1. If there is less than one left in the array, the array is returned
  2. If the array has 2 or more, set the middle pointmid
  3. adoptforEachTraversal, will be less than the middle pointmidPut it on the leftleft, greater than the middle pointmidPut it on the rightright
  4. Returns the reorganized array[...quickSort(left), mid, ...quickSort(right)]
const quickSort = (arr) => {
  if (arr.length <= 1) {
    return arr;
  }
  const midIndex = Math.floor(arr.length / 2);
  const mid = arr.splice(midIndex, 1)[0];
  const left = [];
  const right = [];
  arr.forEach(item => item < mid ? left.push(item) : right.push(item));
  return [...quickSort(left), mid, ...quickSort(right)];
};

console.log(quickSort([7, 1, 3, 2, 5, 4, 7, 6, 1])); // [ 1, 1, 2, 3, 4, 5, 6, 7, 7 ]

Return to directory

The following fast platoon belongs to a simpler fast platoon.

  1. Set left and right boundariesleft = 0right = arr.length - 1
  2. Take the number on the right as the base every time
  3. Those less than the cardinality are placed on the left
  4. Places greater than the cardinality to the right
  5. arr[pos]The position is the number arranged this time
  6. recursionquickSort(arr, left, pos - 1)quickSort(arr, pos + 1, right)
const quickSort = (arr, left = 0, right = arr.length - 1) => {
  if (left < right) {
    let pos = left - 1;

    const rightVal = arr[right];

    for (let i = left; i <= right; i++) {
      if (arr[i] <= rightVal) {
        pos++;

        [arr[i], arr[pos]] = [arr[pos], arr[i]];
      }
    }

    quickSort(arr, left, pos - 1);
    quickSort(arr, pos + 1, right);
  }

  return arr;
};

console.log(quickSort([7, 1, 3, 2, 5, 4, 7, 6, 1])); // [1, 1, 2, 3, 4, 5, 6, 7, 7]

This fast platoon lacks two considerations:

  1. An ordered array. If the current array is already ordered, there is no need for further recursion.
  2. Large amount of duplicate data. If there are many duplicate data in the current array, it is difficult to ensure the array balance of recursion twice.

Return to directory

Three way quick sort is an optimized version of quick sort. It divides the array into three segments, that is, less than, equal to and greater than the reference element. In this way, it can deal with the same elements in the array more efficiently, and other features are basically the same as quick sort.

const quickSort = (arr, left = 0, right = arr.length - 1) => {
  if (left < right) {

    let leftPos = left - 1;

    let middlePos = 0;

    const compareValue = arr[right];

    for (let i = left; i <= right; i++) {
      if (arr[i] <= compareValue) {
        leftPos++;
        [arr[i], arr[leftPos]] = [arr[leftPos], arr[i]];
        if (arr[i] === compareValue) {
          middlePos++;
        }
      }
    }

    quickSort(arr, 0, leftPos - 1);
    quickSort(arr, leftPos + middlePos, right);
  }

  return arr;
}

console.log(quickSort([7, 2, 3, 3, 2, 4, 5, 4, 7, 6, 5, 1])); // [ 1, 2, 3, 4, 5, 6, 7 ]

Return to directory

Merging sorting is similar to fast sorting in that it is recursive divide and conquer. The difference is that fast sorting is sorted while partitioning, while merging is sorted only after partitioning is completed.

The idea of divide and conquer is to decompose the big problem into small sub problems to solve. After the sub problems are solved, the big problem will be solved.

const mergeSort = (arr) => {
  if (arr.length <= 1) {
    return arr;
  }

  const midIndex = Math.floor(arr.length / 2);
  const left = arr.slice(0, midIndex);
  const right = arr.splice(midIndex, arr.length);

  return merge(mergeSort(left), mergeSort(right));
};

const merge = (left, right) => {
  const result = [];

  while (left.length && right.length) {
    if (left[0] < right[0]) {
      result.push(left.shift());
    } else {
      result.push(right.shift());
    }
  }

  while (left.length) {
    result.push(left.shift());
  }

  while (right.length) {
    result.push(right.shift());
  }

  return result;
};

console.log(mergeSort([3, 1, 4, 2])); // [1, 2, 3, 4]

Return to directory

sort Time complexity (good) Time complexity (bad) Spatial complexity stability
Bubble sorting O(n^2) O(n) O(1) stable
Select sort O(n^2) O(n^2) O(1) instable
Insert sort O(n^2) O(n) O(1) stable
Quick sort O(nlogn) O(n^2) O(logn)~O(n) instable
Merge sort O(nlogn) O(nlogn) O(n) stable
Heap sort O(nlogn) O(nlogn) O(1) instable

From the table, we can see that fast scheduling has no great advantage in terms of time complexity.

However, why does fast scheduling become the most commonly used sorting method? This is because the time complexity can only explain the increasing trend of algorithm time cost with the increase of data volume, which does not directly represent the actual execution time. The actual running time also includes the difference of many constant parameters.

In addition, when facing different types of data (such as ordered data and a large number of duplicate data), the performance is also different. In general, the time efficiency of fast scheduling is the highest.

In practice, not only one sort method is used, such as V8Array.sort()When you taken<=10When, useInsert sort, whenn>10The sorting strategy of three-way fast scheduling is adopted.

Twelve search

Return to directory

In the algorithm and data structure, find a number quickly, accurately and ruthlessly.

12.1 sequential traversal

Return to directory

This is the most common search method, with time complexityO(n), that is, you need to traverse the entire array at most.

const search = (arr, target) => {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === target) {
      return i;
    }
  }
  return -1;
}

console.log(search([1, 3, 2, 5, 4, 7, 6], 7));

Return to directory

Compared with sequential traversalO(n)Complexity, we added an additional pointer throughleft = 0, right = arr.length - 1In this way, the left and right pointers keep moving to the middle, so as to find the element faster.

Compared with sequential traversal, the search speed at this time is * 2.

const doubleSearch = (arr, target) => {
  for (let i = 0, j = arr.length - 1; i <= j; i++, j--) {
    if (arr[i] === target) {
      return i;
    } else if (arr[j] === target) {
      return j;
    }
  }
  return -1;
}

console.log(doubleSearch([1, 3, 2, 5, 4, 7, 6], 5));

Return to directory

Used to find the ordered array of [sorted].

  1. Left and right:leftandright
  2. Find intermediate elements each timemidMath.floor((left + right) / 2)
  3. Ifarr[min]Is the element to be found, and returnsmidposition
  4. Ifarr[min] > target, then letright = mid - 1
  5. Ifarr[min] < target, then letleft = mid + 1
  6. Cycle steps 2 to 5 untilleft <= rightNot established
const binarySearch = (arr, target) => {
  let left = 0;
  let right = arr.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) {
      return mid;
    } else if (arr[mid] > target) {
      right = mid - 1;
    } else if (arr[mid] < target) {
      left = mid + 1;
    }
  }

  return -1;
};

console.log(binarySearch([0, 1, 2, 3, 4, 5, 6], 1));
console.log(binarySearch([0, 1, 2, 3, 4, 5], 0));

Return to directory

There are 14 references in this series.

Brush questions:

study:

Title:


Jsliang’s document library is provided byLiang JunronguseKnowledge sharing Attribution – non-commercial use – sharing in the same way 4.0 international license agreementLicense< Br / > based onhttps://github.com/LiangJunrong/document-libraryCreation of works on< Br / > use rights other than those authorized by this license agreement can be obtained fromhttps://creativecommons.org/licenses/by-nc-sa/2.5/cn/Obtained from.