【问题标题】:JavaScript: Find the point where maximum intervals overlapJavaScript:找到最大间隔重叠的点
【发布时间】:2021-02-10 06:37:06
【问题描述】:

我的作业需要帮助,我收到了这个问题,但不知道如何找到答案:

考虑一组退休人员,其中任何人在 X 年开始工作并在 Y 年退休。创建一个函数,接收遵循以下结构的数组:[[x1,y1],[x2,y2],...[xn,yn]] 并计算哪一年大多数人都在工作。

考虑:

  • X 值表示人员开始工作的年份(x>0, x<y);
  • Y 值表示此人退休的年份(y>0);
  • 必须将开始年份视为工作年份;
  • 必须将退休年份视为工作年份。

示例:Input [[1960,2005],[1945,2008],[1938,1999],...]

我试图将最低和最高年份进行比较,并逐年计算有多少人在工作,但没有成功。

编辑:我设法将数组转换为一个并对所有工作年份进行排序。现在我需要管理如何找到每年工作的人。我开发的代码如下:

var variety = [[2000,2008],[2003,2009],[2001,2012],[2004,2020],[2003,2021],[1998,2015]], year = [], anos2 = 0, k=0, minYear=0, maxYear=0
function getyear(people){
    for(var lin = 0; lin < people.length; lin++){ //Esse for é para transformar a matriz em um único vetor dinamicamente
        for(var col = 0; col < people.length-(people.length-2); col++){
            year[k] = people[lin][col]
            k++
        }
    }
    year.sort(compareNumbers) //Ordena o vetor dos anos trabalhados
    console.log(year)
    console.log(people)

}
function compareNumbers(a,b){
    return a -b
}
getyear(variety)

【问题讨论】:

  • 请包含您尝试过的代码,因为我们无法提供我们看不到的代码。 SO 不是为我编写代码的服务。

标签: javascript arrays function


【解决方案1】:

从您的问题来看,您似乎很难解决如何解决问题而不是如何编写代码。希望以下见解对您有所帮助。

如何创建一个从 minYear 到 maxYear 且初始值为 0 的整数数组。 每当您有一个用户在 X 年开始工作并在 Y 年退休时,您只需递增数组中每一年的数字。

因此,从 1960 年到 2015 年工作的人在 1960 年到 2015 年的值的数组中将具有值 1。对每个用户重复。

之后,您可以循环数组以查找数组中的最大值和最小值。这也简化了逻辑,因为可能存在多个最大峰值和多个最小峰值。

还有一种替代方法,您可以通过不创建任何数组并从 minYear 到 maxYear 的每一年循环来分配尽可能多的内存。对于那些年中的每一年,您需要循环让所有员工计算该年有多少人在工作。将第一年保存为最小值和最大值,每隔一年保存一次,如果该值高于最高值或低于最低值,只需更新它并将其用作新的峰值。这使用最少的内存,但会使用更多的 CPU。

总有一些替代方法可以提高 CPU 和内存效率。但是对于初学者来说,这两种方法都可以接受。

【讨论】:

  • 感谢帕斯卡的回答。我将寻找该解决方案并稍后发布。真的很感激。
【解决方案2】:

功能:

const getCounts = (arr = []) =>
{
    const years = {};
    let x, y, year;
    arr.forEach( item => {
        x = item[0];
        y = item[1];
        for ( year = x ; year <= y ; ++year )
        {
            if(years[year] === undefined)
            {
                years[year] = 0;
            }
            years[year]++;
        }
    });
    return years;
}

排序:

const output = getCounts([[1960,2005],[1945,2008],[1938,1999]]);

console.log( Object.entries(output).sort( (a,b) => b[1] - a[1] ) );

【讨论】:

  • 好主意,尽管它返回所有年的计数,而不是计算工作人数最多的那些年。此外,您循环间隔的方式很奇怪,为什么不for (let year = x; year &lt;= y; ++year) ...?
【解决方案3】:

以下方法使用this article 中描述的算法。与以前的答案不同,这不会将 all 范围不必要地分解为年份,也不会在每个范围内逐年迭代。相反,它返回同时工作的最大人数,以及雇用该人数的范围数组。如果需要,将范围数组转换为年份数组很简单。

