---
# System prepended metadata

title: Week 11 - Heaps

---

# Week 11 - Heaps

## Team

Team name: Gappies van andere wappies
Date: 16/6/2022

Members

| Role                                                                                                                                                                          | Name |
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|
| **Facilitator** keeps track of time, assigns tasks and makes sure all the group members are heard and that decisions are agreed upon.                                             |Steohan|
| **Spokesperson** communicates group’s questions and problems to the teacher and talks to other teams; presents the group’s findings.                                              |Ingmar|
| **Reflector** observes and assesses the interactions and performance among team members. Provides positive feedback and intervenes with suggestions to improve groups’ processes. |Ingmar|
| **Recorder** guides consensus building in the group by recording answers to questions. Collects important information and data.                                                   |Stijn|

## Activities
Make sure to have the activities signed off regularly to ensure progress is tracked.

Download the provided project and open it in CLion. **NOTE**: in this week you will have to reuse the code of last week. Follow the instructions given in the `main.cpp` file.

### Activity 1: Comparing different data structures ✅

| Data structure     | Find-max | Delete-Max | Insert |
|--------------------|:--------:|:----------:|:------:|
| Sorted array       | O(1)     | O(1)       | O(n)       |
| Sorted linked list | O(1)     | O(1)       | O(n)       |
| Balanced BST       | O(log n) | O(log n)   | O(log n)|

Best: Sorted array/ sorted linked list

### Activity 2: Identify valid heaps ✅

1st tree: Min-heap
2nd tree: Not a heap. Because if you look at the path from the root to the bottom left node, it is not sorted. So the heap invariant does not hold true.
3rd tree: Min-heap

### Activity 3: Do it yourself ✅


1st swap: 12 - 3
2nd swap: 12 - 11

After these two swaps, the parent value is higher than 12 so the path is not sorted again. This took 2 swaps.

### Activity 4: Worst-case time complexities ✅

| Data structure  | Find-max | Delete-Max | Insert | Find |
|-----------------|:--------:|:----------:|:------:|:----:|
| Balanced BST    | O(log n) | O(log n)   |O(log n)|      O(log n) |
| Binary max-heap | O(1)     | O(log n)   |O(log n)| O(n) |

### Activity 5: Index computations ✅

```c++
size_t maxheap::parent_index(size_t index) {
    // Activity 5
    return floor(((double)index - 1) / 2);
}

size_t maxheap::left_child_index(size_t index) {
    // Activity 5
    size_t left_child = index * 2 + 1;
    if(left_child > size() - 1) {
        return npos;
    }

    return left_child;
}

size_t maxheap::right_child_index(size_t index) {
    // Activity 5
    size_t right_child = index * 2 + 2;
    if(right_child > size() - 1) {
        return npos;
    }

    return right_child;
}
```

Executing this code outputs:

```
test_left_child_index PASS
test_right_child_index PASS
test_parent_index PASS
```

### Activity 6: Implement the find-max operation ✅

```c++
const task &maxheap::maximum() const {
    // Activity 6
    return m_values[0];
}
```

The max value is simply the first index.

### Activity 7: Bubbling up ✅

```c++
size_t maxheap::bubble_up(size_t index) {
    // Activity 7
    // while the value at the given index is greater than its parent value,
    // bubble it up
    // using the functions swap and parent_index

    size_t swaps = 0;
    size_t p_index;
    int priority;
    int parent_priority;

    do {
        p_index = parent_index(index);

        // Make sure there is a parent node.
        if(p_index != npos) {
            priority = m_values[index].priority;
            parent_priority = m_values[p_index].priority;

            if(priority > parent_priority) {
                swap(index, p_index);
                swaps += 1;

                // Swap indices
                index = p_index;
            }
        }
    } while(priority > parent_priority && p_index != npos);

    return swaps;
}
```

### Activity 8: Bubbling down ✅

```c++
size_t maxheap::bubble_down(size_t index) {
    size_t swaps = 0;
    auto currentIndex = index;
    auto leftChildIndex = left_child_index(currentIndex);
    auto rightChildIndex = right_child_index(currentIndex);
    while (m_values[currentIndex] <  m_values[leftChildIndex] || m_values[currentIndex] < m_values[rightChildIndex]) {
        if (std::max(m_values[leftChildIndex].priority, m_values[rightChildIndex].priority) == m_values[leftChildIndex].priority) {
            swap(currentIndex, left_child_index(currentIndex));
            swaps++;
            currentIndex = left_child_index(currentIndex);
            leftChildIndex = left_child_index(currentIndex);
            rightChildIndex = right_child_index(currentIndex);
            continue;
        } else {
            swap(currentIndex, right_child_index(currentIndex));
            swaps++;
            currentIndex = right_child_index(currentIndex);
            leftChildIndex = left_child_index(currentIndex);
            rightChildIndex = right_child_index(currentIndex);
        }
    }
    return swaps;
}
```

### Activity 9: Implement heapify ✅

