THE SYLLABUS TASK
What you need to solve
Implement a Binary Heap data structure and analyze its operations.
Commands: 1 value = insert; 2 = extract maximum; 3 = peek maximum; 4 = array display; 0 = exit. A max heap with capacity 100; duplicates and signed values are accepted.
UNDERSTAND THE IDEA
Explanation
A binary heap combines a complete binary-tree shape with a heap-order rule. In this max heap, each parent is at least as large as either child; siblings need not be sorted.
An array stores that complete tree compactly. For zero-based index i, parent is (i−1)/2, left child is 2i+1 and right child is 2i+2.
Insertion appends and sifts up. Extraction saves the root, moves the last value to index zero, then sifts down by choosing the larger existing child.
TURN THE IDEA INTO STEPS
Step-by-step algorithm
- Check capacity before appending a new value.
- Sift the value upward while it exceeds its parent.
- For extraction, reject an empty heap and save the root.
- Move the last value to the root and repeatedly swap with the larger child if needed.
- Display the array as tree level order; peek simply reads index zero.
SEE THE CONTROL FLOW
Flowcharts
Diamonds show decisions; labeled arrows select a branch. Returning arrows repeat work. Helper-function steps group the operations explained above and in the code. These charts focus on the named core operation. On a phone, scroll within the diagram or open the full-size version.
Max-heap extraction and sift-down
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
int ch; size_t used = 0; int fits = 1;
while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
if (ch == EOF) return 0;
do {
if (ch == 0) fits = 0;
if (used + 1 < capacity) text[used++] = (char)ch;
else fits = 0;
ch = getchar();
} while (ch != EOF && !isspace((unsigned char)ch));
text[used] = '\0';
return fits;
}
int readInt(int *value) {
char token[64], *end;
if (!readToken(token, sizeof token)) return 0;
errno = 0; long result = strtol(token, &end, 10);
if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;
*value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }
#define CAPACITY 100
int heap[CAPACITY], count = 0;
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
int insert(int value) {
if (count == CAPACITY) return 0;
int i = count++; heap[i] = value;
while (i > 0 && heap[(i - 1) / 2] < heap[i]) {
int parent = (i - 1) / 2; swap(&heap[i],&heap[parent]); i = parent;
}
return 1;
}
int extractMax(int *value) {
if (!count) return 0;
*value = heap[0]; heap[0] = heap[--count]; int i = 0;
while (2 * i + 1 < count) {
int child = 2 * i + 1;
if (child + 1 < count && heap[child + 1] > heap[child]) ++child;
if (heap[i] >= heap[child]) break;
swap(&heap[i],&heap[child]); i = child;
}
return 1;
}
/* 1 value: insert, 2: extract maximum, 3: peek, 4: array display, 0: exit. */
int main(void) {
int command, value;
while (readInt(&command)) {
if (!command) return 0;
if (command == 1) {
if (!readInt(&value)) return invalid();
puts(insert(value) ? "Inserted." : "Heap is full.");
} else if (command == 2) {
if (extractMax(&value)) printf("Extracted: %d\n",value); else puts("Empty heap.");
} else if (command == 3) {
if (count) printf("Maximum: %d\n",heap[0]); else puts("Empty heap.");
} else if (command == 4) {
printf("Heap array:"); for (int i = 0; i < count; ++i) printf(" %d",heap[i]); putchar('\n');
} else return invalid();
}
return invalid();
}
Local compile: gcc -std=c17 binary-heap.c -o program -lm
Open in C compiler transfers the source. Copy the sample input into the compiler’s input box separately before running.
UNDERSTAND THE CODE
Line-by-line explanation
Follow the exact source above, from input handling through the algorithm and its result. Line numbers count blank lines; related statements are grouped into steps. Start with main to follow execution, then open the functions it calls.
Start with main → · Algorithm and data structure →
Lines 1–5Include input, conversion and validation declarations
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>stdio.h supplies terminal input/output. stdlib.h declares conversion and allocation functions. string.h supplies string and memory utilities. ctype.h supplies isspace, and errno.h supplies conversion error reporting. Headers declare facilities; including them does not execute an operation.
Lines 7–9Define a complete-token reader and initialise its state
/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
int ch; size_t used = 0; int fits = 1;readToken receives a writable buffer and its capacity. ch is int so it can represent EOF as well as input characters. used counts stored characters, and fits starts as a success flag. One slot must remain for the null terminator.
Lines 10–11Skip leading whitespace and recognise end-of-file
while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
if (ch == EOF) return 0;Read until a non-whitespace character appears. The unsigned-char cast supplies an allowed value to isspace. Return 0 if EOF arrives before a token begins.
Lines 12–15Collect a token without silently truncating it
do {
if (ch == 0) fits = 0;
if (used + 1 < capacity) text[used++] = (char)ch;
else fits = 0;The do body handles the character already read. An embedded zero byte marks the token invalid. Store a character only while there is room for it and a null terminator; otherwise clear fits. Continue reading even after the buffer fills.
Lines 16–20Consume the full token and terminate the buffer
ch = getchar();
} while (ch != EOF && !isspace((unsigned char)ch));
text[used] = '\0';
return fits;
}Read through the next whitespace or EOF, add a null character after the stored prefix, then return fits. A too-long token returns 0 rather than pretending that its stored prefix is the complete token.
Lines 21–23Wrap token input as a checked integer read
int readInt(int *value) {
char token[64], *end;
if (!readToken(token, sizeof token)) return 0;value is an output address. A 64-slot local buffer receives the token; a failed token read returns 0 before attempting conversion.
Lines 24–25Convert base-ten text and check the whole result
errno = 0; long result = strtol(token, &end, 10);
if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;Reset errno before strtol. &end receives the stopping address. Reject conversion errors, no converted digits, leftover characters, or values outside -1,000,000 to 1,000,000. Thus 12x is not accepted as the integer 12.
Lines 26–28Return the accepted value or a standard error status
*value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }Store a validated integer through *value and return 1 from readInt. invalid is a separate helper that prints the error and returns 1 as main’s failure status; the same number has different roles in these two function contracts.
Lines 30–32Store a bounded max-heap in an array
#define CAPACITY 100
int heap[CAPACITY], count = 0;
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }The global array holds up to 100 items and count starts at zero. swap exchanges two integers through their addresses. For a max-heap each parent is at least as large as either child; the entire array is not globally sorted.
Lines 33–35Reject overflow or append the new value
int insert(int value) {
if (count == CAPACITY) return 0;
int i = count++; heap[i] = value;If the heap is full, return 0 before a write. Otherwise i is the old count, count increases, and the new value occupies the next position in the complete-tree shape.
Lines 36–40Sift the new value up through smaller parents
while (i > 0 && heap[(i - 1) / 2] < heap[i]) {
int parent = (i - 1) / 2; swap(&heap[i],&heap[parent]); i = parent;
}
return 1;
}For a non-root index, its parent is (i - 1) / 2. While the parent is smaller, swap the two and continue from the parent index. Return success after the max-heap order is restored.
Lines 41–43Remove the maximum and move the last item to the root
int extractMax(int *value) {
if (!count) return 0;
*value = heap[0]; heap[0] = heap[--count]; int i = 0;An empty heap returns failure. Otherwise copy heap[0] through value, decrease count and use the last item to replace the root. For a one-item heap the active count becomes zero and the following sift loop is skipped.
Lines 44–46Select the larger existing child
while (2 * i + 1 < count) {
int child = 2 * i + 1;
if (child + 1 < count && heap[child + 1] > heap[child]) ++child;The left child is 2*i + 1; continue only if it exists. If a right child also exists and is larger, choose that one so the parent can dominate both children after a swap.
Lines 47–51Sift downward until heap order is restored
if (heap[i] >= heap[child]) break;
swap(&heap[i],&heap[child]); i = child;
}
return 1;
}If the current parent is already at least as large as its larger child, stop. Otherwise swap and continue from the child. Return 1 after providing the extracted maximum.
Lines 52–56Document commands and read operation requests
/* 1 value: insert, 2: extract maximum, 3: peek, 4: array display, 0: exit. */
int main(void) {
int command, value;
while (readInt(&command)) {
if (!command) return 0;Commands support insert, extract maximum, peek, array display and exit. Command 0 returns success without heap-node cleanup because this representation uses a fixed array.
Lines 57–61Insert or extract with explicit status checks
if (command == 1) {
if (!readInt(&value)) return invalid();
puts(insert(value) ? "Inserted." : "Heap is full.");
} else if (command == 2) {
if (extractMax(&value)) printf("Extracted: %d\n",value); else puts("Empty heap.");Insertion requires a value and reports capacity failure. Extraction prints the output value only if extractMax succeeded, otherwise it reports an empty heap.
Lines 62–65Peek or show the heap array without modifying it
} else if (command == 3) {
if (count) printf("Maximum: %d\n",heap[0]); else puts("Empty heap.");
} else if (command == 4) {
printf("Heap array:"); for (int i = 0; i < count; ++i) printf(" %d",heap[i]); putchar('\n');Peek reads index 0 only when count is nonzero. Array display prints the active heap layout, not a sorted traversal. Neither operation removes an item.
Lines 66–69Reject unsupported or incomplete command input
} else return invalid();
}
return invalid();
}An unsupported command or an input stream ending before command 0 returns the standard failure message and status.
FOLLOW THE VALUES
Dry run
- Insert 10,20,15,30,40 to form heap array [40,30,15,10,20].
- Extract 40 and move 20 to the root.
- Swap 20 with larger child 30.
- The remaining array is [30,20,15,10], which satisfies max-heap order.
CHECK THE BEHAVIOUR
Sample input & output
These outputs were captured by running the exact C source shown above. Commands use the stated input format; the programs omit repeated menu prompts so the same sample can be pasted and run.
Sample 1
1 10
1 20
1 15
1 30
1 40
4
2
4
3
0
Inserted.
Inserted.
Inserted.
Inserted.
Inserted.
Heap array: 40 30 15 10 20
Extracted: 40
Heap array: 30 20 15 10
Maximum: 30
Sample 2
2
1 -5
1 -2
1 -2
2
2
2
2
0
Empty heap.
Inserted.
Inserted.
Inserted.
Extracted: -2
Extracted: -2
Extracted: -5
Empty heap.
Sample 3
1 8
3
2
4
0
Inserted.
Maximum: 8
Extracted: 8
Heap array:
Common mistakes
- Heap-array order is not sorted order.
- Choose the larger child for max-heap sift-down.
- Check empty extraction and capacity before any array access.
WHY THIS GROWTH RATE?
Time and space complexity
Insert/extract O(log n); peek O(1); display and arbitrary-key search O(n); heap storage O(n).
Completeness gives height O(log n). A sift follows at most one root-to-leaf or leaf-to-root path and does constant work per level.
Peek reads the root directly. A general value search can inspect all nodes because heap order does not impose BST-style left/right key ranges.
The sample reserves 100 array slots. Sifts are iterative and use O(1) additional space; total heap storage scales as O(n) in a growing-capacity model.
Big-O describes growth in the stated input quantity. The educational limits keep examples safe; complexity discussions state which bounds depend on a generalized input size. Arithmetic on the accepted native numeric types is treated as constant-time.
PREPARE FOR YOUR LAB VIVA
Related viva questions & answers
Try answering aloud, then expand the answer to check your reasoning. These questions focus on the concepts used in this program.
Why is a binary heap required to be complete?
Filling levels left to right allows an array to represent the tree without gaps. This also keeps the height logarithmic and makes index formulas sufficient for navigation.
Why does extraction replace the root with the final array value?
Removing the final leaf preserves completeness. Moving its value to the root leaves only heap order to repair by sifting downward.
Why must sift-down choose the larger of the two children?
The parent must dominate both children. Swapping with the smaller child could leave the larger child still above the new parent’s value.
Is the displayed heap array an ascending or descending sorted list?
No. It is level-order tree storage. Only parent-child order is constrained, so siblings and nodes in different subtrees may appear in either order.
How does a heap’s ordering differ from a BST’s ordering?
A max heap requires parents to be at least their children. A BST partitions all left and right subtree keys around each node, which supports directed key search.
Why is peek cheaper than extracting the maximum?
Peek only reads the root. Extraction changes the tree, replaces that root and may perform logarithmically many swaps to restore heap order.