Python Algorithms & Data Structures track

Lesson 10 · Part 3: Sorting & Searching

Python Sorting in Practice

sorted/sort, key functions, multiple keys and stable sorting

Part 3: Sorting & Searching3 min readFree lesson

Part 03 – Sorting and Navigation | Difficulty: ⭐⭐☆☆☆


Introduction: Timsort

Python's sorted() and list.sort() use the Timsort algorithm.

  • Time complexity: O(n log n) (best case O(n) — already sorted)
  • Space complexity: O(n)
  • Guarantees stable sorting
  • Very fast on real data (mix of run detection and binary insertion sort)

In coding tests, you rarely need to implement sorting yourself. The key is to freely use the key parameter and lambdas.


Learning Objectives

  1. Explain the difference between sorted() and list.sort() and choose appropriately for the situation.
  2. Utilize lambda, len, operator.itemgetter, etc., as parameters for key.
  3. Apply sorting to multi-criteria sorting, coordinate compression, and Top-K extraction.

Key concepts

sorted() vs list.sort()

Item sorted(iterable) list.sort()
Return Value Returns a new list Returns None (in-place)
Modifies Original Does not change Changes
Input Type Any iterable List only
Example Usage new = sorted(lst) lst.sort()
lst = [3, 1, 2]
new = sorted(lst)   #lst as is, new = [1, 2, 3]
lst.sort()          #lst itself is [1, 2, 3]

key Parameter

key is a function applied to each element, which serves as the basis for comparison.

#Sort by length
words = ["banana", "apple", "fig"]
sorted(words, key=len)          # ['fig', 'apple', 'banana']

#Sort by absolute value
nums = [-5, 3, -1, 4]
sorted(nums, key=abs)           # [-1, 3, 4, -5]

#lambda: based on the second element
pairs = [(1, 3), (2, 1), (3, 2)]
sorted(pairs, key=lambda x: x[1])  # [(2,1), (3,2), (1,3)]

operator.itemgetter

Slightly faster and more readable than lambda x: x[1].

from operator import itemgetter, attrgetter

data = [("Alice", 85), ("Bob", 92), ("Charlie", 85)]
sorted(data, key=itemgetter(1))              #Score Criteria
sorted(data, key=itemgetter(1, 0))           #By Score, Name Order

Multi-Criteria Sorting

Tuple comparison is done from left to right:

#Sort by score in descending order, and if equal, by name in ascending order
students = [("Alice", 85), ("Bob", 92), ("Charlie", 85)]
sorted(students, key=lambda s: (-s[1], s[0]))
# [('Bob', 92), ('Alice', 85), ('Charlie', 85)]

Stability Guarantee

Python sorting is stable, so elements with the same key maintain their original order.

data = [("A", 2), ("B", 1), ("C", 2)]
sorted(data, key=lambda x: x[1])
#[('B', 1), ('A', 2), ('C', 2)]  ← A is before C

reverse=True

sorted([3, 1, 2], reverse=True)    # [3, 2, 1]
#Note: It is also possible with key=lambda x: -x, but reverse=True is clearer

See as example

file Content
src/01_sorted_basics.py sorted vs sort, strings · numbers · objects
src/02_key_functions.py key=lambda, len, itemgetter, multiple fields
src/03_multi_key_sort.py Multiple criteria like name→age, tuple comparison
src/04_sort_applications.py Coordinate compression, duplicate removal, Top-K

5 common mistakes

  1. Ignore the return value of sorted() and use the original

    lst = [3, 1, 2]
    sorted(lst)        #← Return value truncated!
    print(lst)         #[3, 1, 2] — Not sorted
    #Solution: lst = sorted(lst) or lst.sort()
    
  2. Do not know that list.sort() returns None

    result = lst.sort()   # result = None!
    #Solution: Use lst after lst.sort()
    
  3. In multi-criteria descending, reverse=True applies to all

    #When you want to sort by score descending and name ascending
    #Incorrect method:
    sorted(data, key=lambda x: (x[1], x[0]), reverse=True)
    #← Name also becomes descending!
    #Correct method:
    sorted(data, key=lambda x: (-x[1], x[0]))
    
  4. TypeError when mixing strings and numbers

    #In Baekjoon string sorting, if you receive numbers as strings
    lst = ["10", "9", "100"]
    sorted(lst)              #['10', '100', '9'] — lexicographical order!
    sorted(lst, key=int)     #['9', '10', '100'] — correct numerical order
    
  5. Using side effects in key function

  • The key function may be called multiple times, so do not change state
  • Use only pure functions

