如何在 JavaScript 中获取数组中第二大的元素

32

我有一个整数数组:

arr[20,120,111,215,54,78];

我需要一个接收数组作为参数并返回该数组第二大元素的函数。


9
你尝试过这件事吗? - jantimon
2
请注意:这不是一个整数数组,而是一个字符串数组。如果您错误地比较这些元素,可能会得到“意外”的结果,因为字符串使用词法排序。例如,'20' < '120' === false,而 20 < 120 === true - Mattias Buelens
我发现这个解决方案(http://www.ajaybadgujar.com/finding-second-largest-number-from-array-in-javascript/)非常有用,而且复杂度很小。 - coder
function processData(myArray) { var setArray = Array.from(new Set(myArray)).sort((a, b) => a - b) return setArray[setArray.length - 2] || myArray[0]; } - Suresh Prajapati
1
最简单的解决方案之一是:- arr = [20,120,111,215,54,78]; let secondLargest = arr.sort(function(x,y) { return y-x} )[1]; 如果你想采用传统的方式;- arr = [20,120,111,215,54,78]; max=arr[0] max2=arr[0] for(let i=0; i<arr.length; i++) { if(max < arr[i]) { max2 = max; max = arr[i]; } else if(max2 < arr[i]) { max2 = arr[i]; } } console.log(max: ${max} and max2: ${max2}); - Chang
由于此问题已关闭,我只能将答案添加为评论。抱歉。 答案如下 ---> let getSecondLargeNumber = function(nums) { nums.sort((a,b)=>b-a); let biggestNumber = nums[0]; let secondLargeNumber = nums.find(element => element < biggestNumber); if (secondLargeNumber == undefined) { secondLargeNumber = 0; } console.log('biggestNumber: ' + biggestNumber); console.log('secondLargeNumber: ' + secondLargeNumber); return secondLargeNumber; } - Ozan BAYRAM
9个回答

60

最直接的实现方式是,不修改原始数组,迭代并跟踪最大和次大的元素:

function nextBiggest(arr) {
  let max = -Infinity, result = -Infinity;

  for (const value of arr) {
    const nr = Number(value)

    if (nr > max) {
      [result, max] = [max, nr] // save previous max
    } else if (nr < max && nr > result) {
      result = nr; // new second biggest
    }
  }

  return result;
}

const arr = ['20','120','111','215','54','78'];
console.log(nextBiggest(arr));

变化

如果在非空数组中没有与最大值不同的下一个最大值,则返回-Infinity的行为可以在函数末尾进行修改,具体取决于需求。

与最大值相同

return result == -Infinity ? max : result;

对于一个空数组,这将像以前一样返回 -Infinity,但如果没有找到下一个不同的最大值,则会返回与最大值相同的值。

返回 null

return result == -Infinity ? null : result;

与上面相同,但null的返回值更能说明下一个不同的最大值不存在。


4
我很惊讶,人们花了15分钟来排序数组,而不是只执行一次遍历。 - davin
1
这在处理两个相等的“最高值”时也存在问题。我想这仍然是一个定义问题,但是对于这个要求的任何算法都应该返回5,对于列表 [5,10,10] - jAndy
@jAndy 感谢您发现这个明显的疏忽 :) 已经修复。 - Ja͢ck
3
在JavaScript中,你通常不太关心你的代码需要用10毫秒还是3毫秒,而更加关心应用程序的简洁性。我并不认为这是一个不好的回答,它很好,但这里评论中关于性能的担忧可能有些夸张。 - Denys Séguret
@PourMeSomeCode 很抱歉回复晚了...如果一个数组只有一个元素或者所有元素都相同,那么从技术上讲就没有“下一个更大的”值可言;因此这样声明…希望这样说得清楚:) - Ja͢ck
显示剩余7条评论

49

Original answer

var secondMax = function (){ 
    var arr = [20, 120, 111, 215, 54, 78]; // use int arrays
    var max = Math.max.apply(null, arr); // get the max of the array
    arr.splice(arr.indexOf(max), 1); // remove max from the array
    return Math.max.apply(null, arr); // get the 2nd max
};

更新 1

正如davin所指出的,性能可以通过不使用splice而是通过-Infininty 临时替换最大值来提高:

演示

var secondMax = function (arr){ 
    var max = Math.max.apply(null, arr), // get the max of the array
        maxi = arr.indexOf(max);
    arr[maxi] = -Infinity; // replace max in the array with -infinity
    var secondMax = Math.max.apply(null, arr); // get the new max 
    arr[maxi] = max;
    return secondMax;
};

无论如何,在我看来最好的算法是Jack的。只需1次遍历,转换为数字。 我的算法很简短,使用内置方法,只想提供它作为一种替代方案,展示实现目标的不同方式。

更新2

包含多个值的边缘情况。