const f = input=>Object.entries(
  // sum up entries/exits by year
  input.reduce(
    (a,[s,e])=>(
      a[s] = (a[s]||0)+1,
      a[e] = (a[e]||0)-1,
      a
    ),{}
  ))
  // convert years back to numbers
  .map(([s,e])=>[1*s,e])
  // sort ascending by year
  .sort(([r],[l])=>r-l)
  .reduce((a,[y,c])=>{
    a.count += c;
    switch (Math
      .sign(a.count - a.r.max)) {
    case 1: // count > max
      // set max; clear ranges
      a.r = {max:a.count,ranges:[]};
      // fall through
    case 0: // count === 0
      // open new range
      a.r.ranges.push([y]);
      break;
    case -1: // count < max
      // close range, if open
      a.r.ranges.push([
        ...a.r.ranges.pop(), y
      ].slice(0,2));
      break;
    }
    return a;
  },{ // initial values
    count:0,
    r:{max:0,ranges:[]}
  })
  // return results object
  .r;

例如:

const input = [
  [1960,2005],
  [1945,2008],
  [1938,1999]
];

console.log(f(input1));

这将导致:

{ max: 3, ranges: [ [ 1960, 1999 ] ] }

表示在 1960 年至 1999 年期间,同时雇用的工人人数最多为 3 人,包括 3 人。

在下面一个更复杂的例子中,为了简洁,我多年来一直使用小整数:

const input = [
  [1,9],
  [1,2],
  [8,9],
  [2,3],
  [5,7],
  [7,9],
  [5,6],
  [8,9],
  [5,9],
  [4,8],
  [1,3]
];

console.log(f(input));

结果是:

{ max: 5, ranges: [ [ 5, 6 ], [ 8, 9 ] ] }

表示同时最多有 5 名工人,并且 5 名工人在两个不重叠的时间间隔内同时受雇:5 到 6 年 8 到 9 年。

如果需要,可以使用以下函数将范围数组分解为平面年份数组:

const explode = ranges=>
  [].concat(
    ...ranges.map(([s,e])=>Array.from(
      {length:e-s+1},(_,i)=>i+s
    ))
  );

例如:

const input = [
  [1,5],
  [6,7],
  [9,9]
];
  
console.log(explode(input));

将导致以下结果:

[ 1, 2, 3, 4, 5, 6, 7, 9 ]

【讨论】:

  • 感谢您的回答,我一定会学习的!
【解决方案4】:

也许你可以这样做;

var years  = [[2000,2008],[2003,2009],[2001,2012],[2004,2020],[2003,2021],[1998,2015]];
    result = years.map(ys => Array.from({length:ys[1]-ys[0]+1}, (_,i) => ys[0]+i))
                  .reduce((p,c) => p.filter(e => c.includes(e)));

console.log(result);

years.map(ys => Array.from({length:ys[1]-ys[0]+1}, (_,i) => ys[0]+i))

部分将您的 [[2000,2008],[2003,2009],[2001,2012],[2004,2020],[2003,2021],[1998,2015]] 数组转换为

[[2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008]
,[2003, 2004, 2005, 2006, 2007, 2008, 2009]
,[2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012]
,[2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020]
,[2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021]
,[1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015]
]

.reduce((p,c) =&gt; p.filter(e =&gt; c.includes(e))) 部分只是reduces by intersecting

这只是一种幼稚但直观的方式。从这个想法发展,您应该能够通过单个.reduce() 阶段来做到这一点。所以它仍然是一个家庭作业。

所以上述解决方案对于给定的数据集是可以的,但是有一个正确的评论。所以我在这里切换到一个有效的代码。它返回一个对象,其中属性是受工作人员影响的年份,值是每年的人数。

var years = [[2000,2008],[2003,2009],[2001,2012],[2004,2020],[2003,2021],[1998,2015]];
    result = years.reduce(function(r,ys) {
                            for (var y = ys[0]; y <= ys[1]; y++){
                              r[y] = r[y] ? r[y]+1 : 1
                            }
                            return r;
                          },{});
console.log(result);
.as-console-wrapper {
max-height: 100% !important
}

【讨论】:

  • 优雅,但实际上似乎不起作用。考虑输入:[[1,9], [1,2], [2,3], [5,7], [5,7], [6,8], [4,8], [1,3]] 这会给你一个空数组作为结果,这显然是不正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-12
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
相关资源
最近更新 更多