Cleanup

Purpose Recommended Method
Simple ascending sorted(lst)
Simple descending sorted(lst, reverse=True)
Based on a specific field key=lambda x: x[1]
Based on multiple fields key=lambda x: (x[1], x[0])
Multi with descending key=lambda x: (-x[1], x[0])
List of tuples key=itemgetter(1)
Object Sorting key=attrgetter('score')

Try it yourself

  1. Try sorting the student list [("Hong Gil-dong", 85, "Class A"), ...] by class, descending score, and then by name.
  2. How can you sort ["apple", "Banana", "cherry"] ignoring uppercase and lowercase letters?
  3. Explain why coordinate compression is necessary, and compress [100, 50, 200, 50, 100] to 0-indexed.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_sorted_basics.pyPython
"""Topic  : sorted() vs list.sort() basics
Time   : O(n log n)  Timsort
Space  : O(n) for sorted(), O(1) extra for .sort()"""

from dataclasses import dataclass, field


# ── sorted() vs list.sort() ───────────────────────────────────────────────────

def demo_sorted_vs_sort() -> None:
    """Demonstration of the key differences between the two functions."""
    original = [3, 1, 4, 1, 5, 9, 2, 6]

    #sorted(): returns a new list, original unchanged
    new_list = sorted(original)
    print(f"sorted() return: {new_list}")
    print(f"Original unchanged: {original}")

    #list.sort(): in-place, returns None
    copy = original[:]
    ret = copy.sort()
    print(f"sort() return value: {ret}     ← None!")
    print(f"Copy modified: {copy}")

    #Common mistakes
    mistake = [3, 1, 2]
    mistake.sort()           #Correct usage
    print(f"Correct sort: {mistake}")

    oops = sorted([3, 1, 2])
    print(f"Using return value: {oops}     ← This is how it should be used")


#── String sorting ───────────────────────────────────────────────────────────────

def demo_string_sort() -> None:
    """Various examples of string sorting."""
    words = ["banana", "Apple", "cherry", "date", "fig"]

    print("=== String sorting ===")
    print(f"Default (dictionary order, case-sensitive): {sorted(words)}")
    print(f"Ignore case                : {sorted(words, key=str.lower)}")
    print(f"By length                  : {sorted(words, key=len)}")
    print(f"By reverse length          : {sorted(words, key=len, reverse=True)}")
    print(f"Order by last letter           : {sorted(words, key=lambda w: w[-1])}")

    #Be careful with numeric strings
    num_strs = ["10", "9", "100", "2", "20"]
    print(f"\nNumber strings in dictionary order  : {sorted(num_strs)}")
    print(f"Number strings in numeric order     : {sorted(num_strs, key=int)}")


#── Number sorting ───────────────────────────────────────────────────────────────

def demo_number_sort() -> None:
    """Various examples of number sorting."""
    nums = [-5, 3, -1, 4, -9, 2]

    print("\n=== Number Sorting ===")
    print(f"Ascending order      : {sorted(nums)}")
    print(f"Descending order     : {sorted(nums, reverse=True)}")
    print(f"By absolute value    : {sorted(nums, key=abs)}")
    print(f"By square            : {sorted(nums, key=lambda x: x**2)}")


#── Custom object sorting ─────────────────────────────────────────────────────────

@dataclass
class Student:
    name: str
    score: int
    grade: str

    def __repr__(self) -> str:
        return f"Student({self.name}, {self.score}, {self.grade})"


