【问题标题】:Is there a way to improve this code in order to avoid a timeout with large arrays?有没有办法改进此代码以避免大型数组超时?
【发布时间】:2019-03-22 19:04:37
【问题描述】:

我正在解决这个问题:https://www.hackerrank.com/challenges/fraudulent-activity-notifications/

我的代码几乎可以正常工作,但对于某些测试用例,它会失败,因为数组很大(超过 200000 个项目)。我花了几个小时试图了解我可以做些什么来提高速度,但我无法提出一个可行的解决方案,所以我的 2 个测试总是因超时而失败,我对通过这个测试感到沮丧。 我想我无法避免第一个循环以及排序中的循环,但想不出更快的方法。

网站描述的问题是这样的:

HackerLand National Bank 制定了一项简单的政策来警告客户可能存在的欺诈性帐户活动。如果客户在某一天的消费金额大于或等于客户过去几天的平均支出,他们会向客户发送有关潜在欺诈的通知。银行不会向客户发送任何通知,直到他们至少拥有前几天的交易数据。

我用这段代码解决了它

function getMedianNumber(arr) {
  arr.sort((a, b) => a - b);

  let medianNumber = 0;
  const middle = Math.floor(arr.length / 2);

  if (arr.length % 2 === 0) {
    // Is even we get the median number
    medianNumber = (arr[middle] + arr[middle - 1]) / 2;
  } else {
    const index = Math.floor(middle);
    medianNumber = arr[index];
  }

  return medianNumber;
}

function activityNotifications(expenditure, d) {
  let notifications = 0;
  let len = expenditure.length - 1;

  for (let i = len; i > d - 1; i--) {
    let trailingDays = expenditure.slice(i - d, i);
    let dayExpense = expenditure[i];
    let median = getMedianNumber(trailingDays);

    if (expenditure[i] >= median * 2) {
      notifications++;
    }
  }

  return notifications;
}

它只在 2 个测试用例中失败,因为传递的数组很大,我收到超时错误。

【问题讨论】:

  • 计算中位数类似于计算滚动和的方式。见en.wikipedia.org/wiki/Moving_average
  • @berig:你能做到吗?三思而后行。
  • 你可以做一个滚动中位数,但它比滚动总和更棘手:stackoverflow.com/a/5970314/10396.
  • @YvesDaoust 是的。当然,添加到窗口,从窗口中删除并计算新的中位数并不像+-id 那样简单,但它是可行的。即使你这样做效率相对较低,例如使用窗口的排序数组,这仍然比像 OP 目前正在做的那样对自己的每个切片进行排序要好得多。
  • @Bergi:我想强调的是,不可能像滚动总和那样在恒定时间内实现滚动中位数。不过,恒定时间滚动最小值是可能的。

标签: javascript algorithm sorting


【解决方案1】:

expenditure.slice(i - d, i); 太贵了,您通过在每次迭代中复制数组元素来使其成为 O(n^2)。使用原始数组上的索引来计算中位数:getMedianNumber(arr, startIndex, endIndex)

【讨论】:

  • 但是 OP 将无法做到 arr.sort((a, b) => a - b)。您将如何找到中位数?
  • @Bergi 通过保持支出计数,MAX_EXPENDITURE 刚好达到 200。他可以在 O(200) 内得到中位数
  • 我以为排序功能变慢了,你能解释一下为什么 MAX_EXPENDITURE 只达到 200 吗?那是什么意思?谢谢
  • @KaiserSoze 其问题描述中,数组支出中的数字都在0到200之间
  • 谢谢@juvian,这是我仍然需要学习的内容,如何真正阅读和理解这些问题。我从不看约束,因为我不理解它们,但至少现在,我明白它们的重要性:)
【解决方案2】:
function copy(a, ind) {
    b = [];
    for(var i = ind; i < a.length; ++i) {
        b.push(a[i]);
    }
    return b;
}
function processData(input) {
    //Enter your code here
    var inputArr = input.split("\n");
    var d = parseInt(inputArr[0].split(" ")[1]);
    var arr = inputArr[1].split(" ").map(Number);
    var countArray = [], sortedArray = [], tempArray = [], notifications = 0, median, count, middle;
    for(var i = 0; i <= 200; ++i) {
        countArray.push(0);
    }
    for(i = 0; i < d; ++i) {
        countArray[arr[i]]++;
    }
    for(var j = 0; i < arr.length; ++i, ++j) {
        tempArray = [], count = 0;
        for(var k = 0; k <= 200; ++k) {
            if(countArray[k] > 0) {
                count += countArray[k];
                tempArray.push({
                    no: k,
                    count: count
                });
            }
        }
        middle = {};
        if((d&1) === 0) {
            middle.index = count / 2;
        } else {
            middle.index = Math.ceil(count / 2);
        }
        var tempCount = 0;
        for(k = 0; k < tempArray.length; ++k) {
            if(tempArray[k].count === middle.index) {
                if((d&1) === 0) {
                    median = (tempArray[k].no + tempArray[k + 1].no) / 2;
                    break;
                } else {
                    median = tempArray[k].no;
                    break;
                }
            } else if(tempArray[k].count > middle.index) {
                median = tempArray[k].no;
                break;
            }
        }

        //console.log(tempArray, median, arr[i]);
        if(arr[i] >= (2 * median)) {
            notifications++;
        }
        countArray[arr[i]]++;
        countArray[arr[j]]--;
    }
    console.log(notifications);
} 

process.stdin.resume();
process.stdin.setEncoding("ascii");
_input = "";
process.stdin.on("data", function (input) {
    _input += input;
});

process.stdin.on("end", function () {
   processData(_input);
});

【讨论】:

  • 你能解释一下这段代码是如何工作的,它比原始代码更好吗? (顺便说一句,这次我没有投反对票 - 答案似乎很好,除了缺乏解释)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多