1 of 32

CSE 373 SP25 Section AX Week 6

Sorting Algorithms

2 of 32

Leetcode Preview - Top K Frequent Elements

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example:

Input: nums = [1,1,1,2,2,3], k = 2

Output: [1,2]

Constraints:

1 <= nums.length <= 10^5

k is in the range [1, number of unique elements in the array]

It is guaranteed that the answer is unique (there is only one possible output)

💡Start thinking about how you would approach this problem💡

3 of 32

Exam Details

  • Two-Stage Exam 2 Tomorrow in CSE2 G20 at 12:30 PM!
    • Any content from 03/31 to 05/08 is potentially relevant for this quiz.
    • Check your email for assigned seating by today!
    • 10 A4 pages of notes are allowed, bring a pencil/pen and Student ID too.
    • Exam Prep? Practice Exam! Lecture Qs! Section Qs! Prework!
    • 25 minutes individual, 25 minutes group
    • Sentence suggestions!

4 of 32

Micro-Teach: Sorting

5 of 32

Merge Sort

  • Divide & conquer algorithm
    • splits the array into halves, recursively sorts each half, and then merges the sorted halves.
  • Key steps:
    • Divide the array into two halves.
    • Recursively sort each half.
    • Merge the two sorted halves into a single sorted array.

6 of 32

Merge Sort

  • Time Complexity:
    • Best, Average, Worst Case: O(n log n)
    • due to the splitting and merging process.
  • Space Complexity:
    • O(n) due to the extra space

needed to store the subarrays

during merging.

7 of 32

Quick Sort

  • Divide & conquer algorithm
    • picks a "pivot" element and partitions the array into elements less than the pivot and elements greater than the pivot, recursively sorting each partition.
  • Key steps:
    • Pick a Pivot (commonly the first, last, or median element).
    • Partition: Reorganize the array so that elements less than the pivot come before it and elements greater come after.
    • Recursively apply Quicksort to the subarrays.

8 of 32

Quick Sort

9 of 32

QuickSort

  • Time Complexity
    • Best/Average Case: O(n log n) when the partition is balanced.
    • Worst Case: O(n²) if the pivot always divides poorly (e.g., when the array is already sorted and we always pivot around leftmost element).
  • Space Complexity
    • Typically O(log n) for recursive calls, but can grow to O(n) for recursive calls in the worst case analysis.

10 of 32

Counting Sort

  • Non-comparative sorting algorithm:
    • sorts an array by counting the number of occurrences of each unique element.
  • Key steps:
    • Count Frequencies: Count the number of occurrences of each unique element.
    • Accumulate Counts: Modify the count array to hold the position of elements in the sorted array.
    • Place Elements: Traverse the input array and place each element in its correct position using the count array.

11 of 32

Counting Sort

  • Time Complexity: O(n + k), where n is the number of elements and k is the range of input values.
  • Space Complexity: O(k) for the count array.

6

12 of 32

Radix Sort

  • Non-comparative sorting algorithm
    • processes each digit (or bit) of the numbers from least significant to most significant, using a stable sub-sorting algorithm like counting sort.
  • Key steps:
    • Start from Least Significant Digit (LSD) or Most Significant Digit (MSD).
    • Sort by current digit using counting sort.
    • Repeat for next digit.

13 of 32

Radix Sort

Sorting the integers according to units, tens, and hundreds place digits (LSD)

14 of 32

Radix Sort

  • Time Complexity: O(l * (n + r)), where
    • l is the number of digits,
    • n is the number of elements,
    • and r is the range of the counting sort.
  • Space Complexity: O(n + r)
    • for the auxiliary array used by the counting sort.

15 of 32

In-Place vs. Out-of-Place Algorithms

An algorithm is considered in-place if it rearranges elements within the original data structure without requiring extra space proportional to the input size. It only uses a constant amount of extra memory (e.g., pointers, temporary variables)

  • Example: Insertion Sort
  • Advantages:
    • Memory Efficient
  • Disadvantages:
    • Slow O(N^2)
    • Only some algorithms can be made in-place easily

16 of 32

In-Place vs. Out-of-Place Algorithms

An out-of-place algorithm requires extra space to store a copy of the input or intermediate results. The input array remains unchanged, and the output is written to a new array or data structure.

  • Example: Merge sort
  • Advantages:
    • Simpler to Implement
    • Safe for Parallel Processing
  • Disadvantages
    • Space Complexity
    • Overhead of Copying Data

17 of 32

1. Sorting Hat

Suppose we sort an array of numbers, but it turns out every element of the array is the same, e.g., {13, 13, 13, …, 13}.

What is the asymptotic runtime for each sorting algorithm below? (in big-Θ notation)

  1. Insertion sort
  2. Selection sort
  3. Merge sort
  4. Quicksort

18 of 32

1. Sorting Hat (Solution)

Suppose we sort an array of numbers, but it turns out every element of the array is the same, e.g., {13, 13, 13, …, 13}.