def demo_object_sort() -> None:
    """Object Sorting."""
    students = [
        Student("Hong Gil-dong", 85, "A"),
        Student("Yi Sun-sin", 92, "B"),
        Student("Gang Gam-chan", 85, "A"),
        Student("Yu Gwan-sun", 78, "B"),
    ]

    print("\n=== Object Sorting ===")
    by_score = sorted(students, key=lambda s: s.score)
    print(f"Score ascending     : {by_score}")

    by_score_desc = sorted(students, key=lambda s: -s.score)
    print(f"Score descending    : {by_score_desc}")

    by_name = sorted(students, key=lambda s: s.name)
    print(f"Name order          : {by_name}")

    #Using attrgetter
    from operator import attrgetter
    by_grade_score = sorted(students, key=attrgetter('grade', 'score'))
    print(f"Class → Score order : {by_grade_score}")


#── Main ────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    demo_sorted_vs_sort()
    demo_string_sort()
    demo_number_sort()
    demo_object_sort()
src/02_key_functions.pyPython
"""Topic  : Using key functions — lambda, len, itemgetter, multiple fields
Time   : O(n log n)  Timsort
Space  : O(n)"""

from operator import itemgetter, attrgetter


#── Basics of lambda ───────────────────────────────────────────────────────────────

def demo_lambda_key() -> None:
    """Various patterns using lambda as a key."""
    print("=== Using lambda key ===")

    #Specific index of a tuple
    coords = [(3, 4), (1, 2), (5, 0), (2, 8)]
    print(f"By x-coordinate: {sorted(coords, key=lambda p: p[0])}")
    print(f"By y-coordinate: {sorted(coords, key=lambda p: p[1])}")

    #By distance (from origin)
    print(f"By distance: {sorted(coords, key=lambda p: p[0]**2 + p[1]**2)}")

    #List of dictionaries
    people = [
        {"name": "Hong Gil-dong", "age": 30},
        {"name": "Yi Sun-sin", "age": 25},
        {"name": "Gang Gam-chan", "age": 35},
    ]
    print(f"By age: {sorted(people, key=lambda p: p['age'])}")
    print(f"By name: {sorted(people, key=lambda p: p['name'])}")


#── Built-in function key ──────────────────────────────────────────────────────────

def demo_builtin_key() -> None:
    """Use a built-in function as a key."""
    print("=== Built-in function key ===")

    words = ["python", "is", "awesome", "and", "fast"]
    print(f"Length order (len)  : {sorted(words, key=len)}")
    print(f"Absolute value (abs)   : {sorted([-3, 1, -5, 2, -1], key=abs)}")
    print(f"Lowercase (lower) : {sorted(['Banana', 'apple', 'Cherry'], key=str.lower)}")

    #Convert to str
    mixed_nums = [100, 9, 20, 3, 50]
    print(f"Numbers → Lexical order   : {sorted(mixed_nums, key=str)}")
    print(f"Numbers → Numerical order   : {sorted(mixed_nums)}")


# ── operator.itemgetter ───────────────────────────────────────────────────────

def demo_itemgetter() -> None:
    """itemgetter: faster and more concise than lambda."""
    print("\n=== operator.itemgetter ===")

    data = [("Alice", 85, "Class A"), ("Bob", 92, "Class B"),
            ("Charlie", 85, "Class A"), ("Dave", 78, "Class B")]

    #Single index
    print(f"Name order   : {sorted(data, key=itemgetter(0))}")
    print(f"Score order   : {sorted(data, key=itemgetter(1))}")

    #Multiple indices — compare with tuple
    print(f"Class → Score order: {sorted(data, key=itemgetter(2, 1))}")

    #Use in list of dictionaries
    products = [
        {"name": "Apple", "price": 1200, "qty": 50},
        {"name": "Pear", "price": 2500, "qty": 30},
        {"name": "Persimmon", "price": 800, "qty": 70},
    ]
    print(f"Price order: {sorted(products, key=itemgetter('price'))}")
    print(f"Quantity order: {sorted(products, key=itemgetter('qty'))}")


