从具有 O(n) 的数组中获取最大的时间顺​​序下降、最小值和最大值

Get the biggest chronological drop, min and max from an array with O(n)

我写了一个 javascript 函数来分析数组中最大的落差。但是还有一个小问题。作为最大值,我总是从我的孔阵列而不是我的下降中获得最大值。

示例: 数组:[100,90,80,120]

最大下降值在 100 到 80 之间。因此最大值必须为 100,最小值必须为 80。我的函数总是 returns 整个数组中的最大值。在我的情况下 120

function checkData(data) {
  let max = 0
  let min = 0
  let drop = 0

  for (let i = 0; i < data.length; i++) {
      if (max < data[i]) {
          max = data[i] //?
      } else {
          let tempDrop = max - data[i]
          drop = Math.max(tempDrop, drop)
          min = max - drop
      }
  }
  return [max, min, drop]
}

我想得到从左到右按时间顺序正确的最大增量

这完全符合预期(按照您的预期输出),如以下代码片段所示。

问题一定在于您如何将数据作为数组发送。

alert(checkData([100, 90, 80, 120]));

function checkData(data) {
  let max = 0
  let min = 0
  let drop = 0

  for (let i = 0; i < data.length; i++) {
    if (max < data[i]) {
      max = data[i] //?
    } else {
      let tempDrop = max - data[i]
      drop = Math.max(tempDrop)
      min = max - drop
    }
  }
  return [max, min, drop]
}

// Output : 120, 80, 20

但是,如果您要查找的是最大值、最小值,然后是最大值和最小值之间的最大差异,在您给出的示例中为 40,那么您可以简单地执行此操作。

alert(checkData([100, 90, 80, 120]));

function checkData(data) {

  let max = data.reduce(function(a, b) {
    return Math.max(a, b);
  });
  let min = data.reduce(function(a, b) {
    return Math.min(a, b);
  });

  let drop = max-min;

  return [max, min, drop];
  
}

// Output : 120, 80, 40

我只提到这个,因为我不知道为什么你说你期望最大的差异是 20,给定 100 和 80,当你在同一个数组中有 120 和 80 时,这意味着最大的差异是40.

您需要跟踪两件事:

  • 一个元素的最大值 i
  • 从元素 i 到列表末尾的最小值。

然后您只需要找出每个指数的跌幅,select哪个指数最大。

代码如下:

function maxDrop (data) {
  if (!data || data.length === 0) {
    return;
  }
  const max = [];
  const min = [];
  for (let i = 0; i < data.length; i++) {
    const left = data.slice(0, i);
    const right = data.slice(i, data.length);
    max.push(Math.max.apply(null, left));
    min.push(Math.min.apply(null, right));
  }
  let maxDrop = max[0] - min[0];
  let maxValue = max[0];
  let minValue = min[0];
  for (let j = 0; j < data.length; j++) {
    const drop = max[j] - min[j];
    if (maxDrop < drop) {
      maxDrop = drop;
      maxValue = max[j];
      minValue = min[j];
    }
  }
  return [maxValue, minValue, maxDrop];
}

console.log(maxDrop([100,90,80,120]))
console.log(maxDrop([100,110,120,300,200,70,60,50,90,400]))
console.log(maxDrop([100,90,80,120,30]))

您需要迭代数组一次 (O(n)) 并保持 运行 的最小值和最大值计数,并且:

  1. 每次值增加时重置它 w.r.t。之前的值
  2. 但如果差异大于之前记录的值,请记下它们

function checkData(array) {
  var currentmax = array[0];
  var currentmin = array[0];
  var overallmax = array[0];
  var overallmin = array[0];
  var i;
  for (i = 1; i < array.length; i++) {
    if (array[i] <= array[i - 1]) {
      // value is same or decreased
      currentmin = array[i];
    } else {
      // check if previous iteration was the largest
      if (currentmax - currentmin > overallmax - overallmin) {
        overallmax = currentmax;
        overallmin = currentmin;
      }
      // restart
      currentmax = array[i];
      currentmin = array[i];
    }
  }
  // check if last iteration was the largest
  if (currentmax - currentmin > overallmax - overallmin) {
    overallmax = currentmax;
    overallmin = currentmin;
  }

  return [overallmax, overallmin, overallmax - overallmin];
}
console.log(checkData([100, 90, 80, 120]));
console.log(checkData([100, 90, 80, 120, 200, 100, 90]));
console.log(checkData([10, 100, 50, 50, 10, 100, 50, 50, 1]));

听起来您希望 returned 最大值和最小值与计算最大跌幅时使用的值相同,而不是总体最大值和最小值。在这种情况下,只需添加一个额外的变量来存储更新 drop 时的变量。替换

drop = Math.max(tempDrop, drop)

使用 if 语句(伪代码):

if current_drop is greater than stored_drop:
  stored_drop = current_drop
  stored_max_min = current max and min

然后 return 额外存储的最大值和最小值。

你的循环应该跟踪当前的下降并将它与之前最大的下降进行比较。您可以通过跟踪索引来做到这一点:

function checkData(data) {
  let bestDropStart = 0
  let bestDropEnd = 0
  let bestDrop = 0
  let currentDropStart = 0
  let currentDropEnd = 0
  let currentDrop = 0
  for (let i = 1; i < data.length; i++) {
    if (data[i] < data[i - 1]) {
      // we are dropping
      currentDropEnd = i
      currentDrop = data[currentDropStart] - data[i]
    } else {
      // the current drop ended; check if it's better
      if (currentDrop > bestDrop) {
        bestDrop = currentDrop
        bestDropStart = currentDropStart
        bestDropEnd = currentDropEnd
      }
      // start a new drop
      currentDropStart = currentDropEnd = i
      currentDrop = 0
    }
  }
  // check for a best drop at end of data
  if (currentDrop > bestDrop) {
    bestDrop = currentDrop
    bestDropStart = currentDropStart
    bestDropEnd = currentDropEnd
  }
  // return the best drop data
  return [data[bestDropStart], data[bestDropEnd], bestDrop]
}

console.log(checkData([100, 90, 80, 120]))
console.log(checkData([100, 90, 80, 120, 30]))
console.log(checkData([70, 100, 90, 80]))
console.log(checkData([100, 90, 80, 120, 30, 50]))

您也可以只保留当前和最佳下降的开始值和结束值,但我更喜欢明确跟踪索引。这样对我来说似乎更清楚(更容易调试和维护)。

要再添加一个.. 一种可能的方法是先创建所有下降范围,然后再创建下降幅度最大的范围

减少版本:

function checkData(data) {
let dropInd = 1,
   mx = data.reduce((arr,val, ind) => {
 if(ind && val > data[ind-1])dropInd++;         
 arr[dropInd] = arr[dropInd] || {max:val};
 arr[dropInd].min = val;
 arr[dropInd].drop = arr[dropInd].max - val;
 return arr;
}, []) //after reduce, islands are created based on 'dropInd'  
  .sort((v1,v2)=>v2.drop - v1.drop) //sort by drop descending
  [0]; //the first value now contains the maximum drop  

  return [mx.max,mx.min,mx.drop];
}



console.log(checkData([100, 90, 80, 120]));
console.log(checkData([100, 90, 80, 120, 200, 100 ,90]));
console.log(checkData([200,100, 90, 80, 120,  100 ,90]));
console.log(checkData([200,120,190, 90, 80, 120,  100 ,90]));