正如评论所指出的那样:如果我们有一个数组,例如[3, 3, 5, 5, 5, 4, 4],则此解决方案“无效”。 另一方面,对于“第二大元素”,我们会考虑什么才算“第二大元素”,这也是一种解释方式。在该示例中:

  1. 最大值(5)的3个元素在索引2、3、4处
  2. 第二大值(4)的2个元素在索引5、6处
  3. 第二小值(3)的2个元素在索引1、2处

“第二大元素”可以解释为:

  1. 第2个(最大的)元素-位于索引3的5-假设存在顺序,并且我们的目标是唯一值
  2. (第2大的)元素-位于索引5的4-假设存在顺序,并且我们的目标是唯一值

3
目前为止唯一正确的 O(n) 解法。 - davin
2
@davin 它会修改数组。 - Ja͢ck
@davin 我不会称之为改进... 你看过 我的 回答了吗?:) 不过 -Infinity 是一个很好的观点.. - Ja͢ck
@Jack,你不认为这是一种改进吗?自从什么时候节省时间和空间并保持输入不是一种改进了呢? - davin
13
它在像 [5,3,20,9,20] 这样的列表上也会“失败”。 - jAndy
显示剩余9条评论

23

最简单的解决方案是排序:

// here's your array :
var stringArray = new Array('20','120','111','215','54','78');

// let's convert it to a real array of numbers, not of strings :
var intArray = stringArray.map(Number);

// now let's sort it and take the second element :
var second = intArray.sort(function(a,b){return b-a})[1]; 

如果你不需要最简单的方法但需要最快速的方法,那么你需要编写一个 for 循环并在循环时存储两个最大元素。


16
+1 表示支持使用 .map(Number) 的方法。 <3 表示喜欢或者爱。 - jAndy
6
如果列表中有两个相同的数字,这将失败。 - Harry Bomrah
1
@dystroy 我认为Harry的意思是如果有两个值为“215”,它会得到错误的第二大值。 - Ja͢ck
2
@DenysSéguret 对于输入 ['20','120','111','215','54','54'] 不起作用。 - coder
2
如果存在重复项,请在排序之前执行 intArray = [...new Set(intArray)]; - Henrikh Kantuni
显示剩余6条评论

8

首先将它倒叙排列,然后获取第二个元素:

['20','120','111','215','54','78'].sort(function(a, b) { return b - a; })[1];
// '120'

很明显,这也适用于字符串。

1
+1 是指在排序时,将字符串转换为数字并不是必需的。 - Denys Séguret
@dystroy,强制转换是必需的,但并不是显式所需要的。这种方法确实将值转换为Number,只不过它是隐式地进行的。 - davin
@davin 我知道,当然,我是在谈论添加一个中间步骤。 - Denys Séguret
当数组中存在重复数字时,例如[2, 3, 6, 6, 5],使用这行代码将会失败,期望输出为5,但实际得到的是6。 - Abhishek Singh
对于数组中的重复值,此代码未通过预期的算法。 - Bangash

1

对数组进行排序,然后返回第二个索引。

var arr = ['20','120','111','215','54','78'];

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

console.log(arr[1]);

4
如果数组中存在重复项,这将会失败。 - Gopal Yadav

0
function getSecondLargest(nums) {
 return [...new Set(nums)].sort((a,b)=>b-a)[1]    
}

简洁明了,这是它的优点。您是否愿意就仅具有一个不同值的参数的行为发表评论,以与其他建议的“sort()解决方案”进行比较? - greybeard

0
你可以尝试这个:
function second_highest(arr)
{
  var second_highest = arr.sort(function(a, b) { return b - a; })[1];
  return second_highest;

}

请注意,sort将对原始数组进行排序,而不会创建副本。因此,任何依赖于先前项目排序的内容可能会出现问题。 - René Wolferink

0

将数组从小到大排序,然后使用.length-2获取倒数第二个。

var myArray =['20','120','111','215','54','78'];
var secondLargest = myArray.sort(function(a,b){return a - b})[myArray.length-2];
alert(secondLargest); //120;

1
如果最小数重复多次,就会失败 lol。 - minigeek

0

以下是使用过滤器和归约方法的解决方案:

arr = [20,120,111,215,54,78];
const secondMax = arr
         .filter((ele)=> ele !== Math.max(...arr))
         .reduce((acc, ele) => ele > acc ? ele : acc , arr[0])

为什么不使用 Math.max(...arr.filter(x => x !== Math.max(...arr))) 呢? - MoRe
这也很好用,看起来更美观。 - Smriti Rastogi
如果只想使用reduce,可以使用以下代码:arr.slice(2).reduce((x, y) => [...x, y].sort((a, b) => b - a).slice(0, 2), arr.slice(0, 2))[1] - MoRe
arr.slice(2).reduce((x, y) => y > x[0] ? [y, x[0]] : x[0] > y > x[1] ? [x[0], y] : x, arr.slice(0, 2).sort())[1] - MoRe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接