What is the asymptotic runtime for each sorting algorithm below? (in big-Θ notation)

  1. Insertion sort: Θ(N), one pass through the data without shifting.
  2. Selection sort: Θ(N2), same runtime regardless of the nature of data.
  3. Merge sort: Θ(N log N), same runtime regardless of the nature of data.
  4. Quicksort: Θ(N2), all elements are going to fall to the same side of the pivot, essentially sorting one element per iteration.

19 of 32

2. PartitionHybridSort

Give the worst-case runtime for each of the function calls below. Give your answer as a function of N, the size of the input array (in big-Θ notation). Assume input=small integers

Explain how each subsort contributes to each of your runtimes.

  1. PartitionHybridSort(arr, 0, N, InsertionSort) Θ( )
  2. PartitionHybridSort(arr, 0, N, Mergesort) Θ( )
  3. PartitionHybridSort(arr, 0, N, CountingSort) Θ( )
  4. PartitionHybridSort(arr, 0, N, PartitionHybridSort) Θ( )

// arr = array of integers

// lo = the lowest index in the array to be sorted

// hi = the highest index in the array to be sorted

// subsort = the sorting algorithm to use in Step 3

void PartitionHybridSort(arr, lo, hi, subsort) {

if (hi - lo <= 0) { // Step 1

return;

}

// Choose pivot element for partitioning, starting with arr[lo]

partition around arr[lo] of the current subproblem; // Step 2

// For future sorting runs, choose a new pivot element at index i (unknown)

sort the left from lo to i using subsort; // Step 3

sort the right from i + 1 to hi using subsort;

}

20 of 32

2. PartitionHybridSort (Solution)

PartitionHybridSort(arr, 0, N, InsertionSort) Θ( N2 )

All elements could be partitioned such that one of the partitions contains almost all the elements and the other contains a very small number of elements.

In this case, you run insertion sort on almost all elements, leading to quadratic runtime.

// arr = array of integers

// lo = the lowest index in the array to be sorted

// hi = the highest index in the array to be sorted

// subsort = the sorting algorithm to use in Step 3

void PartitionHybridSort(arr, lo, hi, subsort) {

if (hi - lo <= 0) { // Step 1

return;

}

// Choose pivot element for partitioning, starting with arr[lo]

partition around arr[lo] of the current subproblem; // Step 2

// For future sorting runs, choose a new pivot element at index i (unknown)

sort the left from lo to i using subsort; // Step 3

sort the right from i + 1 to hi using subsort;

}

21 of 32

2. PartitionHybridSort (Solution)

PartitionHybridSort(arr, 0, N, Mergesort) Θ( NlogN )

Step 2 results in linear runtime.

However, this is dominated by the log-linear runtime of merge sort.

// arr = array of integers

// lo = the lowest index in the array to be sorted

// hi = the highest index in the array to be sorted

// subsort = the sorting algorithm to use in Step 3

void PartitionHybridSort(arr, lo, hi, subsort) {

if (hi - lo <= 0) { // Step 1

return;

}

// Choose pivot element for partitioning, starting with arr[lo]

partition around arr[lo] of the current subproblem; // Step 2

// For future sorting runs, choose a new pivot element at index i (unknown)

sort the left from lo to i using subsort; // Step 3

sort the right from i + 1 to hi using subsort;

}

22 of 32

2. PartitionHybridSort (Solution)

PartitionHybridSort(arr, 0, N, CountingSort) Θ( N )

Since the inputs are small integers, CountingSort sorts the element in linear runtime.

Since step 2 also takes linear runtime, the combined worst-case runtime remains linear.

// arr = array of integers

// lo = the lowest index in the array to be sorted

// hi = the highest index in the array to be sorted

// subsort = the sorting algorithm to use in Step 3

void PartitionHybridSort(arr, lo, hi, subsort) {

if (hi - lo <= 0) { // Step 1

return;

}

// Choose pivot element for partitioning, starting with arr[lo]

partition around arr[lo] of the current subproblem; // Step 2

// For future sorting runs, choose a new pivot element at index i (unknown)

sort the left from lo to i using subsort; // Step 3

sort the right from i + 1 to hi using subsort;

}

23 of 32

2. PartitionHybridSort (Solution)

PartitionHybridSort(arr, 0, N, PartitionHybridSort) Θ( N2 )

Since the subsort is also PartitionHybridSort, this is just quicksort.

Worst case scenario is if our starting array is sorted highest to lowest when the end array we want is sorted from lowest to highest.

So the number of elements we have in our sub problem decreases by 1 each recursion (N-1) and we will have to recurse N times. T(N) = T(N-1) + N = Θ(N2).

// arr = array of integers

// lo = the lowest index in the array to be sorted

// hi = the highest index in the array to be sorted

// subsort = the sorting algorithm to use in Step 3

