Reference for this blog
Js version of Data Structure and Algorithm

Part1 Adapted from “Cartoon Algorithm” original author: Programmer Xiao Grey

preface

As we all know, compared to backend developers,
algorithmIs a weak spot for us front-end developers.

In recent years, with the increasingly fierce competition in the Internet industry, the algorithm requirements for front-end positions in the market have also been improved to a certain extent.

I remember that in my junior year, I only prepared several common sorting algorithms for the interview of Tencent. However, this year, many big companies, including Toutiao, had their front-end written test questions.Greedy algorithm“”Dynamic programming“”Divide and conquer algorithm“And so on. It’s not that easy to deal with this kind of advanced algorithmic problem in the field without preparing in advance.

If you haven’t heard of any of these algorithms and want to get into a big factory, don’t hesitate to learn them before you lose all your hair.

                                         

This blog will be split into two parts

  • Part1: Describe the idea of dynamic planning through cartoons
  • Part2: Practice with a LeetCode question about dynamic programming





Part1: Understanding cartoons


             


           


              


         

Eleven eleven eleven eleven eleven


             


            

    



Topic:



There is a single shelf that can only hold 10 books, and you can only put one or two books at a time. Ask a program to figure out how many ways you can fill a bookshelf.


For example, one book at a time, 10 times, that’s one way to do it. We can abbreviate it as 1,1,1,1,1,1,1,1,1.




For example, if you put two books in five times, that’s another way to do it. We could write it as 2,2,2,2,2.



Of course, there are many, many ways.


              

         

         

             

          

         

Eleven eleven eleven eleven eleven

             

            

           


          

          

            


    


The first:



The second:



 

            


             

        


      

      


 

Here is another example for you to understand:



As shown in the figure, it is assumed that only road1 or Road2 can reach the destination

(Equivalent to we put the last step of the book into two books and one book two cases)

There are x routes to reach Road1 (equivalent to the number F(8) of 0 to 8 copies)

There are y ways to reach Road2 (equivalent to the number F(9) of 0 to 9 copies)

So the probability of getting there is going to be x plus y.


        

             


     

  

          

               


       F(1) = 1;

       F(2) = 2;

F(n) = F(n-1)+F(n-2) (n >=3)


          


         


        


         


          


I believe that you will have a preliminary understanding of dynamic programming,

So you can try to code this problem for yourself

Next, let’s deepen our understanding of dynamic programming by using a LeetCode practice problem


Part2: Actual combat

Different paths ⅱ

LeetCode 63

Questions of medium difficulty

Topic describes

A robot is located in the upper left corner of an M x N grid (the starting point is marked “Start” in the figure below).


The robot can only move one step to the right or down at a time. The robot attempts to reach the lower right corner of the grid (marked “Finish” in the image below).


Now consider that there are obstacles in the grid. So how many different paths are there going to be from the top left to the bottom right?









Obstacles and empty positions in the grid are used separately
1
0To represent.



Description:The value of m and n does not exceed 100.

Example 1

