让 for 循环或 .forEach 方法删除当前数组元素,然后让流程从同一索引继续?

Have for loop or a .forEach method remove the current array element, then have flow continue from from the same index?

我正在从 coderbytes 解决这个问题:

使用 JavaScript 语言,让函数 SecondGreatLow(arr) 分别获取存储在 arr 中的数字数组和 return 第二小数和第二大数,由 space 分隔。例如:如果 arr 包含 [7, 7, 12, 98, 106],则输出应为 12 98。数组不会为空,并且至少包含 2 个数字。如果只有两个数字,它会变得棘手!

我的解决方案的工作原理是从数组中删除最大值和最小值,然后使用数学方法 return 第二大值和第二小值。

但是,当数组的最大或最小元素有两个或多个实例,并且它们的索引位置彼此相邻时,我相信只有这个值的第一个实例被删除并且流程跳过第二个.

有没有办法让循环运行通过相同的索引值两次以处理相邻的最大值或最小值?

这是我测试过的解决方案的两次迭代。我最初尝试使用 .forEach,第二次尝试使用 for 循环。我遇到了 console.logged 代码有效的情况并且不是每次尝试都如此。

我对这一切真的很陌生,我在空闲时间学习了将近一个月的时间,所以非常感谢您解释自己,就好像我真的很笨一样。谢谢!!!


// * First attempt - using .forEach method *

// outputs the second lowest value in the array
function secondLowest (arr) {
    var g = function () {
        return Math.min.apply(null, arr);
    }
    arr.forEach(function (val, indx, arr) {
        if (val === g()) {
            arr.splice(indx, 1);
        }
    });
    lowestVal = g(); // store this value to be added back in for the secondGreatest function (in case there were only two digits in the arr argument)
    return Math.min.apply(null, arr);   
}


// number trimmed from the array in the function secondLowest.. 
// to be added back in for the function secondGreatest
var lowestVal = 0


// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
    arr.splice(0,0,lowestVal);
    var g = function () {
        return Math.max.apply(null, arr);
    }
    arr.forEach(function (val, indx, arr) {
        if (val === g()) {
            arr.splice(indx, 1);
        }
    });
    return Math.max.apply(null, arr);
}


// putting together the output
function SecondGreatLow (arr) {
    return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));

// * Second attempt - using for loops *

// outputs the second lowest value in the array
function secondLowest (arr) {
    var g = function () {
        return Math.min.apply(null, arr);
    }
    lowestVal = g();
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === g()) {
            arr.splice(i, 1);

        }
    }
    return Math.min.apply(null, arr);   
}


// number trimmed from the array in the function secondLowest.. 
// to be added back in for the function secondGreatest
var lowestVal = 0


// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
    arr.splice(0,0,lowestVal);
    var g = function () {
        return Math.max.apply(null, arr);
    }
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === g()) {
            arr.splice(i, 1);
        }
    }
    return Math.max.apply(null, arr);
}


// putting together the output
function SecondGreatLow (arr) {
    return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));

我尝试使用 delete 运算符以保持参数数组长度一致(而不是用 splice 缩短它,我认为这允许相邻值传递到删除元素的索引位置而不是在下一个 运行通过 for 循环或 forEach 方法)但是 Math.min/max.apply 方法不喜欢在数组参数中包含 'undefined'。

此外,如果我的代码看起来 ugly/annoying 并且让您感到畏缩,那么请借此机会发泄.. 帮助我学习编写不会激怒别人的代码 ;)


** 找到解决方案 ** 谢谢你提醒我排序方法!(功能?)这是我最后得到的:

function SecondGreatLow (arr) {
    var secondLow = 0,
        secondHigh = 0;
    arr.sort(function(a,b){
        return a-b;
    });
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            secondLow = arr[i];
            break;
        }
    }
    for (var j = (arr.length-2); j >= 0; j--) {
        if (arr[j] !== arr[j+1]) {
            secondHigh = arr[j];
            break;
        }
    }
    return secondLow + " " + secondHigh;
}
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));

多么棒的社区。我会带着更多的问题回来,希望我有足够的信心在不久的将来回答一些问题。谢谢!

我觉得我可能遗漏了什么,但挑战似乎不包括从原始数组中删除项目的要求,所以我不明白你为什么要以这种方式修改它.您提供的要求只是说明 return 'a b' 其中 a 是第二低的,b 是第二高的。

所以,我首先建议对列表进行排序。由于您知道您在上限和下限工作,因此您不必迭代任何内容(您也不应该)。您的测试数组已经排序,但确保顺序将使您的代码更健壮并能够处理其他输入。查看 Arrays API 了解更多详情。