```c++
size_t maxheap::heapify() {
    size_t swaps = 0;
    if (!this->m_values.empty()) {
        swaps = heapifyTree(0);
    }
    return swaps;
}

size_t maxheap::heapifyTree(size_t index) {
    size_t swaps{0};
    auto biggestIndex = index;
    auto leftChildIndex = left_child_index(index);
    auto rightChildIndex = right_child_index(index);

    if (!invariantHolds(leftChildIndex)) {
        swaps += heapifyTree(leftChildIndex);
    }
    if (!invariantHolds(rightChildIndex)) {
        swaps +=heapifyTree(rightChildIndex);
    }

    if (leftChildIndex != npos && this->m_values[leftChildIndex] > this->m_values[biggestIndex]) {
        biggestIndex = leftChildIndex;
    } else if (rightChildIndex != npos && this->m_values[rightChildIndex] > this->m_values[biggestIndex]) {
        biggestIndex = rightChildIndex;
    }
    if (biggestIndex != index) {
        swaps += bubble_down(index);
    }

    return swaps;
}

bool maxheap::invariantHolds (size_t i) {
    auto leftChildIndex = left_child_index(i);
    auto rightChildIndex = right_child_index(i);
    if (i == npos) {
        return true;
    }
    if (leftChildIndex != npos && m_values[leftChildIndex] > m_values[i]) {
        return false;
    } else if (rightChildIndex != npos && m_values[rightChildIndex] > m_values[i]) {
        return false;
    }
    return true;
}
```

### Activity 10: Heapify - time complexity ✅

| Number of values | Number of swaps  |
|------------------|:----------------:|
| 5                |      3           |
| 10               |      8           |
| 20               |      17          |
| 50               |      47          |
| 100              |      96          |
| 200              |      195         |
| 300              |      295         |
| 400              |      394         |
| 500              |      493         |
| 1000             |      992         |


As you can see the number of swaps is almost exactly the number of values, so the worst case time complexity is O(n)

### Activity 11: Bubbling down vector elements ✅
```c++
size_t left_child_index(std::vector<int> &numbers, size_t index) {
    size_t left_child = index * 2 + 1;
    if(left_child > numbers.size() - 1) {
        return std::string::npos;
    }

    return left_child;
}

size_t right_child_index(std::vector<int> &numbers, size_t index) {
    size_t right_child = index * 2 + 2;
    if(right_child > numbers.size() - 1) {
        return std::string::npos;
    }

    return right_child;
}

void swap(std::vector<int> &numbers, size_t first_index, size_t second_index) {
    std::swap(numbers[first_index], numbers[second_index]);
}

void bubble_down(std::vector<int> &values, size_t index, size_t count) {
    auto currentIndex = index;
    auto leftChildIndex = left_child_index(values, currentIndex);
    auto rightChildIndex = right_child_index(values, currentIndex);
    while ((values[currentIndex] <  values[leftChildIndex] || values[currentIndex] < values[rightChildIndex])) {
        if (leftChildIndex >= count) break;
        if (std::max(values[leftChildIndex], values[rightChildIndex]) == values[leftChildIndex]
        || rightChildIndex == count
        || rightChildIndex == std::string::npos) {
            if (leftChildIndex == std::string::npos) break;
            swap(values, currentIndex, left_child_index(values, currentIndex));
            currentIndex = left_child_index(values, currentIndex);
            leftChildIndex = left_child_index(values, currentIndex);
            rightChildIndex = right_child_index(values, currentIndex);
            continue;
        } else {
            if (rightChildIndex == std::string::npos) break;
            swap(values, currentIndex, right_child_index(values, currentIndex));
            currentIndex = right_child_index(values, currentIndex);
            leftChildIndex = left_child_index(values, currentIndex);
            rightChildIndex = right_child_index(values, currentIndex);
        }
    }
}

bool invariantHolds (std::vector<int> &numbers, size_t i) {
    auto leftChildIndex = left_child_index(numbers,i);
    auto rightChildIndex = right_child_index(numbers, i);
    if (i == std::string::npos) {
        return true;
    }
    if (leftChildIndex != std::string::npos && numbers[leftChildIndex] > numbers[i]) {
        return false;
    } else if (rightChildIndex != std::string::npos && numbers[rightChildIndex] > numbers[i]) {
        return false;
    }
    return true;
}

void heapifyTree(std::vector<int> &numbers, size_t index) {
    auto biggestIndex = index;
    auto leftChildIndex = left_child_index(numbers, index);
    auto rightChildIndex = right_child_index(numbers, index);

    if (!invariantHolds(numbers, leftChildIndex)) {
        heapifyTree(numbers, leftChildIndex);
    }
    if (!invariantHolds(numbers, rightChildIndex)) {
        heapifyTree(numbers, rightChildIndex);
    }

    if (leftChildIndex != std::string::npos && numbers[leftChildIndex] > numbers[biggestIndex]) {
        biggestIndex = leftChildIndex;
    } else if (rightChildIndex != std::string::npos && numbers[rightChildIndex] > numbers[biggestIndex]) {
        biggestIndex = rightChildIndex;
    }
    if (biggestIndex != index && index < numbers.size()) {
        bubble_down(numbers, index, numbers.size());
    }
}

// Activity 11
void heapify(std::vector<int> &numbers) {
    if (!numbers.empty()) {
        heapifyTree(numbers, 0);
    }
}
```