#── Composite key pattern ─────────────────────────────────────────────────────────

def demo_complex_key() -> None:
    """A complex key pattern frequently used in practice."""
    print("=== Composite key pattern ===")

    #Descending by score + ascending by name (negative trick)
    students = [("Hong Gil-dong", 85), ("Yi Sun-sin", 92), ("Gang Gam-chan", 85), ("Yu Gwan-sun", 92)]
    result = sorted(students, key=lambda s: (-s[1], s[0]))
    print(f"Score↓ Name↑: {result}")

    #Ascending by string length + lexicographical order
    words = ["cat", "at", "dog", "be", "ant"]
    result2 = sorted(words, key=lambda w: (len(w), w))
    print(f"Length↑ Lexical↑: {result2}")

    #Special case: sorting with None included
    data_with_none = [3, None, 1, None, 2]
    result3 = sorted(data_with_none, key=lambda x: (x is None, x or 0))
    print(f"None last : {result3}")


# ── attrgetter ────────────────────────────────────────────────────────────────

def demo_attrgetter() -> None:
    """attrgetter: Sorting based on object attributes."""
    from dataclasses import dataclass

    @dataclass
    class Point:
        x: int
        y: int
        label: str

        def __repr__(self) -> str:
            return f"({self.x},{self.y},{self.label})"

    points = [Point(3, 4, "C"), Point(1, 2, "A"), Point(3, 1, "B")]

    print("\n=== attrgetter ===")
    print(f"x order          : {sorted(points, key=attrgetter('x'))}")
    print(f"x→y order         : {sorted(points, key=attrgetter('x', 'y'))}")
    print(f"label order       : {sorted(points, key=attrgetter('label'))}")


#── Main ────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    demo_lambda_key()
    demo_builtin_key()
    demo_itemgetter()
    demo_complex_key()
    demo_attrgetter()
src/03_multi_key_sort.pyPython
"""Topic  : Multi-criteria sorting — Name → Age, Tuple Comparison
Time   : O(n log n)
Space  : O(n)"""


#── Multi-Criteria Sorting Patterns ───────────────────────────────────────────────

def demo_multi_key_patterns() -> None:
    """Practical patterns for multi-criteria sorting."""
    print("=== Multi-Criteria Sorting Patterns ===")

    employees = [
        ("Kim Cheolsu", "Development Team", 5_000_000),
        ("Lee Younghee", "Marketing", 4_500_000),
        ("Park Minjun", "Development Team", 4_500_000),
        ("Choi Jisoo", "Marketing", 5_000_000),
        ("Jung Daeun", "Development Team", 6_000_000),
    ]

    #By team, if it's the same team, by descending salary
    by_dept_salary = sorted(
        employees,
        key=lambda e: (e[1], -e[2])
    )
    print("By team, salary in descending order within team:")
    for emp in by_dept_salary:
        print(f"{emp[1]:10s} | {emp[0]} | {emp[2]:,} KRW")

    #By descending salary, if equal then by ascending name
    by_salary_name = sorted(
        employees,
        key=lambda e: (-e[2], e[0])
    )
    print("Salary descending, name ascending:")
    for emp in by_salary_name:
        print(f"{emp[0]} | {emp[2]:,} KRW")


#── Principle of Tuple Comparison ────────────────────────────────────────────────

def demo_tuple_comparison() -> None:
    """Python tuple comparison is the basis for multi-criteria sorting."""
    print("=== Principle of tuple comparison ===")

    pairs = [(1, 2), (1, 1), (2, 0), (1, 3)]
    print(f"Original      : {pairs}")
    print(f"After sorting: {sorted(pairs)}")
    print("→ (1,1) < (1,2) < (1,3) < (2,0)  If the first element is the same, compare the second")

    #Coding Test Practice: by starting point ascending, by ending point descending
    intervals = [(1, 5), (1, 3), (2, 4), (1, 7)]
    sorted_intervals = sorted(intervals, key=lambda x: (x[0], -x[1]))
    print(f"Interval sorting: {intervals}")
    print(f"Start↑ End↓   : {sorted_intervals}")


