3

I'm trying to pass a pre-written test with focus on performance. What is the most efficient way to return the sum of the two highest numbers in a List of int? I have tried the following and according to the test it wasn't fast enough when it comes to larger lists:

1.  list.Sort();
    list.Reverse();
    return list[0] + list[1];

2.  return list.OrderByDescending(num => num).FirstOrDefault() + list.OrderByDescending(num => num).Skip(1).FirstOrDefault();

3.  var secondHighest = list.Distinct()
                            .OrderByDescending(i => i)
                            .Skip(1)
                            .First();

    return list.Max() + secondHighest;
  • 1
    What is supposed to happen if the highest value appears twice? Is then highest and second highest that same value? – Fildor Jul 05 '22 at 07:35
  • Related: [Extract the k maximum elements of a list](https://stackoverflow.com/questions/15089373/extract-the-k-maximum-elements-of-a-list) – Theodor Zoulias Jul 05 '22 at 07:46
  • 1
    @Fildor There's no need to return the sum of distinct int's. If the two highest values are the same, return the sum of them anyway. – nissedanielsen Jul 05 '22 at 07:47

4 Answers4

6

Any straightforward sorting operation would be O(n*log(n)) (OrderBy(Descending), Sort) at best (on random data) though current task is achievable in O(n) with a simple loop (assuming there are at least 2 items in the list of course, for the case when there are 2 or less elements just return list.Sum()):

var first = int.MinValue;
var second = int.MinValue;
foreach(var i in list)
{
    if(i > first)
    {
        second = first;
        first = i;
    }
    else if(i > second)
    {
        second = i;
    }
}

var result = first + second;

Also it worth noting that there can be implementation depended LINQ optimizations for some of cases like combination of OrderBy(Descending)with operators like First(OrDefault) or possibly Take (see one, two, three).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
3

Well, I'm proposing this algorithm:

highest = 0
highestSet = false
secondHighest = 0
secondHighestSet = false
foreach item in list
    if item >= highest or !highestSet
        if highestSet
          secondHighest = highest
        highestSet = true
        highest = item
    else if item >= secondHighest or !secondHighestSet
        secondHighestSet = true
        secondHighest = item

return highest + secondHighest

Input set of [3, 2, 3, 2], it will return 6. This is O(n) time complexity.

For a set of [3], it will return 3.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
1

LINQ can be a very good solution for it.

long sum = 0;
if(list.Count > 1) 
  sum = list.OrderByDescending(z=>z).Take(2).Sum();
else
  sum = list.Sum();
  • The question was about performance/efficiency. Do you think your solution using Linq especially the call to OrderByDescending makes it falling into that group? – Ralf Jul 05 '22 at 08:58
  • @Ralf apparently the LINQ includes performance optimizations that make the `OrderByDescending`+`Take` a solution with O(n) complexity. At least this is what my benchmarks show on .NET 6. – Theodor Zoulias Jul 05 '22 at 09:08
  • @TheodorZoulias Can you share how you benchmarked? Linq uses Quicksort here so it should be O(n*log(n)). And the way Quicksort works it does not tend to have highest/lowest value positioned fast. So i don't see how a optimization for take should work without sacrificing performance in most other cases. And, by the way, i can't see anything like that in the sourcecode of Net6. – Ralf Jul 05 '22 at 09:33
  • @TheodorZoulias https://stackoverflow.com/questions/64665974/is-the-ienumerable-orderby-first-optimisation-in-net-core-3-1-documented-an – Matthew Watson Jul 05 '22 at 10:20
  • @TheodorZoulias The optimisation is only done for `.First()` - if you use `Take(2)` it will do a full sort. See [this DotNetFiddle](https://dotnetfiddle.net/0BTK7Q) – Matthew Watson Jul 05 '22 at 10:26
  • Anyway, this solution will be `O(N.Log(N))` in the general case, because of the `Take(2)`. – Matthew Watson Jul 05 '22 at 10:30
  • @Ralf I posted my findings [here](https://stackoverflow.com/questions/15089373/extract-the-k-maximum-elements-of-a-list/72869657#72869657 "Extract the k maximum elements of a list"). – Theodor Zoulias Jul 05 '22 at 12:45
-1

You can try this simple way:

list.Sort();
list.Reverse();
var secondHighest=list.Take(2).Sum();
 
return secondHighest;

or

 list=list.OrderByDescending(o=>o).ToList();
 var secondHighest=list.Take(2).Sum();
 
return secondHighest;

At first, it sorts your list, then reverses it , now you have descending sorted list , than it will take 2 highest elements and aggregate them .

Apollo13
  • 11
  • 6