A: hi! ~ Hello everyone, I am YK bacteria 🐷, a microsystem front-end ✨, love to think, love to summarize, love to record, love to share 🏹, welcome to follow me 😘 ~ [wechat account: Yk2012Yk2012, wechat public account: ykyk2012]

“This is the 29th day of my participation in the Gwen Challenge in November. See details of the event: The Last Gwen Challenge in 2021”

In fact, I wrote this problem in my official account three years ago: divide and conquer to solve the largest subcolumn and problem — and the fastest and fastest to solve the largest subcolumn and problem — online processing at that time was still using C language, today we use JavaScript to explore this problem again. Our real purpose is through this problem, learn some algorithm ideas, learn some methods and skills to solve problems!

53. Maximum suborder sum

Leetcode-cn.com/problems/ma…

Given an integer array nums, find a contiguous subarray with the maximum sum (the subarray contains at least one element) and return the maximum sum.

① Preliminary study: peace by violence

We can think of the simplest and most violent way to do this, which is to sum up all the subcolumns, and then compare them one by one, and take the largest subcolumn sum.

/ * * *@param {number[]} nums
 * @return {number}* /
var maxSubArray = function (nums) {
  let thisSum = -Infinity;
  let maxSum = -Infinity;

  for (let i = 0; i < nums.length; i++) {
    for (let j = i; j < nums.length; j++) {
      thisSum = 0; // nums[I] to nums[j
      for (let k = i; k <= j; k++) {
        thisSum += nums[k];
        if (thisSum > maxSum) { // If the subcolumn sum you just got is larger
          maxSum = thisSum; // Update the result}}}}return maxSum;
};
Copy the code

The test was correct

But it was too inefficient to pass all the test cases

There are three cycles, so his time is O(N3)O(N^{3})O(N3).

And if you think about it a little bit, do you really need to sum all of these? Is there a lot of repeated summation, can we optimize it?

② Progress: optimization of summation

Let’s transform the third loop, and the third loop is not really necessary, just add one element to the previous one, and you get a new sum

The improved code

/ * * *@param {number[]} nums
 * @return {number}* /
var maxSubArray = function(nums) {
  let thisSum = -Infinity;
  let maxSum = -Infinity;

  for (let i = 0; i < nums.length; i++) {
    thisSum = 0; // nums[I] to nums[j
    for (let j = i; j < nums.length; j++) {
      thisSum += nums[j];
      // For the same I, different j, just add one term on the basis of the j-1 cycle
      if (thisSum > maxSum) {// If the sum we just got is larger
        maxSum = thisSum; // Update the result}}}return maxSum;

};
Copy the code

As you can see, the time complexity is only O(N2)O(N^{2})O(N2), and of course, that doesn’t run LeetCode

O(N^{2})O(N ^{2})O(N ^ 2), the immediate response is: how do I reduce it to O(NlogN)O(NlogN)O(NlogN)?

③ Divide and rule

The algorithmic idea that we use is — divide-and-conquer 1) divide it into two or more smaller problems; 2) Solve each small problem separately; 3) The solution to the original problem can be obtained by combining the solutions to the small problems.

In this case, what we did was we split the sequence in half, and then we split the sequence in half. Solve a big problem into a lot of small problems to deal with, and then respectively across their boundaries, the problem together.

Let’s take a concrete example. Let’s give you a sequence

So let’s divide it into pairs (take the left side as an example)

The largest subcolumn sum is 4, and everything else is similar

And then across the middle line

The largest subcolumn sum on the left is 6, and the largest subcolumn sum on the right is similar

And then finally you get the largest subcolumn sum in the middle

The largest subcolumn sum is 11

The encoding is as follows

/ * * *@param {number[]} nums
 * @return {number}* /
function maxSubArray(nums) {
    let len = nums.length

    if(len === 0) {return 0
    }

    return maxSubArraySum(nums, 0, len - 1)};// Computes the maximum sum of intersecting suborders
function maxCrossingSum(nums, left, mid, right){
    let sum = 0
    let leftSum = -Infinity
    for(let i = mid; i >= left; i--){
        sum += nums[i]
        if(sum > leftSum){
            leftSum = sum
        }
    }

    sum = 0
    let rightSum = -Infinity
    for(let i = mid + 1; i <= right; i++){
        sum += nums[i]
        if(sum > rightSum){
            rightSum = sum
        }
    }
    return (leftSum + rightSum)
}

// Calculate the maximum suborder and function
function maxSubArraySum(nums, left, right){

    if(left === right){
        return nums[left]
    }

    let mid = Math.floor((left + right)/2)

    let leftSum = maxSubArraySum(nums, left, mid)
    let rightSum = maxSubArraySum(nums, mid+1, right)
    let crossSum = maxCrossingSum(nums, left, mid, right)

    return max3(leftSum, rightSum, crossSum)
}

// Returns the maximum of three numbers
function max3(num1, num2, num3){
    return Math.max(num1, Math.max(num2, num3))
}
Copy the code

I finally got out, but I still can’t do it efficiently

Divide-and-conquer optimization: line tree

So let’s optimize, divide and conquer and we can solve for the longest common ascending subsequence with a line segment tree, so let’s just look at the code

function Status(lSum, rSum, mSum, iSum) {
    this.lSum = lSum; // lSum represents the largest sum of subsegments within [l,r] whose left endpoint is L
    this.rSum = rSum; // rSum represents the sum of the largest subsegments in [l,r] with r as the right endpoint
    this.mSum = mSum; // mSum represents the largest sum of subsegments in [l,r]
    this.iSum = iSum; // iSum represents the interval sum of [l,r]
}

function pushUp(lSub, rSub){

    // iSum is the sum of left subinterval + right subinterval
    const iSum = lSub.iSum + rSub.iSum;
    
    // lSum is the lSum and the left interval of the left subinterval
    const lSum = Math.max(lSub.lSum, lSub.iSum + rSub.lSum);
    
    // rSum represents the sum of the largest subsegments in [l,r] with r as the right endpoint
    const rSum = Math.max(rSub.rSum, rSub.iSum + lSub.rSum);
    
    // mSum represents the largest sum of subsegments in [l,r]
    const mSum = Math.max(Math.max(lSub.mSum, rSub.mSum), lSub.rSum + rSub.lSum);
    
    return new Status(lSum, rSum, mSum, iSum);
}

// Query the maximum sum of subsegments in a sequence [l,r]
function getInfo(a, l, r){
    // Recursive termination condition
    if (l === r) {
        return new Status(a[l], a[l], a[l], a[l]);
    }
    
    // The following is the process of "dividing"
    // Get the median value
    const m = (l + r) >> 1;
    // Divide and conquer: left
    const lSub = getInfo(a, l, m);
    // Divide and conquer
    const rSub = getInfo(a, m + 1, r);
    
    return pushUp(lSub, rSub);
}

var maxSubArray = function(nums) {
    return getInfo(nums, 0, nums.length - 1).mSum;
};
Copy the code

The time complexity here is O(N)O(N)O(N) space complexity O(logN)O(logN)O(logN) O(logN)

⑤ Online processing

ThisSum maintains a right-summing subsum. If the sum of the preceding and subsumes is not as large as the i-th element, a new summing subsum is maintained starting from I

MaxSum holds the maximum sum of suborders in traversal

/ * * *@param {number[]} nums
 * @return {number}* /
var maxSubArray = function(nums) {
    // The sum of the current suborder
    let thisSum = 0;
    // Maximum suborder sum
    let maxSum = nums[0];
    
    // Walk through the sequence
    for(let i = 0; i < nums.length; i++){
    
        if(thisSum + nums[i] < nums[i]){
            thisSum = nums[i];
        }else{
            thisSum += nums[i];
        }
        
        if(thisSum > maxSum){ maxSum = thisSum; }}return maxSum;
};
Copy the code

Simplify your code

/ * * *@param {number[]} nums
 * @return {number}* /
var maxSubArray = function(nums) {
    let thisSum = 0;
    let maxSum = nums[0];
    
    for(let i = 0; i< nums.length; i++){
        thisSum = Math.max(thisSum + nums[i], nums[i]);
        maxSum = Math.max(maxSum, thisSum);
    }
    
    return maxSum;
};
Copy the code

The time is O(N)O(N)O(N), the space is O(1)O(1)O(1) O(1)