void PartitionHybridSort(arr, lo, hi, subsort) {

if (hi - lo <= 0) { // Step 1

return;

}

// Choose pivot element for partitioning, starting with arr[lo]

partition around arr[lo] of the current subproblem; // Step 2

// For future sorting runs, choose a new pivot element at index i (unknown)

sort the left from lo to i using subsort; // Step 3

sort the right from i + 1 to hi using subsort;

}

24 of 32

2. PartitionHybridSort (Solution Summary)

Give the worst-case runtime for each of the function calls below. Give your answer as a function of N, the size of the input array (in big-Θ notation). Assume input=small integers

Explain how each subsort contributes to each of your runtimes.

  1. PartitionHybridSort(arr, 0, N, InsertionSort) Θ( N2 )
  2. PartitionHybridSort(arr, 0, N, Mergesort) Θ( NlogN )
  3. PartitionHybridSort(arr, 0, N, CountingSort) Θ( N )
  4. PartitionHybridSort(arr, 0, N, PartitionHybridSort) Θ( N2 )

// arr = array of integers

// lo = the lowest index in the array to be sorted

// hi = the highest index in the array to be sorted

// subsort = the sorting algorithm to use in Step 3

void PartitionHybridSort(arr, lo, hi, subsort) {

if (hi - lo <= 0) { // Step 1

return;

}

// Choose pivot element for partitioning, starting with arr[lo]

partition around arr[lo] of the current subproblem; // Step 2

// For future sorting runs, choose a new pivot element at index i (unknown)

sort the left from lo to i using subsort; // Step 3

sort the right from i + 1 to hi using subsort;

}

25 of 32

Any Questions?

26 of 32

Code Challenges: Heaps/Hashing

27 of 32

Instructions

Each group of 3 or 4 students will be assigned the following problem:

  • LeetCode 347. Top K Frequent Elements

Different Approaches to Use:

  • Brute Force
  • HashMap & MinHeap
  • HashMap & Sorting
  • HashMap & TreeMap�

For instructions, search online for the problem by ID number, e.g. “LeetCode 347”.

We will have about 20 minutes for this activity:

  • Groups will get together and work on coming up with a general solution approach to the problem for 15 minutes
  • TAs will go over a sample solution for the last 5 minutes of the activity

NO NEED to write up code for this activity. Try to come up with solution approaches and how you would tackle the program on the high-level using specific data structures.

28 of 32

Different Approaches Available - Leetcode 347

  • Brute Force
  • HashMap & Priority Queue
  • HashMap & Sorting
  • HashMap & TreeMap

29 of 32

Presentation Guidelines

Questions to Ask Yourself:

  • What is the problem really asking?
  • What operations do I need to perform often?
  • In the examples, are there patterns that can help getting started?
  • When should I use specific data structures?

Presentation Guidelines

  • Explain how you approached the problem
  • Run through how you eliminated other options
  • Describe the data structure(s) and algorithm(s) you chose and why
  • Go through your solution step by step, explaining the logic

Complete the reflection activity in Gradescope and add your groupmates!

30 of 32

Example Solution 1 - LeetCode 347 (HashMap & Priority Queue)

class Solution {

public int[] topKFrequent(int[] nums, int k) {

Map<Integer, Integer> counter = new HashMap<>();

for (int n : nums) {

counter.put(n, counter.getOrDefault(n, 0) + 1);

}

PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>(

(a, b) -> Integer.compare(b.getValue(), a.getValue())

);

for (Map.Entry<Integer, Integer> entry : counter.entrySet()) {

heap.offer(entry);

}

int[] res = new int[k];

for (int i = 0; i < k; i++) {

res[i] = Objects.requireNonNull(heap.poll()).getKey();

}

return res;

}

}

31 of 32

Example Solution 2 - LeetCode 347 (HashMap & Bucket-Sort)

class Solution {

public int[] topKFrequent(int[] nums, int k) {

Map<Integer, Integer> counter = new HashMap<>();

for (int n : nums) {

counter.put(n, counter.getOrDefault(n, 0) + 1);

}

List<Integer>[] freq = new ArrayList[nums.length + 1];

for (int i = 0; i < freq.length; i++) {

freq[i] = new ArrayList<>();

}

for (Map.Entry<Integer, Integer> entry : counter.entrySet()) {

int frequency = entry.getValue();

freq[frequency].add(entry.getKey());

}

int[] res = new int[k];

int idx = 0;

for (int i = freq.length - 1; i >= 0; i--) {

for (int num : freq[i]) {

res[idx++] = num;

if (idx == k) {

return res;

}

}

}

return new int[0];

}

}

32 of 32

Closing Announcements

  • Thanks for coming to section! Best of luck on Exam 2 Tomorrow! Stay safe, stay hydrated, and get lots of sleep! :)
  • Look out for Priority Queues Code + Analysis Deadlines next week! (Allocate considerable time for OptimizedHeapMinPQ)

Please don't hesitate to reach out if you have any

questions or concerns about the course, or if there's

anything else we can help you with! We're here to support

you however we can!