#── Name Sorting (Baekjoon Style) ───────────────────────────────────────────────

def demo_name_length_sort() -> None:
    """Baekjoon 1181 style: Ascending by length, and if the same, in lexicographical order."""
    words = ["zoo", "a", "bc", "ab", "b", "aaa", "z"]
    result = sorted(set(words), key=lambda w: (len(w), w))
    print(f"=== Name sorting (Baekjoon 1181 style) ===")
    print(f"Input: {words}")
    print(f"Remove duplicates + length↑ dictionary↑: {result}")


#── Coordinate Sorting (Baekjoon Style) ───────────────────────────────────────────

def demo_coordinate_sort() -> None:
    """Baekjoon 11650 style: x in ascending order, if equal then y in ascending order."""
    coords = [(3, 4), (1, 1), (1, -1), (2, 2), (3, 3)]
    result = sorted(coords)          #OK with basic tuple comparison
    print(f"=== Coordinate sorting (Baekjoon 11650 style) ===")
    print(f"Input  : {coords}")
    print(f"Sorted : {result}")

    #When written explicitly
    result2 = sorted(coords, key=lambda p: (p[0], p[1]))
    assert result == result2
    print("← sorted by x→y")


#── Multi-Level Sorting Using Stable Sort ─────────────────────────────────────────

def demo_stable_multi_sort() -> None:
    """Sequential sorting by priority using stable sort (classical technique)."""
    print("=== Stable sort multi-level sorting ===")

    data = [
        ("Alice", 85, "Class B"),
        ("Bob", 92, "Class A"),
        ("Charlie", 85, "Class A"),
        ("Dave", 78, "Class A"),
    ]

    #Goal: Class A → Class B, if the same class then descending score, if same score then by name
    #Method 1: Composite key (recommended)
    result1 = sorted(data, key=lambda x: (x[2], -x[1], x[0]))
    print("Composite key sorting:")
    for r in result1:
        print(f"  {r}")

    #Method 2: Consecutive application of stable sort (from lower priority → higher priority)
    step1 = sorted(data, key=lambda x: x[0])           #Name
    step2 = sorted(step1, key=lambda x: -x[1])          #Score descending
    step3 = sorted(step2, key=lambda x: x[2])           #Class

    print("Multi-level stable sorting:")
    for r in step3:
        print(f"  {r}")

    assert result1 == step3, "The results of the two methods are different!"
    print("Both methods yield the same result: ✓")


#── Main ────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    demo_multi_key_patterns()
    demo_tuple_comparison()
    demo_name_length_sort()
    demo_coordinate_sort()
    demo_stable_multi_sort()
src/04_sort_applications.pyPython
"""Topic: Sorting Applications — Coordinate Compression, Duplicate Removal, Top-K
Time: O(n log n) for sorting, O(n) for compression/duplicate removal
Space: O(n)"""


#── Coordinate Compression ─────────────────────────────────────────────────────────

def coordinate_compression(arr: list[int]) -> list[int]:
    """Coordinate Compression.

    When the range of values is very large (e.g., 0~10⁹), convert to indices (0~n-1).
    Refer to Baekjoon 18870.

    Args:
        arr: The original array to compress
    Returns:
        The compressed 0-indexed array"""
    #Remove duplicates and sort → Map to ranks
    rank = {v: i for i, v in enumerate(sorted(set(arr)))}
    return [rank[v] for v in arr]


def demo_coordinate_compression() -> None:
    """Example of coordinate compression."""
    print("=== Coordinate Compression ===")
    examples = [
        [5, 4, 2, 3, 1],
        [100, 50, 200, 50, 100],
        [1_000_000, 1, 500_000, 1, 1_000_000],
    ]
    for arr in examples:
        compressed = coordinate_compression(arr)
        print(f"Original: {arr}")
        print(f"Compressed: {compressed}")
        print()


