data structures
student rankings
student marks
educational data
data organization

Best Data Structure to store marks and ranks of students

Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms

Introduction

Storing student marks and ranks requires a data structure that supports fast insertion, efficient sorting for rank computation, and quick lookups by student ID. The best choice depends on the primary operations: a dictionary/hash map works well for fast lookups by student ID, a sorted container (balanced BST, sorted list) enables efficient rank queries, and a heap is ideal when you only need the top-N students. For real applications, combining a hash map for O(1) lookups with a sorted structure for O(log n) rank queries is the most practical approach.

Dictionary / Hash Map (Fast Lookup)

python
1# Simple dictionary: O(1) lookup by student ID
2students = {
3    "S001": {"name": "Alice", "marks": 92, "rank": None},
4    "S002": {"name": "Bob", "marks": 85, "rank": None},
5    "S003": {"name": "Charlie", "marks": 91, "rank": None},
6}
7
8# Look up a student by ID — O(1)
9print(students["S002"]["marks"])  # 85
10
11# Update marks — O(1)
12students["S002"]["marks"] = 88
13
14# Compute ranks by sorting — O(n log n)
15sorted_students = sorted(students.items(), key=lambda x: x[1]["marks"], reverse=True)
16for rank, (sid, data) in enumerate(sorted_students, 1):
17    students[sid]["rank"] = rank
18
19for sid, data in students.items():
20    print(f"{data['name']}: marks={data['marks']}, rank={data['rank']}")
21# Alice: marks=92, rank=1
22# Bob: marks=88, rank=3
23# Charlie: marks=91, rank=2

Dictionaries provide O(1) lookups and updates but require O(n log n) re-sorting every time ranks need to be recomputed. This is suitable when rank queries are infrequent compared to lookups and updates.

Sorted List with bisect (Maintained Order)

python
1import bisect
2
3class RankedStudents:
4    def __init__(self):
5        self.sorted_marks = []  # sorted list of (marks, student_id)
6        self.students = {}       # student_id -> {name, marks}
7
8    def add_student(self, sid, name, marks):
9        self.students[sid] = {"name": name, "marks": marks}
10        bisect.insort(self.sorted_marks, (marks, sid))
11
12    def get_rank(self, sid):
13        """Rank 1 = highest marks. O(n) for position lookup."""
14        marks = self.students[sid]["marks"]
15        # Count how many students have higher marks
16        total = len(self.sorted_marks)
17        idx = bisect.bisect_right(self.sorted_marks, (marks, sid))
18        return total - idx + 1
19
20    def top_n(self, n):
21        """Return top N students. O(n) slice."""
22        return [(sid, m) for m, sid in reversed(self.sorted_marks[-n:])]
23
24rs = RankedStudents()
25rs.add_student("S001", "Alice", 92)
26rs.add_student("S002", "Bob", 85)
27rs.add_student("S003", "Charlie", 91)
28
29print(rs.get_rank("S001"))  # 1
30print(rs.top_n(2))          # [('S001', 92), ('S003', 91)]

bisect.insort maintains a sorted list with O(n) insertion (due to shifting). Rank lookup is O(log n) for finding the position. This works well for small to medium datasets (up to ~10,000 students).

Heap (Top-N Students Efficiently)

python
1import heapq
2
3# Min-heap: efficiently get top N students
4students_data = [
5    (92, "Alice"), (85, "Bob"), (91, "Charlie"),
6    (78, "Diana"), (95, "Eve"), (88, "Frank"),
7]
8
9# Get top 3 students by marks — O(n log k)
10top_3 = heapq.nlargest(3, students_data, key=lambda x: x[0])
11print(top_3)
12# [(95, 'Eve'), (92, 'Alice'), (91, 'Charlie')]
13
14# Streaming: maintain top-k as new students arrive
15class TopKTracker:
16    def __init__(self, k):
17        self.k = k
18        self.heap = []  # min-heap of size k
19
20    def add(self, marks, name):
21        if len(self.heap) < self.k:
22            heapq.heappush(self.heap, (marks, name))
23        elif marks > self.heap[0][0]:
24            heapq.heapreplace(self.heap, (marks, name))
25
26    def get_top_k(self):
27        return sorted(self.heap, reverse=True)
28
29tracker = TopKTracker(3)
30for marks, name in students_data:
31    tracker.add(marks, name)
32print(tracker.get_top_k())
33# [(95, 'Eve'), (92, 'Alice'), (91, 'Charlie')]

