THE SYLLABUS TASK
What you need to solve
Implement Quadratic Probing and display the hash table after each insertion.
n (0–100), then n nonnegative keys. Table size 11; home=k%11, probes=(home+i²)%11 for i=0…10. Duplicates are ignored; insertion can fail even when some slots are empty.
UNDERSTAND THE IDEA
Explanation
Quadratic probing changes offsets from 0,1,2,3… to 0,1,4,9…. This spreads keys away from one consecutive run.
For this fixed formula, not every table slot is reachable from every home index. The program bounds attempts and reports exhaustion of that key’s probe sequence.
Keys with the same home position still follow the same sequence, causing secondary clustering. This is distinct from linear probing’s contiguous primary clustering.
TURN THE IDEA INTO STEPS
Step-by-step algorithm
- Compute the home index.
- Evaluate home+i² modulo 11 for bounded attempts.
- Return duplicate status for an equal occupied key.
- Insert at the first reachable empty slot.
- If the sequence offers none, report failure and show the unchanged table.
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.
Bounded probing: h+i*i
#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 SIZE 11
int table[SIZE], occupied[SIZE];
int insert(int key) {
int base = key % SIZE;
for (int i = 0; i < SIZE; ++i) {
int index = (base + i * i) % SIZE;
if (occupied[index] && table[index] == key) return 2;
if (!occupied[index]) { occupied[index] = 1; table[index] = key; return 1; }
}
return 0;
}
void display(void) {
printf("Table:");
for (int i = 0; i < SIZE; ++i) {
if (occupied[i]) printf(" %d:%d",i,table[i]); else printf(" %d:_",i);
}
putchar('\n');
}
/* n (0..100), then n nonnegative keys. Display after every insertion attempt. */
int main(void) {
int n, key; if (!readInt(&n) || n < 0 || n > 100) return invalid();
if (!n) display();
for (int i = 0; i < n; ++i) {
if (!readInt(&key) || key < 0) return invalid();
int result = insert(key);
if (result == 2) puts("Duplicate ignored.");
else if (!result) puts("Insertion failed: probe sequence has no free slot.");
else puts("Inserted.");
display();
}
return 0;
}
Local compile: gcc -std=c17 quadratic-probing.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–31Keep occupancy separate from stored integer values
#define SIZE 11
int table[SIZE], occupied[SIZE];The table has 11 slots. Global occupied flags start at zero, so key 0 is a valid stored key rather than an empty-slot sentinel.
Lines 32–33Start insertion at the primary hash index
int insert(int key) {
int base = key % SIZE;base = key % SIZE gives an index from 0 through 10 for the nonnegative input keys. The helper returns 2 for duplicate, 1 for insertion and 0 when no probed slot is available.
Lines 35–36Compute at most eleven candidate positions
for (int i = 0; i < SIZE; ++i) {
int index = (base + i * i) % SIZE;(base + i*i) % SIZE uses squared offsets. Some indexes repeat, so this sequence can fail even when other table slots are empty. Bounding the loop by SIZE guarantees termination.
Lines 37–38Detect a duplicate or fill an empty slot
if (occupied[index] && table[index] == key) return 2;
if (!occupied[index]) { occupied[index] = 1; table[index] = key; return 1; }If the occupied slot already stores key, return 2. If it is empty, set occupied, store key and return 1. No deletion operation is provided, so there are no tombstones to handle.
Lines 39–41Report that this probe sequence found no free position
}
return 0;
}After all attempts, return 0. For quadratic probing this does not prove that the whole table is full.
Lines 42–48Display every physical slot and its occupancy
void display(void) {
printf("Table:");
for (int i = 0; i < SIZE; ++i) {
if (occupied[i]) printf(" %d:%d",i,table[i]); else printf(" %d:_",i);
}
putchar('\n');
}Print index:key for occupied entries and index:_ for empty ones, then a newline. The separate occupancy array makes zero keys display correctly.
Lines 49–52Read an insertion count and display an initially empty table if needed
/* n (0..100), then n nonnegative keys. Display after every insertion attempt. */
int main(void) {
int n, key; if (!readInt(&n) || n < 0 || n > 100) return invalid();
if (!n) display();Accept n from 0 to 100. For n = 0, display the empty table even though the insertion loop will have no iterations.
Lines 53–55Read one nonnegative key and attempt insertion
for (int i = 0; i < n; ++i) {
if (!readInt(&key) || key < 0) return invalid();
int result = insert(key);Validate the key before computing its remainder. Save the helper’s status so the next branch can distinguish a duplicate from probe failure.
Lines 56–60Report the attempt and display the resulting table
if (result == 2) puts("Duplicate ignored.");
else if (!result) puts("Insertion failed: probe sequence has no free slot.");
else puts("Inserted.");
display();
}Print Duplicate ignored, the probe-failure message, or Inserted according to the result. Always show the table after this attempt, even if the key was not added.
Lines 61–62Finish after all requested attempts
return 0;
}Return success after processing n valid key tokens. An insertion failure is reported as an operation result rather than a malformed-input failure.
FOLLOW THE VALUES
Dry run
- For keys [0,11,22,33,44,55], home is always zero.
- The first six distinct reachable offsets are 0,1,4,9,5,3.
- All six keys can occupy those slots.
- Another same-home key 66 fails although indexes 2,6,7,8,10 remain empty.
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
4
23 34 45 27
Inserted.
Table: 0:_ 1:23 2:_ 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:_ 1:23 2:34 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:_ 1:23 2:34 3:_ 4:_ 5:45 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:_ 1:23 2:34 3:_ 4:_ 5:45 6:27 7:_ 8:_ 9:_ 10:_
Sample 2
7
0 11 22 33 44 55 66
Inserted.
Table: 0:0 1:_ 2:_ 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:_ 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:_ 3:_ 4:22 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:_ 3:_ 4:22 5:_ 6:_ 7:_ 8:_ 9:33 10:_
Inserted.
Table: 0:0 1:11 2:_ 3:_ 4:22 5:44 6:_ 7:_ 8:_ 9:33 10:_
Inserted.
Table: 0:0 1:11 2:_ 3:55 4:22 5:44 6:_ 7:_ 8:_ 9:33 10:_
Insertion failed: probe sequence has no free slot.
Table: 0:0 1:11 2:_ 3:55 4:22 5:44 6:_ 7:_ 8:_ 9:33 10:_
Sample 3
4
1 1 12 23
Inserted.
Table: 0:_ 1:1 2:_ 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Duplicate ignored.
Table: 0:_ 1:1 2:_ 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:_ 1:1 2:12 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:_ 1:1 2:12 3:_ 4:_ 5:23 6:_ 7:_ 8:_ 9:_ 10:_
Common mistakes
- Do not describe probe exhaustion as proof that the entire table is full.
- A prime size alone does not make i² visit every slot.
- Use a bounded loop even when the sequence revisits earlier slots.
WHY THIS GROWTH RATE?
Time and space complexity
Worst O(m) insertion attempts; output O(m) per attempt; stored table O(m).
The loop limits attempts to m, which bounds time even when quadratic residues repeat.
The number of distinct reachable slots depends on the formula and table size. A prime table with i² probing guarantees a reachable empty slot while fewer than half the slots are occupied, but not full-table coverage.
Expected performance depends on load and hashing assumptions. Printing all slots adds O(m) per attempted insertion; m is fixed at 11 in this sample.
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 can quadratic insertion fail while the table has empty slots?
For h+i² modulo 11, only certain residues are reachable from a given home. If those slots are occupied, unrelated empty slots cannot be used by that sequence.
What offsets appear first from home zero in this example?
They are 0,1,4,9,5,3 before residues repeat. The example is intentionally small enough for students to trace the limited coverage.
What is secondary clustering?
Keys with the same initial hash follow an identical quadratic probe sequence. They can still compete for the same slots even though those slots are not consecutive.
Does using a prime table size guarantee complete quadratic coverage?
No. With the specific i² rule, the prime-size benefit is a below-half-load insertion guarantee, not a guarantee that every slot is visited.
Why are attempts bounded even when repeated residues are possible?
A repeated sequence might never encounter an empty slot. A finite m-attempt loop reports failure rather than running indefinitely.
How does changing the quadratic formula change behavior?
Different coefficients or probing designs can produce different reachable slots and guarantees. The page states h+i² explicitly so its trace and claims match the implemented rule.