虽然它似乎超出了您的问题范围,但您可能还想研究排序算法以了解更多有关其工作原理的信息,而不是仅仅依赖 API。

排序后,您应该能够轻松地从边界向内比较以获得第二低和第二高的值。

此外,您不需要使用数学 API,简单的不等式运算符(< 和 >)就可以解决问题。

编辑:虽然我建议您自己解决问题,但这里有一个简单的问题解决方案。我把它放在这里,所以如果你遇到困难,你可以参考这个(和相关的评论)作为指导。

function SecondGreatLow(arr) {
  var i;
  var j;
  var lowest;
  var highest;
  var secondLowest;
  var secondHighest;

  //Sort Array
  arr.sort(function (a, b) {
    return a - b;
  });

  //Get Bounds
  //Since we sorted the array, and the default sort is in 
  //ascending lexicographical order, then we're guaranteed that 
  //our 'lowest' value is at index 0 and our 'highest' value is
  //at index arr.length -1.  Note that these values may be
  //equal.
  lowest = arr[0];
  highest = arr[arr.length - 1];

  //Search for second lowest.
  for (i = 0; i < arr.length; i++) {
    if (arr[i] > lowest) {
      secondLowest = arr[i];
      break;
    }
  }

  //If we reach the end of the array, but didn't 
  //find a greater value, then, since the array is sorted,
  //we're guaranteed that all values in the array are equal.
  //Therefore, the required value comparisons have no meaning,
  //and we return 'undefined'.
  if (secondLowest === 'undefined') {
    return 'undefined';    
  }

  //Search for second highest, working backwards from the 
  //high end of the array until we reach our crossover point 
  //with the previous search.  Either some value > arr[i] is the
  //second highest, or arr[i] is, so there's no point in looking 
  //at values in the indices lower than i.
  for (j = arr.length - 1; j >= i; j--) {
    if (arr[j] < highest) {
      secondHighest = arr[j];
      break;
    }
  }

  return secondLowest + ' ' + secondHighest;

}

var result = SecondGreatLow([3,3,4,5,4,6]);
console.log(result);

JSFiddle

您可以创建一个由 2 个元素限制的优先级队列,然后将所有数组提供给它并弹出值,这就是答案。

简单的实现如下所示:

function UniqueNElementSortedQueue(length, comparison) {
    this.length = length;
    this.data = [];
    this.comparison = comparison;
}

UniqueNElementSortedQueue.prototype.push = function(v) {
    if (this.data.indexOf(v) > -1) {
        return;
    }

    this.data.push(v);
    this.data.sort(this.comparison);
    this.data.length = this.length;
};

UniqueNElementSortedQueue.prototype.popIfN = function() {
    if (this.data.length == this.length) {
        return this.data[this.length - 1];
    }
};

JSFiddle:http://jsfiddle.net/fmfv67xy/

解决方案是O(N)(有人可能会争辩说我在内部进行了排序,他们是对的:-))按操作次数和O(N)按附加内存(其中N 与 "next-lowest/greatest" 指数值呈线性关系)

由于描述没有定义如果没有提供足够的数据要return什么 - 我的实现 returns undefined.

实际上,让我把我的评论变成一个答案,因为我认为同时担心性能总是有帮助的:

  • 创建4个局部变量:
    • 最大second_largest 初始化为一个小于您在数组中期望的任何数字,或者初始化为您的数据类型可以采用的最小可能值 (-2^31 - 1)
    • 最小second_smallest 初始化为一个比你在数组中期望的任何数字都大的数字,或者你的数据类型的最大可能值 (2^31)
  • 循环你的数组一次:
    • 如果找到大于largest的数,则设置second_largest 最大最大到那个数字
    • 如果你发现小于最大但大于second_largest的东西,设置second_largest为那个数字
    • 如果找到一个小于smallest的数字,设置second_smallest 最小最小到那个数字
    • 如果你发现比最小大但小于second_smallest的东西,设置second_smallest为那个数字
  • 完成循环后,您的答案将包含在 second_largestsecond_smallest

考虑到您的阵列看起来有多小,您可能不会注意到此答案与其他建议的答案之间的性能差异,但我认为养成始终将此问题放在后面的好习惯你写的每一行代码都需要你的头脑。在这个答案中,您只处理每个数组元素一次(例如,算法在 O(n)), whereas adding a sorting step leads to every element being processed multiple times in a general case (the best sorting algorithms (Timsort 中运行)的预期运行时间为 O(n log n)).

需要注意的一件事:

@elclanrs在他的评论([1, 1])中提到了一个特例,根据问题的定义没有定义解(多个1都算最大数,所以有没有第二大)。在这种情况下,上面的算法仍然会有 second_largestsecond_smallest设置为初始值。