Top Scoring Entities — Problem Statement & Solution Guide
Quick Answer & Algorithm Key Takeaway
Arrays
O(n)O(k)Problem Description
Given an array of objects where each object contains a unique string field "name" and a positive integer field "score", produce a new array consisting of the five objects with the highest scores sorted from highest to lowest. The ordering must be stable: if two or more objects share the same score, they appear in the output in the same relative order as in the input. If the input contains fewer than five objects, return all of them sorted by score descending while preserving stability. The returned array should contain the full objects (both name and score).
DSA Pattern Breakdown
DSA Pattern Breakdown
"Top Scoring Entities"
WHY DOES IT MATTER?
Selecting a small subset of high‑value items without sorting the whole dataset is a recurring pattern in performance‑critical code, from leaderboards to recommendation engines. Mastering the bounded‑heap technique prevents unnecessary O(n log n) work and keeps memory footprints low.
OPTIMIZATION CHALLENGE
The key insight is that you only need to keep track of the current worst element among the top‑k candidates. A min‑heap of fixed size lets you compare incoming items to that worst element in O(1) (peek) and update in O(log k), turning a potentially quadratic scan into linear time.
REAL-WORLD CONNECTION
Think of a newsroom editor who must keep only the five most important stories on the front page. As new stories arrive, the editor replaces the least important one if a more critical story appears, without re‑reading all previous stories each time.
When coding, store both score and original index in the heap node; use a comparator that first compares scores and then indices. After the scan, convert the heap to an array and sort it descending by score and ascending by index to produce the final stable order.
COMPLEXITY AT A GLANCE
O(n)O(k)Core Theory — Why This Approach?
The problem is a classic selection problem where we need the top‑k elements from a stream while preserving the original relative order for equal scores. A naïve solution would sort the entire array, which costs O(n log n) time and O(n) space, and it discards the stability requirement unless a stable sort is used. For very large inputs this becomes prohibitive because the full sort does unnecessary work on the majority of elements that will never appear in the final top‑5 list. The optimal paradigm leverages a bounded priority queue (min‑heap) of size k (here k=5). As we scan the array once, we keep only the best k candidates; each insertion or removal costs O(log k), yielding overall O(n log k) time, which for constant k collapses to linear O(n). By storing the original index alongside each object, we can break ties by index, guaranteeing stable ordering when scores are equal. This approach reduces both time and auxiliary space dramatically while satisfying the stability constraint.
Interview Questions on This Problem
Q1How would you modify the algorithm if the requirement changed from "top 5" to "top k" where k is provided at runtime?
Maintain a min‑heap of size k instead of a fixed size. While iterating, push each element with its index; if the heap exceeds k, pop the smallest (by score then index). After the pass, extract heap elements and sort them descending by score and ascending by original index to preserve stability.
Q2Explain why a simple quick‑select partition alone does not guarantee the stable ordering of equal scores, and how you would enforce stability after selection.
Quick‑select partitions based solely on score can reorder elements with identical scores arbitrarily, breaking stability. To enforce stability, you can augment each element with its original index and treat (score, index) as the comparison key, or after selecting the threshold score, perform a stable filter of the original array to collect all elements with scores above the threshold, then take the first k respecting original order.
Q3In a distributed system processing massive logs, how could you compute the top‑5 scoring entities across multiple nodes efficiently?
Each node runs the local top‑5 heap algorithm on its partition, emitting its local top‑5 list. A central aggregator then merges these lists using another min‑heap of size 5 (or k) while preserving original timestamps for tie‑breaking, resulting in the global top‑5 with O(N log k) total work and minimal network payload.
Examples
Input
[{"name":"alpha","score":15},{"name":"beta","score":20},{"name":"gamma","score":10},{"name":"delta","score":20},{"name":"epsilon","score":5},{"name":"zeta","score":25}]Output
[{"name":"zeta","score":25},{"name":"beta","score":20},{"name":"delta","score":20},{"name":"alpha","score":15},{"name":"gamma","score":10}]Explanation: The scores in descending order are 25,20,20,15,10,5. The object with score 25 (zeta) is first. For the two objects with score 20, beta appears before delta in the original list, so that order is kept. The next highest scores are 15 (alpha) and 10 (gamma). Only five objects are required, so epsilon (score 5) is omitted.
Input
[{"name":"x","score":3},{"name":"y","score":7}]Output
[{"name":"y","score":7},{"name":"x","score":3}]Explanation: There are only two entities. Sorting them by score descending yields y (7) followed by x (3).
Input
[{"name":"a","score":100},{"name":"b","score":90},{"name":"c","score":80},{"name":"d","score":70},{"name":"e","score":60},{"name":"f","score":60},{"name":"g","score":60}]Output
[{"name":"a","score":100},{"name":"b","score":90},{"name":"c","score":80},{"name":"d","score":70},{"name":"e","score":60}]Explanation: Scores in descending order are 100,90,80,70,60,60,60. The first five positions correspond to a,b,c,d and the first occurrence of score 60, which is entity e. Because stability is required, entities f and g (also score 60) are excluded from the top five.
Constraints
- 1 <= entities.length <= 100000
- Each name consists of 1 to 20 alphanumeric characters
- 1 <= score <= 10^9
- All names are distinct
Optimal Approach & Strategy
Use a fixed‑size min‑heap (size 5) while scanning once, keeping only the current top‑5 candidates and preserving order via original indices.
Brute Force Approach
Sort the entire array with a stable sort and then take the first five elements.
Code Solutions
const fs = require('fs');
function topScoringEntities(arr){
// Attach original index to guarantee stability even if engine's sort were unstable
const withIdx = arr.map((obj,i)=>({obj,i}));
withIdx.sort((a,b)=>{
if(b.obj.score!==a.obj.score) return b.obj.score - a.obj.score;
return a.i - b.i; // preserve original order for equal scores
});
return withIdx.slice(0,5).map(x=>x.obj);
}
function main(){
const input = fs.readFileSync(0,'utf8').trim();
if(!input) return;
const parts = input.split(/\s+/);
const n = parseInt(parts[0]);
let idx=1;
const arr=[];
for(let i=0;i<n;i++){
const name=parts[idx++];
const score=parseInt(parts[idx++]);
arr.push({name,score});
}
const res=topScoringEntities(arr);
console.log(JSON.stringify(res));
}
main();#include <bits/stdc++.h>
using namespace std;
struct Obj{string name;int score;};
vector<Obj> topScoringEntities(const vector<Obj>& arr){
vector<pair<Obj,size_t>> indexed;
indexed.reserve(arr.size());
for(size_t i=0;i<arr.size();++i) indexed.emplace_back(arr[i],i);
stable_sort(indexed.begin(), indexed.end(), [](const auto& a,const auto& b){
return a.first.score>b.first.score; // descending, stable_sort keeps original order for ties
});
vector<Obj> res;
for(size_t i=0;i<indexed.size() && i<5; ++i) res.push_back(indexed[i].first);
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; if(!(cin>>n)) return 0;
vector<Obj> arr(n);
for(int i=0;i<n;++i){cin>>arr[i].name>>arr[i].score;}
vector<Obj> res=topScoringEntities(arr);
cout<<"[";
for(size_t i=0;i<res.size();++i){
cout<<"{\"name\":\""<<res[i].name<<"\",\"score\":"<<res[i].score<<"}";
if(i+1<res.size()) cout<<",";
}
cout<<"]\n";
return 0;
}import java.io.*;
import java.util.*;
class Entity{String name;int score;Entity(String n,int s){name=n;score=s;}}
public class Main{
static List<Entity> topScoringEntities(List<Entity> arr){
List<Pair> list=new ArrayList<>();
for(int i=0;i<arr.size();i++) list.add(new Pair(arr.get(i),i));
list.sort((a,b)->{
if(b.e.score!=a.e.score) return Integer.compare(b.e.score,a.e.score);
return Integer.compare(a.idx,b.idx);
});
List<Entity> res=new ArrayList<>();
for(int i=0;i<list.size() && i<5;i++) res.add(list.get(i).e);
return res;
}
static class Pair{Entity e;int idx;Pair(Entity e,int idx){this.e=e;this.idx=idx;}}
public static void main(String[] args)throws Exception{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=br.readLine();
if(line==null||line.isEmpty()) return;
int n=Integer.parseInt(line.trim());
List<Entity> arr=new ArrayList<>();
for(int i=0;i<n;i++){
String[] parts=br.readLine().trim().split("\\s+");
arr.add(new Entity(parts[0],Integer.parseInt(parts[1])));
}
List<Entity> res=topScoringEntities(arr);
StringBuilder sb=new StringBuilder();
sb.append('[');
for(int i=0;i<res.size();i++){
Entity e=res.get(i);
sb.append('{').append("\"name\":\"").append(e.name).append("\",\"score\":").append(e.score).append('}');
if(i+1<res.size()) sb.append(',');
}
sb.append(']');
System.out.println(sb.toString());
}
}import sys, json
def top_scoring_entities(arr):
# enumerate to keep original positions
indexed = [(obj, i) for i, obj in enumerate(arr)]
indexed.sort(key=lambda x: (-x[0]['score'], x[1]))
return [obj for obj, _ in indexed[:5]]
def main():
data=sys.stdin.read().strip().split()
if not data:
return
n=int(data[0])
arr=[]
idx=1
for _ in range(n):
name=data[idx]; idx+=1
score=int(data[idx]); idx+=1
arr.append({"name":name,"score":score})
res=top_scoring_entities(arr)
print(json.dumps(res))
if __name__=="__main__":
main()const fs = require('fs');
function topScoringEntities(arr){
// Attach original index to guarantee stability even if engine's sort were unstable
const withIdx = arr.map((obj,i)=>({obj,i}));
withIdx.sort((a,b)=>{
if(b.obj.score!==a.obj.score) return b.obj.score - a.obj.score;
return a.i - b.i; // preserve original order for equal scores
});
return withIdx.slice(0,5).map(x=>x.obj);
}
function main(){
const input = fs.readFileSync(0,'utf8').trim();
if(!input) return;
const parts = input.split(/\s+/);
const n = parseInt(parts[0]);
let idx=1;
const arr=[];
for(let i=0;i<n;i++){
const name=parts[idx++];
const score=parseInt(parts[idx++]);
arr.push({name,score});
}
const res=topScoringEntities(arr);
console.log(JSON.stringify(res));
}
main();Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.