#── Deduplication Based on Sorting ────────────────────────────────────────────────

def deduplicate_sorted(arr: list[int]) -> list[int]:
    """Remove duplicates by sorting and then comparing adjacent elements (O(n log n))."""
    if not arr:
        return []
    a = sorted(arr)
    result = [a[0]]
    for i in range(1, len(a)):
        if a[i] != a[i - 1]:
            result.append(a[i])
    return result


def demo_deduplication() -> None:
    """Comparison of duplicate removal methods."""
    print("=== Deduplication ===")
    data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

    #Method 1: set (order not important)
    by_set = sorted(set(data))

    #Method 2: Compare adjacent after sorting (when order preservation is not required)
    by_sort = deduplicate_sorted(data)

    #Method 3: Deduplication preserving order
    seen: set[int] = set()
    order_preserved = [x for x in data if not (seen.add(x) or x in seen)]  # type: ignore

    print(f"Original      : {data}")
    print(f"Using set     : {by_set}")
    print(f"Sort comparison: {by_sort}")
    assert by_set == by_sort


#── Top-K Elements ──────────────────────────────────────────────────────────────

def top_k_sort(arr: list[int], k: int) -> list[int]:
    """Top-K extraction by sorting O(n log n)."""
    return sorted(arr, reverse=True)[:k]


def top_k_heap(arr: list[int], k: int) -> list[int]:
    """Extract Top-K with heapq.nlargest O(n log k)."""
    import heapq
    return heapq.nlargest(k, arr)


def demo_top_k() -> None:
    """Top-K extraction comparison."""
    print("\n=== Top-K Extraction ===")
    scores = [85, 92, 78, 96, 88, 74, 100, 65, 91, 83]
    k = 3

    by_sort = top_k_sort(scores, k)
    by_heap = top_k_heap(scores, k)

    print(f"Scores: {scores}")
    print(f"Top-{k} (sorted): {by_sort}")
    print(f"Top-{k} (heap)  : {by_heap}")

    #Top K indices
    indexed = sorted(enumerate(scores), key=lambda x: -x[1])[:k]
    print(f"Top-{k} indices: {[i for i, _ in indexed]}")


#── Sorting-Based Greedy Algorithm ──────────────────────────────────────────────

def interval_scheduling(intervals: list[tuple[int, int]]) -> int:
    """Activity selection problem (Greedy sorted by finish time ascending).

    Return the maximum number of non-overlapping activities."""
    #Sort by end time in ascending order
    sorted_intervals = sorted(intervals, key=lambda x: x[1])
    count = 0
    last_end = -1
    for start, end in sorted_intervals:
        if start >= last_end:
            count += 1
            last_end = end
    return count


def demo_greedy() -> None:
    """Example of sorting-based greedy."""
    print("\n=== Sorting-Based Greedy (Activity Selection) ===")
    activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9),
                  (5, 9), (6, 10), (8, 11), (8, 12), (2, 14)]
    max_activities = interval_scheduling(activities)
    print(f"Activity List: {activities}")
    print(f"Maximum Number of Non-Overlapping Activities: {max_activities}")


#── Main ────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    demo_coordinate_compression()
    demo_deduplication()
    demo_top_k()
    demo_greedy()

    #Accuracy Verification
    assert coordinate_compression([5, 4, 2, 3, 1]) == [4, 3, 1, 2, 0]
    assert coordinate_compression([100, 50, 200, 50, 100]) == [1, 0, 2, 0, 1]
    print("Coordinate Compression Accuracy Check: PASS")

Assignment

sorted(), key, Applying multi-criteria sorting to practical problems.


List of issues

# problem Tier Core Technology
1 Baekjoon 1181 – Word Sorting Silver V Multi-criteria sorting + removing duplicates
2 Baekjoon 11650 – Coordinate Sorting Silver V Tuple sorting
3 Baekjoon 18870 – Coordinate Compression Silver II Coordinate compression + binary search