Input:


 [ 


[0, 0].


[0, 0].



[0, 0]




Output: 2


Explanation: There is an obstacle in the middle of the 3×3 grid.


There are two different paths from the top left to the bottom right:


1. Right -> right -> Down -> Down


2. Down -> down -> right -> right

title

As you can see, this problem is almost identical to the one we showed in the cartoon.

But it makes it a little bit more difficult, and we need to take into account the obstacles.

Remember the three elements of dynamic programming [optimal substructure] [boundary] and [state transition formula] we mentioned earlier?

Take the picture given in the question for example:

Without considering obstacles, how many cases can we get to the end using the idea of dynamic programming?

There are obviously two ways: from above the finish line or to the left of the finish line



The 7 by 3 matrix

So we can easily conclude that the end point of this 7 by 3 matrix F of 7 by 3 has optimal substructures F of 6 by 3 and F of 7 by 2.

So far, its state transition formula is clear: F(m*n) = F(m-1*n) + F(m*n-1)

Finally, let’s consider its boundary case:

In fact, the boundary case of F(2*2) we considered before can be divided into one column and one row, namely, F(1*2) + F(2*1).

All F of m by n matrices can be split into one row and one column cases, so we only have two boundary cases here.

  • The first boundary F(1*n) is 1 row and n columns

At this point, if there is any obstacle in the line, it cannot be passed


   



  • The second boundary, F(n*1), is n rows and one column

In this case, any obstacle in the column cannot be passed


     


Code implementation

export default (arr) => {
  let dp = (m, n) = > {
    // Check whether the starting or target element is 1(obstacle). If the starting or last cell is 1, it means that there is no way to get there.
    // Return 0 directly
    if (arr[m - 1][n - 1= = =1 || arr[0] [0= = =1) {
      return 0
    }
    / / a border
    if (m < 2 || n < 2) {
      // The first boundary is 1 row and n columns
      if (m < 2) {
        return arr[m - 1].includes(1)?0 : 1
      } else {
        // Second boundary n row 1 column
        for (let i = 0; i < m; i++) {
          if (arr[i][0= = =1) {
            return 0}}return 1}}else {
      / / recursion
      return dp(m - 1, n) + dp(m, n - 1)}}return dp(arr.length, arr[0].length)
}Copy the code

Added: Time complexity analysis

Problem analysis

Thank you for your questions in the comments section

First of all, the code above was fine, but the time limit was exceeded on test case 27 on LeetCode

This test case is relatively complex, a 33 by 22 two-dimensional matrix

So why is the time complexity of our method too high when the matrix reaches a certain length?

Let’s review our previous thinking:

  • F(10) = F(9) + F(8)             
  • F(9) = F(8) + F(7)      

If I factor F of 9, then F of 10 can be written as F of 10

  •  F(8) + F(8) + F(7)

F of 8 is equal to F of 7 plus F of 6.

So if WE continue to factor F of 8, F of 10 can be written as F of 10

  • F(7) + F(7) +F(7) + F(6) + F(6)

Notice that?

The further down you go, the more repetitions you get, and you might say, well, isn’t that just one more F(n)

But here’s what I have to warn you:

F(n) is not simply a reference to a value, it is a recursive function that repeats F every time we repeat it

So instead of talking about time complexity how do we calculate it

But I can tell you that our previous method was order 2 to the n.

So how to improve?

Put forward the train of thought

Here are two ideas that you can try to write yourself:

  1. Cache each calculated value, that is, record F(9),F(8),F(7)… Each time you recurse to the value of the previously calculated data, you directly take the value without having to call the recursive function again.
  2. Since only the first two data are relied on each time, only the data of the first two times can be saved through two variables. For example, F(3) only depends on F(1) and F(2), and F(6) only depends on F(5) and F(4).

Code optimization

// Pass in a two-dimensional array
arr => {
    / / the number of rows
    let n = arr.length;
    if(! n){return 0;
    }
    / / the number of columns
    let m = arr[0].length;
    // The starting or ending point is an obstacle
    if(arr[0] [0= = =1 || arr[n - 1][m - 1= = =1) {return 0;
    }
    // Record the number of possible paths to each location
    var rode= [];
    // Iterate over each line
    for(let i = 0; i < n; i++){
        rode[i] = [];    // Iterate over each element in each row
        for(let j = 0; j < m; j++){
            // If a node is an obstacle, the number of paths to that node is 0
            if(arr[i][j] === 1){
                rode[i][j] = 0;
            } else if(i === 0) {// If the first row is passed each node depends on its left node
                rode[i][j] = rode[i][j - 1= = =0 ? 0 : 1;
            } else if(j === 0) {// If the first column is used, each node can pass depending on the node above it
                rode[i][j] = rode[i - 1][j] === 0 ? 0 : 1;
            } else {
                // Otherwise recurse
                rode[i][j] = rode[i - 1][j] + rode[i][j - 1]; }}}return rode[n - 1][m - 1];
}Copy the code

reference

  • Programmer Grey – Comics: What is Dynamic Programming?
  • The js version of data structure and algorithm
  • You can also refer to FE_Yuan’s supplement for this blog: front-end interview algorithm – Dynamic programming

conclusion

Did you notice that when you have the three elements of dynamic programming optimal substructure boundary and state transition formula

Finally, it is not difficult to solve the algorithm problem of dynamic programming. But there are ideas that we need to digest well.

I’m sure you’ll be able to solve these problems in the future.