Skip to lab content

WEEK 12 · ADVANCED DATA STRUCTURES LAB

Collision resolution — linear probing

B.Tech CSE & Allied Branches · II Year, I Semester · AR25C173 tested sample runs

THE SYLLABUS TASK

What you need to solve

Implement Linear Probing and display the hash table after each insertion.
Input format & conventions

n (0–100), then n nonnegative keys up to 1,000,000. Fixed table size 11. h(k)=k%11; probes (h+i)%11. Duplicate keys are ignored. An underscore denotes an empty slot, so zero is valid data.

UNDERSTAND THE IDEA

Explanation

Linear probing stores every key directly in the table. A collision checks the next slot, wrapping around at the end.

A separate occupied array distinguishes empty storage from a stored zero. Each insertion attempts at most 11 positions, so a full table cannot cause an infinite loop.

The table is printed after each insertion attempt, including ignored duplicates and failures. Deletion is not included, so no tombstone state is needed.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Compute the home slot key % 11.
  2. For i=0…10, inspect (home+i)%11.
  3. If that slot already holds the key, report a duplicate.
  4. If it is empty, store and mark the key; otherwise continue.
  5. Report bounded failure if no slot is free and print the 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

Bounded probing: h+i

Open full-size flowchart ↗

C17

Complete C program

Download .c
#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) % 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 linear-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) % SIZE;

(base + i) % SIZE checks consecutive slots, wrapping at the table boundary. 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. The full-cycle probe rule here has examined every slot.

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

  1. Keys [23,34,45] all hash to index 1.
  2. 23 occupies 1; 34 probes 1 then occupies 2.
  3. 45 probes 1,2 and occupies 3.
  4. The table after the third insertion contains 1:23, 2:34, 3:45.

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

INPUT
4
23 34 45 27
OUTPUT
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:45 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:_ 1:23 2:34 3:45 4:_ 5:27 6:_ 7:_ 8:_ 9:_ 10:_

Sample 2

INPUT
4
0 11 0 22
OUTPUT
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:_
Duplicate ignored.
Table: 0:0 1:11 2:_ 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_

Sample 3

INPUT
12
0 11 22 33 44 55 66 77 88 99 110 121
OUTPUT
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:22 3:_ 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:_ 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:_ 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:_ 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:66 7:_ 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:66 7:77 8:_ 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:66 7:77 8:88 9:_ 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:66 7:77 8:88 9:99 10:_
Inserted.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:66 7:77 8:88 9:99 10:110
Insertion failed: probe sequence has no free slot.
Table: 0:0 1:11 2:22 3:33 4:44 5:55 6:66 7:77 8:88 9:99 10:110

Common mistakes

  • Zero must not act as the empty sentinel.
  • Limit attempts to m to avoid looping forever.
  • Deletion would require tombstones or another repair strategy.

WHY THIS GROWTH RATE?

Time and space complexity

Insertion worst O(m); expected O(1) with suitable hashing and controlled load; each display O(m).

Linear probing visits at most m slots. With a full or clustered table, insertion can inspect all of them.

Expected constant insertion requires assumptions about hash distribution and load factor; it is not guaranteed for arbitrary inputs.

Displaying m slots after every one of q insertions contributes O(qm) total output work. This example fixes m=11, so its actual loops are bounded. Stored keys and occupancy use O(m) space.

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

6 focused questions

Try answering aloud, then expand the answer to check your reasoning. These questions focus on the concepts used in this program.

What is primary clustering in linear probing?

Consecutive occupied slots form runs. A new key whose home position lies in or near a run tends to extend it, increasing future probe lengths.

Why is zero stored correctly in this updated table?

Emptiness is recorded in a separate occupied array. A slot can therefore contain integer zero while still being marked occupied.

Why does linear probing always visit all slots before repeating?

Adding one modulo m cycles through every index exactly once over m probes. This lets the bounded loop establish that the table is full.

Why is a duplicate checked during probing rather than using a full-table scan first?

With no deletion, a stored key lies on its own probe path before the first empty slot. Checking that path preserves ordinary probe costs and avoids an unnecessary table-wide scan.

What would break if a deleted slot were simply marked empty?

A later key may have been displaced beyond that slot. A lookup that stops at the newly empty position could miss it; deletion needs a tombstone or cluster-repair method.

How does printing after every insertion affect the demonstration’s running time?

Even a short successful probe is followed by scanning all m slots for output. Therefore the demonstration includes O(m) display work per attempt, separate from insertion cost.