Naive Linear Scan with Manual Shift
This method iterates through the collection, identifying target values and shifting remaining items leftward to fill gaps. Each removal triggers a backward propagation of elements, resulting in quadratic time complexity.
#include <stdio.h>
int strip_target(int* arr, int capacity, int target) {
int len = capacity;
for (int idx = 0; idx < len; idx++) {
if (arr[idx] == target) {
for (int src = idx + 1; src < len; src++) {
arr[src - 1] = arr[src];
}
idx--;
len--;
}
}
return len;
}
int main() {
int data[] = {3, 2, 2, 3};
int remove_val = 3;
int count = sizeof(data) / sizeof(data[0]);
int updated_len = strip_target(data, count, remove_val);
printf("Filtered Array: ");
for (int i = 0; i < updated_len; i++) {
printf("%d ", data[i]);
}
printf("\n");
printf("Remaining Count: %d\n", updated_len);
return 0;
}
Dual Index Traversal (Preserving Sequence)
A single pass can handle filtering by maintaining two markers. The outer scanner examines every item, while the inner tracker records positions for valid entries. Non-matching elements are written sequentially to the tracking index.
int filter_sequence(int* buffer, int limit, int exclude) {
int write_idx = 0;
for (int read_idx = 0; read_idx < limit; read_idx++) {
if (buffer[read_idx] != exclude) {
buffer[write_idx++] = buffer[read_idx];
}
}
return write_idx;
}
Conceptual Mapping: The scanning iterator advances unconditionally, acting as the search mechanism. The writing iterator only progresses upon accepting a candidate value, effectively compacting the dataset without temporary storage.
Boundary Contraction Method
When sequence preservation is unnecessary, collapsing from both extremes yields optimal average performance. The left marker identifies invalid tokens, while the right marker supplies replacement values. Upon detection, the rightmost valid item overwrites the current position, and the right boundary contracts inward.
int collapse_bounds(int* stream, int total, int discard) {
int head = 0;
int tail = total;
while (head < tail) {
if (stream[head] == discard) {
stream[head] = stream[tail - 1];
tail--;
} else {
head++;
}
}
return head;
}
This strategy guarantees at most N combined movements across both indices during worst-case scenarios. Unlike sequential copying, it eliminates redundant assignments by swapping obsolete slots with trailing data until convergence occurs.