JS: Three Algorithms day1

Posted by jkmcgrath on Wed, 02 Mar 2022 18:34:27 +0100

Backtracking, greedy, and dynamic planning ideas are developed by doing a lot of exercises, not in a few days, and the title won't tell you which algorithm to use. Keep working on the problem.

Back-tracking - 46. Full arrangement (medium) 51. Queen N (difficult)

Pre-order traversal code executes at the point in time before entering a node, and post-order traversal code executes at the point in time after leaving a node. Path and Selection are attributes of each node

def backtrack(Route, Selection List):
	if Satisfies the end condition:
        result.add(Route)
        return
	for Choice in Selection List:
	    # Make a choice
	    Remove the selection from the selection list
	    Route.add(Choice)
	    backtrack(Route, Selection List)
	    # Undo Selection
	    Route.remove(Choice)
	    Add this selection to the selection list

A slight variation is made here, instead of explicitly recording the Selection List, the current selection list is derived by recording the elements in the trace with a user array.

JS Syntax - Copy Array
[...path]
Array.from(path)
path.slice()

46 Questions
Remember the simplest framework

var permute = function(nums) {
    let used = new Array(nums.length).fill(false)
    let res = [], track = []
    function backtrack(nums, track, used) {
        if(track.length == nums.length) {
            res.push([...track])
            return
        }
        for(let i=0; i<nums.length; i++) {
            if(used[i]) continue
            track.push(nums[i])
            used[i] = true
            backtrack(nums, track, used)
            track.pop()
            used[i] = false
        }
    }
    backtrack(nums, track, used)
    return res;
};

It's all about changing the way you choose and excluding illegal ones.

Each solution contains a different chess placement scheme for the n-queen problem in which'Q'and'.' They represent the Queen and the empty seat.

The difficulty is to return to the required format. The path to the article is a two-dimensional array. I use one-dimensional instead. Test the JS syntax. Understanding of map methods.

51 code questions

var isValid = (row, col, track) => {
    for(let i=0; i<row; i++) {
        if(track[i]==col || Math.abs(track[i] - col) / (row - i) == 1) return false
    }
    return true
}
function transform(track) {
    let n = track.length
    let board = new Array(n).fill(0).map(() => new Array(n).fill('.'))
    for(let i=0; i<n; i++) board[i][track[i]] = 'Q'
    // Does not change the original array
    return board.map((val) => val.join(""))
}
var solveNQueens = function(n) {
    let res = [], track = []
    function backtrack(track, row) {
        // Not n-1
        if(row == n) {
            res.push(transform(track))
            return
        }
        for(let col = 0; col < n; col++) {
            if(!isValid(row, col, track)) continue
            track.push(col)
            backtrack(track, row+1)
            track.pop()
        }
    }
    backtrack(track, 0)
    return res;
};

Article Summary

In fact, backtracking algorithm is actually what we often call DFS algorithm, which is essentially a kind of violence exhaustion algorithm

When writing the backtrace function, you need to maintain the Path you have traveled and the Selection List you can currently make. When the End Condition is triggered, you enter the Path in the result set.

Sometimes, we don't want all the legal answers, just one answer. What can we do?
Second return, do not continue looking for Selection List
Third return, there is no appropriate next step in the Choice List to tell the previous layer of failure

Greed - 455. Distribute cookies (simple)

The algorithmic transcript does not summarize the greedy algorithm, because greed has no routine, which is common sense derivation and counter-example verification.

The essence of greed is to select the local optimum for each stage to reach the global optimum.
If the simulation is feasible, you can try out the greedy strategy by manually simulating yourself. If not, you may need to plan dynamically.

Previous Tabi horse races were greedy, 870. Advantage shuffle

455 question codes

var findContentChildren = function(g, s) {
    g.sort((a, b) => b - a) // Appetite
    s.sort((a, b) => b - a) // Biscuits
    let res = 0, index = 0 // Cookie Pointer
    for(let i=0; i<g.length && index<s.length; i++) {
        if(g[i] <= s[index]) {
            res++
            index++
        }
    }
    return res
};

376. Swing Sequence () - Greed or Rule



The difference of the entire sequence of instance 2 is (16, -12, 5, 3, 2, -5, 11, -8)

I don't know what the code says. Look at other people's explanations of leetcode

7. Because we can select at most one of the adjacent identical elements, we can ignore the adjacent identical elements.
8. Elements in the sequence that are neither "peaks" nor "valleys" are referred to as "transitional elements". For example, in the sequence [1,2,3,4][1,2,3,4], 22 and 33 are both "transitional elements".

Greedy Algorithm
Once you understand the concept, it looks like you are looking for mathematical extremes. Then it inexplicably becomes a sliding window of size 3.

Error cases:
[0,0]
1
Error cases:
[4, 2, 2, 4] If the left-right difference is updated every time, the two minimum values are ignored. So it's a double pointer, not a sliding window.
Error cases:
[1,1,1,2,2,2,1,1,1,3,3,3,2,2,2]

If rightDiff is not 0, leftDiff will inherit it negatively because I use the middle number to subtract both sides.
If rightDiff is 0, it is going flat, and leftDiff retains the last slope.

The final boundary case prevents the last rightDiff from being 0, so the extreme value is never found, and the rightmost side is ignored. The way to do this is to see if leftDiff has a slope, then the rightmost side is also the extreme value.
Because the leftmost side must count as one, look at the rightmost side.

var wiggleMaxLength = function(nums) {
    if(nums.length == 1) return 1
    let leftDiff = nums[1] - nums[0], rightDiff = 0, res = 0;
    for(let i = 1; i<nums.length - 1; i++) {
        rightDiff = nums[i] - nums[i+1]
        if(leftDiff * rightDiff > 0) {
            res++
        }
        if(rightDiff) leftDiff = -rightDiff
    }
    board = leftDiff ? 2 : 1
    return res + board
};

dynamic programming algorithm
It won't work either. Or understand the concept of mountains and valleys

Interpretation is easy at first glance because dynamic programming is difficult: defining array meanings and state transfer equations.
Initialization: Think of the leftmost as an extreme, either a maximum or a minimum, but not necessarily the rightmost.
Equation of State: You can see that if the value of the previous node is the same, the node will not be processed

var wiggleMaxLength = function(nums) {
    let n = nums.length
    if(n == 1) return 1
    const dp1 = new Array(n).fill(1) // Peak
    const dp2 = new Array(n).fill(1) // Mountain valley
    for(let i=1; i<n; i++) {
        for(let j=0; j<i; j++) {
            if(nums[j] > nums[i]) dp2[i] = Math.max(dp2[i], dp1[j]+1)
            if(nums[j] < nums[i]) dp1[i] = Math.max(dp1[i], dp2[j]+1)
        }
    }
    return Math.max(...dp1, ...dp2)
};

Keeping the maximum values of peaks and valleys makes it unnecessary for j to traverse from 0 to i-1 in the dp transfer equation

This simplification was also tried out. Or it could mean that the nearest mountain to the current Valley must have more nodes.

var wiggleMaxLength = function(nums) {
    let n = nums.length
    if(n == 1) return 1
    let dp1 = 1 // Peak
    let dp2 = 1 // Mountain valley
    for(let i=1; i<n; i++) {
        if(nums[i-1] > nums[i]) dp2 = dp1+1
        if(nums[i-1] < nums[i]) dp1 = dp2+1
    }
    return Math.max(dp1, dp2)
};

Topics: Javascript Algorithm leetcode