A heap is optimal when you only need the top-N students. heapq.nlargest is O(n log k) where k is the number of results, much faster than sorting all n students when k is small.

Balanced BST with SortedContainers

python
1from sortedcontainers import SortedList
2
3class StudentRankings:
4    def __init__(self):
5        self.sorted_marks = SortedList()  # balanced BST internally
6        self.students = {}
7
8    def add(self, sid, name, marks):
9        self.students[sid] = {"name": name, "marks": marks}
10        self.sorted_marks.add((marks, sid))
11
12    def remove(self, sid):
13        marks = self.students[sid]["marks"]
14        self.sorted_marks.remove((marks, sid))
15        del self.students[sid]
16
17    def update_marks(self, sid, new_marks):
18        old_marks = self.students[sid]["marks"]
19        self.sorted_marks.remove((old_marks, sid))
20        self.students[sid]["marks"] = new_marks
21        self.sorted_marks.add((new_marks, sid))
22
23    def get_rank(self, sid):
24        marks = self.students[sid]["marks"]
25        idx = self.sorted_marks.index((marks, sid))
26        return len(self.sorted_marks) - idx
27
28    def top_n(self, n):
29        return [(sid, m) for m, sid in reversed(self.sorted_marks[-n:])]
30
31rankings = StudentRankings()
32rankings.add("S001", "Alice", 92)
33rankings.add("S002", "Bob", 85)
34rankings.add("S003", "Charlie", 91)
35print(rankings.get_rank("S001"))  # 1
36rankings.update_marks("S002", 94)
37print(rankings.get_rank("S002"))  # 1 (Bob now has highest marks)

SortedList from the sortedcontainers library provides O(log n) insertion, deletion, and index-based lookup. This is the best all-around choice when ranks need to be queried frequently and marks are updated often.

Comparison Table

Data StructureLookup by IDInsertGet RankTop-NUpdate Marks
Dictionary (hash map)O(1)O(1)O(n log n)O(n log n)O(1)
Sorted list (bisect)O(1)*O(n)O(log n)O(n) sliceO(n)
HeapO(n)O(log n)N/AO(n log k)O(n)
SortedList (BST)O(1)*O(log n)O(log n)O(n) sliceO(log n)

* With a separate hash map for ID-to-data mapping.

Common Pitfalls

  • Re-sorting the entire list for every rank query: If ranks are queried after every insertion, sorting an unsorted list each time is O(n log n) per query. Use a maintained sorted structure (SortedList, BST) for O(log n) rank queries.
  • Using a heap when arbitrary rank queries are needed: Heaps only efficiently support finding the minimum (or maximum) element. Getting the rank of an arbitrary student requires O(n) search through the heap. Use a sorted structure for general rank queries.
  • Not handling tied marks: When multiple students have the same marks, you need to decide on a tie-breaking strategy — same rank for ties (dense ranking), or sequential ranks (ordinal ranking). Define this explicitly in your rank computation.
  • Forgetting to update both structures after a marks change: When using a hash map alongside a sorted structure, updating marks in the hash map without updating the sorted structure causes rank inconsistencies. Always update both atomically.
  • Choosing O(n) structures for large datasets: For thousands of students with frequent updates and rank queries, simple lists with linear-time operations become a bottleneck. Use SortedList or a database with indexed columns for datasets larger than a few hundred records.

Summary

  • Use a dictionary for fast O(1) student lookups by ID
  • Use SortedList from sortedcontainers for O(log n) insertions, rank queries, and updates
  • Use a heap when you only need the top-N students efficiently
  • Combine a hash map (for lookups) with a sorted structure (for ranks) for the best of both
  • For large-scale applications, use a database with indexed columns on marks
  • Always define a tie-breaking strategy for students with identical marks

Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms