If you can limit yourself to ASCII characters, I would recommend a lookup table instead of a hash table. This search table contains a total of 128 entries.
A possible approach is as follows.
We start with an empty Q queue (can be implemented using linked lists) and a lookup table . For the character ch, T [ch] stores the pointer into the node queue containing the character ch and the index of the first occurrence of ch in the string. Initially, all T entries are NULL .
Each node queue stores the symbol and the first occurrence index, as indicated earlier, and also has a special boolean flag called remove, which indicates that the node has been removed from the queue.
Read the line character by character. If the character i th is ch, check to see if T [ch] = NULL . If so, this is the first occurrence of ch in the string. Then add the node for ch containing the index i to the queue.
If T [ch] is not NULL , it is a repeating character. If the node pointed to by T [ch] has already been deleted (ie the "node" flag is set), then nothing needs to be done. Otherwise, remove the node from the queue by manipulating the pointers of the previous and next nodes. Also set the remote node flag to indicate that the node is now deleted. Note that we do not release / delete the node at this point, and do not return T [ch] to NULL .
If we continue like this, the nodes for all duplicate characters will be removed from the queue. The remote flag is used to ensure that no node is removed from the queue twice if the character occurs more than two times.
After the string has been fully processed, the first node of the linked list will contain the character code, as well as the index of the first non-repeating character. The memory can then be freed by iterating over the entries of the lookup table T and freeing any non- NULL entries.
Here is the implementation of C. Here, instead of the remote flag, I set the prev and next pointers of the current node to NULL when it is deleted, and check this to see if the node has already been deleted.
#include <stdio.h> #include <stdlib.h> struct queue_node { int ch; int index; struct queue_node *prev; struct queue_node *next; }; void print_queue (struct queue_node *head); int main (void) { int i; struct queue_node *lookup_entry[128]; struct queue_node *head; struct queue_node *last; struct queue_node *cur_node, *prev_node, *next_node; char str [] = "GeeksforGeeks"; head = malloc (sizeof (struct queue_node)); last = head; last->prev = last->next = NULL; for (i = 0; i < 128; i++) { lookup_entry[i] = NULL; } for (i = 0; str[i] != '\0'; i++) { cur_node = lookup_entry[str[i]]; if (cur_node != NULL) { /* it is a repeating character */ if (cur_node->prev != NULL) { /* Entry has not been removed. Remove it from the queue. */ prev_node = cur_node->prev; next_node = cur_node->next; prev_node->next = next_node; if (next_node != NULL) { next_node->prev = prev_node; } else { /* Last node was removed */ last = prev_node; } cur_node->prev = NULL; cur_node->next = NULL; /* We will not free the node now. Instead, free * all nodes in a single pass afterwards. */ } } else { /* This is the first occurence - add an entry to the queue */ struct queue_node *newnode = malloc (sizeof(struct queue_node)); newnode->ch = str[i]; newnode->index = i; newnode->prev = last; newnode->next = NULL; last->next = newnode; last = newnode; lookup_entry[str[i]] = newnode; } print_queue (head); } last = head->next; while (last != NULL) { printf ("Non-repeating char: %c at index %d.\n", last->ch, last->index); last = last->next; } /* Free the queue memory */ for (i = 0; i < 128; i++) { if (lookup_entry[i] != NULL) { free (lookup_entry[i]); lookup_entry[i] = NULL; } } free (head); return (0); } void print_queue (struct queue_node *head) { struct queue_node *tmp = head->next; printf ("Queue: "); while (tmp != NULL) { printf ("%c:%d ", tmp->ch, tmp->index); tmp = tmp->next; } printf ("\n"); }