Problem 1: Baekjoon 1181 – Word Sorting (Silver V)

Problem Description

Sort N words based on the following criteria:

  1. Shorter words first
  2. If lengths are the same, in lexicographical order
  3. Remove duplicate words

Limitations

  • 1 ≤ N ≤ 20,000

Approach

words = set(words)                         #Remove duplicates
result = sorted(words, key=lambda w: (len(w), w))

Precautions

  • Duplicates should be removed in the input first
  • Possible with sorted(set(...)) at once

Problem 2: Baekjoon 11650 – Coordinate Sorting (Silver V)

Problem Description

Sort N 2D coordinates in x→y order.

Approach

coords = [tuple(map(int, input().split())) for _ in range(n)]
coords.sort()    #Basic tuple comparison: if x is equal, then compare y

Precautions

  • Both sorted() or .sort() are possible
  • Since the default tuple comparison is in (x, y) order, OK without a key

Problem 3: Baekjoon 18870 – Coordinate Compression (Silver II)

Problem Description

Convert N numbers to relative ranks (0-indexed) and print.

Example: [5, 4, 2, 3, 1] → [4, 3, 1, 2, 0]

Limitations

  • 1 ≤ N ≤ 1,000,000
  • -10⁹ ≤ number ≤ 10⁹

Approach

# O(n log n)
sorted_unique = sorted(set(arr))
rank = {v: i for i, v in enumerate(sorted_unique)}
result = [rank[v] for v in arr]

Binary search method (bisect)

from bisect import bisect_left
sorted_unique = sorted(set(arr))
result = [bisect_left(sorted_unique, v) for v in arr]

Precautions

  • Since there may be duplicates, it is essential to remove them using set()
  • Since there are values larger than 100 million, they cannot be used as array indexes

Checklist before submission

  • 1181: Have duplicate words been removed?
  • 1181: When lengths are the same, are they sorted lexicographically?
  • 11650: Have you optimized input/output speed using sys.stdin?
  • 18870: Is memory and time sufficient for N=1,000,000?
  • 18870: Are duplicate values being handled correctly?

Model answer

See the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

boj_11650.pyPython
"""Baekjoon 11650 – Sorting Coordinates
https://www.acmicpc.net/problem/11650
Time  : O(n log n)
Space : O(n)

Strategy: Tuple (x, y) default comparison sorting"""

import sys
input = sys.stdin.readline


def main() -> None:
    n = int(input())
    coords = [tuple(map(int, input().split())) for _ in range(n)]
    coords.sort()      #Basic comparison of (x, y) tuples: if x is equal, compare y
    out = '\n'.join(f"{x} {y}" for x, y in coords)
    print(out)


if __name__ == "__main__":
    main()
boj_1181.pyPython
"""Baekjoon 1181 – Word Sorting
https://www.acmicpc.net/problem/1181
Time  : O(n log n)
Space : O(n)

Strategy: Remove duplicates first, then sort using multiple criteria (length, lexicographical order)"""

import sys
input = sys.stdin.readline


def main() -> None:
    n = int(input())
    words = {input().rstrip() for _ in range(n)}   #Remove duplicates with set
    result = sorted(words, key=lambda w: (len(w), w))
    print('\n'.join(result))


if __name__ == "__main__":
    main()
boj_18870.pyPython
"""Baekjoon 18870 – Coordinate Compression
https://www.acmicpc.net/problem/18870
Time  : O(n log n)
Space : O(n)

Strategy: Remove duplicates → Sort → Convert to rank dictionary in O(1)"""

import sys
input = sys.stdin.readline


def main() -> None:
    n = int(input())
    arr = list(map(int, input().split()))

    #Remove duplicates + sort → map ranking
    sorted_unique = sorted(set(arr))
    rank = {v: i for i, v in enumerate(sorted_unique)}

    print(' '.join(str(rank[v]) for v in arr))


if __name__ == "__main__":
    main()

Learn this lesson with a mentor

Work through the Python Algorithms & Data Structures track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now