### Activity 12: In-place heap sort ✅

```c++
void heap_sort(std::vector<int> &numbers) {
    heapify(numbers);
    auto end = numbers.size() - 1;
    while (end > 0) {
        if (numbers[0] > numbers[end]) std::swap(numbers[0], numbers[end]);
        end--;
        bubble_down(numbers, 0, end);
    }
}
```

### Activity 13: General heap sort ✅

```c++
template <typename T>
size_t left_child_index(std::vector<T> &numbers, size_t index) {
    size_t left_child = index * 2 + 1;
    if(left_child > numbers.size() - 1) {
        return std::string::npos;
    }

    return left_child;
}

template <typename T>
size_t right_child_index(std::vector<T> &numbers, size_t index) {
    size_t right_child = index * 2 + 2;
    if(right_child > numbers.size() - 1) {
        return std::string::npos;
    }

    return right_child;
}

template <typename T>
void swap(std::vector<T> &numbers, size_t first_index, size_t second_index) {
    std::swap(numbers[first_index], numbers[second_index]);
}

template <typename T>
void bubble_down(std::vector<T> &values, size_t index, size_t count) {
    auto currentIndex = index;
    auto leftChildIndex = left_child_index(values, currentIndex);
    auto rightChildIndex = right_child_index(values, currentIndex);
    while ((values[currentIndex] <  values[leftChildIndex] || values[currentIndex] < values[rightChildIndex])) {
        if (leftChildIndex >= count) break;
        if (std::max(values[leftChildIndex], values[rightChildIndex]) == values[leftChildIndex]
        || rightChildIndex == count
        || rightChildIndex == std::string::npos) {
            if (leftChildIndex == std::string::npos) break;
            swap(values, currentIndex, left_child_index(values, currentIndex));
            currentIndex = left_child_index(values, currentIndex);
            leftChildIndex = left_child_index(values, currentIndex);
            rightChildIndex = right_child_index(values, currentIndex);
            continue;
        } else {
            if (rightChildIndex == std::string::npos) break;
            swap(values, currentIndex, right_child_index(values, currentIndex));
            currentIndex = right_child_index(values, currentIndex);
            leftChildIndex = left_child_index(values, currentIndex);
            rightChildIndex = right_child_index(values, currentIndex);
        }
    }
}

template <typename T>
bool invariantHolds (std::vector<T> &numbers, size_t i) {
    auto leftChildIndex = left_child_index(numbers,i);
    auto rightChildIndex = right_child_index(numbers, i);
    if (i == std::string::npos) {
        return true;
    }
    if (leftChildIndex != std::string::npos && numbers[leftChildIndex] > numbers[i]) {
        return false;
    } else if (rightChildIndex != std::string::npos && numbers[rightChildIndex] > numbers[i]) {
        return false;
    }
    return true;
}

template <typename T>
void heapifyTree(std::vector<T> &numbers, size_t index) {
    auto biggestIndex = index;
    auto leftChildIndex = left_child_index(numbers, index);
    auto rightChildIndex = right_child_index(numbers, index);

    if (!invariantHolds(numbers, leftChildIndex)) {
        heapifyTree(numbers, leftChildIndex);
    }
    if (!invariantHolds(numbers, rightChildIndex)) {
        heapifyTree(numbers, rightChildIndex);
    }

    if (leftChildIndex != std::string::npos && numbers[leftChildIndex] > numbers[biggestIndex]) {
        biggestIndex = leftChildIndex;
    } else if (rightChildIndex != std::string::npos && numbers[rightChildIndex] > numbers[biggestIndex]) {
        biggestIndex = rightChildIndex;
    }
    if (biggestIndex != index && index < numbers.size()) {
        bubble_down(numbers, index, numbers.size());
    }
}

template <typename T>
void heapify(std::vector<T> &numbers) {
    if (!numbers.empty()) {
        heapifyTree(numbers, 0);
    }
}

template <typename T>
void heap_sort(std::vector<T> &numbers) {
    heapify(numbers);
    auto end = numbers.size() - 1;
    while (end > 0) {
        if (numbers[0] > numbers[end]) std::swap(numbers[0], numbers[end]);
        end--;
        bubble_down(numbers, 0, end);
    }
}

```

## Looking back

### What we've learnt

Which heaps there were, and the advantages of each of them.

### What were the surprises

That heaps could be used in different ways.

### What problems we've encountered

There were no big problems that we encountered.

### What was or still is unclear

Nothing, the things we didn't know we asked in class.

### How did the group perform?

Very well as always. :)





