Searching with different variables in a Binary Search Tree - c

I have a binary search tree which stores ID, name and other details of a person and insertion is based on the id.
struct tree_node {
int ID;
char name[32];
char city[16];
struct tree_node* left_node;
struct tree_node* right_node;
}
This tree uses ID for insertion and searching and deletion and update. What if I want to add an option that all the above operation can be based on name or city. I will have to create a new tree for every attribute. Is there an alternative method to do?

A separate tree is required for each key. To reduce the amount of code required, the tree control parts can be separated out as members of a common struct type:
struct tree_node {
struct tree_node *left;
struct tree_node *right;
};
struct zone {
int ID;
char name[32];
char city[16];
struct tree_node id_node;
struct tree_node name_node;
struct tree_node city_node;
};
Then some functions can be defined to convert pointers to the id_node, name_node and city_node members to their containing struct zone. These make use of a simplified version of the container_of() macro from Linux:
#include <stddef.h>
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
static inline struct zone *
struct_zone_from_id_node(const struct tree_node *id_node_p)
{
return container_of(id_node_p, struct zone, id_node);
}
static inline struct zone *
struct_zone_from_name_node(const struct tree_node *name_node_p)
{
return container_of(name_node_p, struct zone, name_node);
}
static inline struct zone *
struct_zone_from_city_node(const struct tree_node *city_node_p)
{
return container_of(city_node_p, struct zone, city_node);
}
It will be useful to define a function to clear the linkage of a struct tree_node before inserting it into a tree or after removing it from a tree:
static inline void tree_node_clear(struct tree_node *node)
{
node->left = NULL;
node->right = NULL;
}
The trees for each key will need their own root node pointers:
struct tree_node *id_root = NULL;
struct tree_node *name_root = NULL;
struct tree_node *city_root = NULL;
For convenience, define functions to return the ID from the containing struct zone of a node of the "ID" tree, to return a pointer to the name member in the containing struct zone of a node of the "name" tree, and to return a pointer to the city member in the containing struct zone of a node of the "city" tree:
static inline int id_from_id_node(const struct tree_node *id_node_p)
{
return struct_zone_from_id_node(id_node_p)->ID;
}
static inline char *
name_from_name_node(const struct tree_node *name_node_p)
{
return struct_zone_from_name_node(name_node_p)->name;
}
static inline char *
city_from_city_node(const struct tree_node *city_node_p)
{
return struct_zone_from_city_node(city_node_p)->city;
}
Some functions for comparing IDs, names and cities can be defined. For example:
#include <string.h>
static int cmp_id(int id_a, int id_b)
{
return (id_a > id_b) - (id_a < id_b);
}
static int cmp_name(const char *name_a, const char *name_b)
{
return strcmp(name_a, name_b);
}
static int cmp_city(const char *city_a, const char *city_b)
{
return strcmp(city_a, city_b);
}
Those functions can be used in the definitions of functions for comparing nodes of the "ID", "name" and "city" trees:
static int tree_cmp_id(const struct tree_node *id_node_a,
const struct tree_node *id_node_b)
{
int id_a = id_from_id_node(id_node_a);
int id_b = id_from_id_node(id_node_b);
return cmp_id(id_a, id_b);
}
static int tree_cmp_name(const struct tree_node *name_node_a,
const struct tree_node *name_node_b)
{
const char *name_a = name_from_name_node(name_node_a);
const char *name_b = name_from_name_node(name_node_b);
return cmp_name(name_a, name_b);
}
static int tree_cmp_city(const struct tree_node *city_node_a,
const struct tree_node *city_node_b)
{
const char *city_a = city_from_city_node(city_node_a);
const char *city_b = city_from_city_node(city_node_b);
return cmp_city(city_a, city_b);
}
Some of the generic tree node handling functions will need a parameter that points to a function for comparing two nodes. The type of the function pointer can be defined as a typedef name:
typedef int (*tree_node_cmp_fn)(const struct tree_node *,
const struct tree_node *);
For example, the generic tree function for inserting a struct tree_node might have a prototype like the following (using a pointer to a pointer to the root node for functions that modify the tree):
int tree_insert(struct tree_node **root, struct tree_node *node,
tree_node_cmp_fn cmp);
Here is a possible implementation of tree_insert(). It returns 0 if the node was added successfully, or -1 if a node with a matching key already exists in the tree (and it also sets errno to EEXIST in that case):
#include <errno.h>
int tree_insert(struct tree_node **root, struct tree_node *node,
tree_node_cmp_fn cmp)
{
int found = 0;
while (!found && *root != NULL) {
int c = cmp(node, *root);
if (c < 0) {
root = &(*root)->left;
} else if (c > 0) {
root = &(*root)->right;
} else {
/* A node with a matching key already exists. */
found = -1;
}
}
if (found) {
/* Set an error if a node with a matching key already exists. */
errno = EEXIST;
} else {
/*
* *root will be NULL.
* Clear new node's linkage and append it to the tree.
*/
tree_node_clear(node);
*root = node;
}
return found;
}
The tree_insert() function can be called with the appropriate comparision functions in the definitions of functions for inserting a struct zone into the "ID" tree, the "name" tree, or the "city" tree:
int id_tree_insert(struct tree_node **id_root, struct zone *zone)
{
return tree_insert(id_root, &zone->id_node, tree_cmp_id);
}
int name_tree_insert(struct tree_node **name_root, struct zone *zone)
{
return tree_insert(name_root, &zone->name_node, tree_cmp_name);
}
int city_tree_insert(struct tree_node **city_root, struct zone *zone)
{
return tree_insert(city_root, &zone->city_node, tree_cmp_city);
}
Functions for searching the "ID", "name" and "city" trees follow a similar pattern to each other, but using a generic tree search function is not very practical:
struct zone *id_tree_find(struct tree_node *id_root, int id)
{
struct zone *result = NULL;
while (result == NULL && id_root != NULL) {
struct zone *cur_zone = struct_zone_from_id_node(id_root);
int cmp = cmp_id(id, cur_zone->ID);
if (cmp < 0) {
id_root = id_root->left;
} else if (cmp > 0) {
id_root = id_root->right;
} else {
result = cur_zone;
}
}
return result;
}
struct zone *name_tree_find(struct tree_node *id_root, const char *name)
{
struct zone *result = NULL;
while (result == NULL && id_root != NULL) {
struct zone *cur_zone = struct_zone_from_name_node(id_root);
int cmp = cmp_name(name, cur_zone->name);
if (cmp < 0) {
id_root = id_root->left;
} else if (cmp > 0) {
id_root = id_root->right;
} else {
result = cur_zone;
}
}
return result;
}
struct zone *city_tree_find(struct tree_node *id_root, const char *city)
{
struct zone *result = NULL;
while (result == NULL && id_root != NULL) {
struct zone *cur_zone = struct_zone_from_city_node(id_root);
int cmp = cmp_city(city, cur_zone->city);
if (cmp < 0) {
id_root = id_root->left;
} else if (cmp > 0) {
id_root = id_root->right;
} else {
result = cur_zone;
}
}
return result;
}
Because the functions are so similar, a macro could be defined to define the functions. This makes use of the container_of() macro defined earlier:
#define DEFINE_TREE_FIND(fn_name, root, container_type, member, \
val_type, val, cmp_fn) \
container_type *fn_name(struct tree_node *root, val_type val) \
{ \
container_type *_result = NULL; \
while (_result == NULL && root != NULL) { \
container_type *_current = \
container_of(root, container_type, member); \
int _cmp = cmp_fn(val, _current->member); \
if (_cmp < 0) { \
root = root->left; \
} else if (_cmp > 0) { \
root = root->right; \
} else { \
_result = _current; \
} \
} \
return _result; \
}
#define DEFINE_TREE_FIND_ZONE(fn_name, root, member, \
val_type, val, cmp_fn) \
DEFINE_TREE_FIND(fn_name, root, struct zone, member, \
val_type, val, cmp_fn)
(The DEFINE_TREE_FIND_ZONE() macro is specific to defining functions that find a struct zone, but the DEFINE_TREE_FIND() macro is more general and can be used to define functions for finding any container type that includes a struct tree_node member.)
Then the searching functions can be defined by calling the DEFINE_TREE_FIND_ZONE() macro outside any functions (I have appended 2 to the function names to distinguish them from the previously defined search functions):
DEFINE_TREE_FIND_ZONE(id_tree_find2, id_root, ID, int, id, cmp_id)
DEFINE_TREE_FIND_ZONE(name_tree_find2, name_root, name,
const char *, name, cmp_name)
DEFINE_TREE_FIND_ZONE(city_tree_find2, city_root, city,
const char *, city, cmp_city)
For completeness, define a generic function to remove a node from a tree. Here is a function prototype:
int tree_remove(struct tree_node **root, struct tree_node *node,
tree_node_cmp_fn cmp);
Here is an implementation to tree_remove(). It returns 0 if the node was found and removed. It returns -1 and sets errno to ENOENT if no node that matched the node's key was found, or sets errno to EEXIST if a different node with the node's key was found:
int tree_remove(struct tree_node **root, struct tree_node *node,
tree_node_cmp_fn cmp)
{
int not_found = -1;
while (not_found && *root != NULL) {
int c = cmp(node, *root);
if (c < 0) {
root = &(*root)->left;
} else if (c > 0) {
root = &(*root)->right;
} else {
/* Found! */
not_found = 0;
}
}
if (not_found) {
/* Set an error if a node is not in the tree. */
errno = ENOENT;
} else if (*root != node) {
/* Found a different node with the same key! Set an error. */
not_found = -1;
errno = EEXIST;
} else {
/* Found the correct node, so remove it. */
if ((*root)->left == NULL) {
/* No left node, so replace *root with right node (if any). */
*root = (*root)->right;
} else if ((*root)->right == NULL) {
/* No right node, so replace *root with left node (if any). */
*root = (*root)->left;
} else {
/* Find the right-most node on the left sub-tree. */
struct tree_node **link = &(*root)->left;
struct tree_node *move;
while ((*link)->right != NULL) {
link = &(*link)->right;
}
/* Move *link to the position of the removed node. */
move = *link;
*link = move->left;
move->left = (*root)->left;
move->right = (*root)->right;
*root = move;
/* Clear removed node's linkage. */
tree_node_clear(node);
/* Clear removed node's linkage. */
tree_node_clear(node);
}
}
return not_found;
}
The tree_remove() function can be called with the appropriate comparision functions in the definitions of functions for removing a struct zone from the "ID" tree, the "name" tree, or the "city" tree:
int id_tree_remove(struct tree_node **id_root, struct zone *zone)
{
return tree_remove(id_root, &zone->id_node, tree_cmp_id);
}
int name_tree_remove(struct tree_node **name_root, struct zone *zone)
{
return tree_remove(name_root, &zone->name_node, tree_cmp_name);
}
int city_tree_remove(struct tree_node **city_root, struct zone *zone)
{
return tree_remove(city_root, &zone->city_node, tree_cmp_city);
}
If the struct zone was allocated dynamically, the caller can free it once it has been removed from all the trees it is attached to.

Related

Is there a generic linked list functionality for any structure

Suppose I have two structures in my code like this:
typedef struct Food {
char* name;
int food_id;
int price;
int capacity;
int hall_id;
int day;
int reserved;
int profit;
Food* next;
} Food;
typedef struct Coupon {
int id;
int percentage;
int capacity;
Coupon* next;
} Coupon;
And I want to implement a linked list data structure with them. For example I have a Food* variable which points to food number 1 and then food number 2 in next points to the next food and...
The problem is when I want to write functions for the linked lists, I have to write 2 functions for every job. For example I want to have a function that gets the head of the list and a new element, then add the new element to the list. Because the types of these two linked lists are different, I can't think of a way to write only one function for both. Is there a way to do so?
For example I want to make this function work for all types:
void add_front(Coupon* head, Coupon* new_el){
while (head->next != NULL){
head = head->next;
}
head->next = new_el;
new_el->next = NULL;
}
First, you separate the domain data of each entry from the managing data.
typedef struct {
char* name;
int food_id;
int price;
int capacity;
int hall_id;
int day;
int reserved;
int profit;
} Food;
typedef struct {
int id;
int percentage;
int capacity;
} Coupon;
Then you can use a union with pointers to the domain data in the entry's structure. Each entry will be of the same size.
typedef struct Entry {
struct Entry* next;
union {
Food* food;
Coupon* coupon;
} data;
} Entry;
You could even place the domain data directly in the union, but this will waste memory if only small sized values are stored.
typedef struct Entry {
struct Entry* next;
union {
Food food;
Coupon coupon;
} data;
} Entry;
Now you are able to add new entries of different data with a generic function.
void add_front(Entry* head, Entry* new_el) {
while (head->next != NULL){
head = head->next;
}
head->next = new_el;
new_el->next = NULL;
}
A possible trick is to use the fact that it is legal to convert a pointer to a struct to a pointer to its initial member, and that it is legal to convert from any pointer type to void * and back. So provided next is the first member, a number of functions could be independant of the actual class, if they take void * parameters for any struct for which the first element is a next pointer. Of course, auxiliary function able to handle a real object should be provided...
Here is an example code showing a possible implementation of add_before, add_after, list_remove (remove is defined in stdio.h) and display and showing an example of use with Coupon objects:
#include <stdio.h>
typedef struct Food {
struct Food* next;
char* name;
int food_id;
int price;
int capacity;
int hall_id;
int day;
int reserved;
int profit;
} Food;
typedef struct Coupon {
struct Coupon* next;
int id;
int percentage;
int capacity;
} Coupon;
void* add_before(void* list, void* elem) {
*(void **)elem = list;
return elem;
}
void* add_after(void* list, void* elem) {
if (NULL == list) return elem;
void** last = list;
while (*last != NULL) {
last = *last;
}
*last = elem;
return list;
}
// eltdisplay is a pointer to a function able to display an element
void display(void* list, void (*eltdisplay)(void*, FILE *), FILE *out) {
while (NULL != list) {
eltdisplay(list, out);
if (NULL != *(void **)list) {
fprintf(out, " -> ");
}
list = *(void **)list;
}
fprintf(out, "\n");
}
void* list_remove(void* list, void* elem, int(*comp)(void* elt1, void* elt2)) {
if (list == NULL) return NULL;
void** cur = list, **old = NULL;
while (cur != NULL) {
if (0 == comp(cur, elem)) {
if (old == NULL) return *cur;
*old = *cur;
break;
}
old = cur;
cur = *cur;
}
return list;
}
int couponcomp(void* elt1, void* elt2) {
return ((Coupon*)elt1)->id != ((Coupon*)elt2)->id;
}
void coupondisplay(void* elt, FILE *out) {
Coupon* coupon = elt;
fprintf(out, "%d", coupon->id);
}
int main() {
Coupon data[3] = { {NULL, 1}, {NULL, 2}, {NULL, 3} };
Coupon* list = NULL;
for (int i = 0; i < sizeof(data) / sizeof(*data); i++) {
list = addLast(list, data+i);
}
display(list, coupondisplay, stdout);
Coupon data2 = { NULL, 2 };
list = list_remove(list, &data2, couponcomp);
display(list, coupondisplay, stdout);
return 0;
}
It compiles with no warning and displays as expected:
1 -> 2 -> 3
1 -> 3
You could use a macro :
From one of my personal project
#if !defined(CIRCULAR_DOUBLE_LINKED_LIST_H)
#define CIRCULAR_DOUBLE_LINKED_LIST_H
//T must include ->prev and ->next member
#define DECLARE_NAMED_CIRCULAR_DOUBLE_LINKED_LIST(T, name) \
static inline T* name ## _add_after(T* source, T* item) { \
T* last_next = source->next; \
source->next = item; \
item->prev = source; \
item->next = last_next; \
last_next->prev = item; \
return source; \
} \
static inline T* name ## _add_before(T* source, T* item) {\
T* last_prev = source->prev; \
source->prev = item; \
item->next = source; \
item->prev = last_prev; \
last_prev->next = item; \
return source; \
} \
static inline T* name ## _remove(T* item) { \
T* next = item->next; \
item->prev->next = item->next; \
item->next->prev = item->prev; \
return next == item ? NULL : next; \
}
#define DECLARE_CIRCULAR_DOUBLE_LINKED_LIST(T) DECLARE_NAMED_CIRCULAR_DOUBLE_LINKED_LIST(T, list_ ## T)
#endif // CIRCULAR_DOUBLE_LINKED_LIST_H
typedef struct Food {
Food* next;
Food* prev;
char* name;
int food_id;
int price;
int capacity;
int hall_id;
int day;
int reserved;
int profit;
} Food;
DECLARE_CIRCULAR_DOUBLE_LINKED_LIST(Food)
list_Food_add_after(Food*, Food*);
list_Food_add_before(Food*, Food*);
list_Food_remove(Food*);
I think the best way I ever found to do this in C (i.e., without templates) was:
Make a SinglyLinkedNode class that just has SinglyLinkedNode *next
Write your list functions based on this class -- every list is a list of SinglyLinkedNode
Add SinglyLinkedNode node fields to Food and Coupon, and use that to link them together.
Additionally provide functions or macros to get the containing Food or Coupon pointer from a SinglyLinkedNode pointer, like Coupon *couponFromNode(Node *p);
Note that I would never actually do this for singly-linked lists, because singly-linked list operations are so easy to write that you don't really need list methods. This technique starts to get useful for doubly-linked lists or more complex introspective containers.

Failed to add node on Chained List

I wanted to create a chained list, initialize it and add a new node at the beginning.
But i have this problem :
////
error: request for member ‘value’ in something not a structure or union
////
t_bool list_add_elem_at_front(t_list *front_ptr, double elem)
{
if (front_ptr == NULL)
{
front_ptr = malloc(sizeof(*front_ptr));
front_ptr->value = elem;
front_ptr->next = NULL;
}
printf("%f\n", front_ptr->value);
return (TRUE);
}
I'm sure the struct is malloc but I really don't get why it doesn't find "value" and the "*next" on the structure
int main(void)
{
int i = 2.1;
t_list list_head = NULL;
list_add_elem_at_front(&list_head, i);
}
And the header file
typedef struct s_node
{
double value;
struct s_node *next;
} t_node;
typedef t_node *t_list;
try this
t_bool list_add_elem_at_front(t_list *front_ptr, double elem)
{
if (*front_ptr == NULL)
{
*front_ptr = malloc(sizeof(**front_ptr));
(*front_ptr)->value = elem;
(*front_ptr)->next = NULL;
}
printf("%f\n", (*front_ptr)->value);
return (TRUE);
}
In:
t_bool list_add_elem_at_front(t_list *front_ptr, double elem)
You have too many pointers in the argument:
t_list *front_ptr
when you combine that with:
typedef t_node *t_list;
result in a t_node**
Rather than remove the * in the argument, I would just use t_node * everywhere
And additionally the problem is in the argument of sizeof that should be t_node

Assignment from incompatible pointer type (structs, linked list)

Creating a dictionary data structure using a linked list.
typedef struct _dictionary_entry_t
{
const char* key;
const char* value;
struct dictionary_entry_t *next;
struct dictionary_entry_t *prev;
} dictionary_entry_t;
typedef struct _dictionary_t
{
dictionary_entry_t *head;
dictionary_entry_t *curr;
int size;
} dictionary_t;
Working on the function to add dictionary entries to the linked list.
int dictionary_add(dictionary_t *d, const char *key, const char *value)
{
if (d->curr == NULL) //then list is empty
{
d->head = malloc(sizeof(dictionary_entry_t));
d->head->key = key; //set first dictionary entry key
d->head->value = value; //set first dictionary entry value
d->head->next = NULL;
//d->curr = d->head;
}
else
{
d->curr = d->head;
while (strcmp((d->curr->key), key) != 0 && d->curr != NULL) //while keys don't match and haven't reached end of list...
{
d->curr = d->curr->next;
}
}
return -1;
}
assigning d->curr to d->curr->next gives me the warning 'assignment from incompatible pointer type'.
What is my mistake here? both curr and next are of the type *dictionary_entry_t
next is a struct dictionary_entry_t *, but d->curr is a dictionary_entry_t * aka struct _dictionary_entry_t *. Note the difference in underscores.
One way to solve this would be to be consistent with your underscores, declaring next as:
struct _dictionary_entry_t *next;
However, I prefer a different way: typedeffing before declaring the struct. Then:
typedef struct _dictionary_entry_t dictionary_entry_t;
struct _dictionary_entry_t {
/* ... */
dictionary_entry_t *next;
/* ... */
};
In addition to the issue raised by #icktoofay, another problem is your loop condition:
while (strcmp((d->curr->key), key) != 0 && d->curr != NULL)
If d->curr is NULL, then when you do the strcmp(), you're attempting to dereference a NULL pointer. Bad things will happen. Reverse those:
while ((d->curr != NULL) && strcmp(d->curr->key, key) != 0)
Or, more concisely:
while (d->curr && strcmp (d->cur->key, key))

insertion binary search tree in C

I've been stuck on the insertion part of the binary search tree. I get so confused with nested structs. The basic idea of this program is to create a bst that is able to hold names and double values which get stored by value (obviously).
Example: I want to store
Jane 3.14
John 3.233
Luke 6.4
Mike 1.4
so the bst would look like
3.14
/ \
1.4 3.233
\
6.4
however I'm having trouble with the insertHelper recursion portion of the code. The hash table is a bonus part of the code that I'll try implementing at a later time. Thank you for your help!
typedef struct name_val // holds name and value
{
char *name;
double value;
}NAME_VAL;
typedef struct node //binary search tree
{
NAME_VAL *nV;
struct node *left;
struct node *right;
}NODE;
struct tmap_struct //handle for bst and hashtable
{
int nL; //nodes on left
int nR; //nodes on right
NODE *root;
NODE **table;
};
int tmap_insert(TMAP_PTR hashTree, char * name, double val)
{
if(hashTree->root == NULL)
{
NODE *bst = (NODE *)malloc(sizeof(NODE));
NAME_VAL *root = (NAME_VAL *)malloc(sizeof(NAME_VAL));
bst->nV = root;
bst->nV->value = val;
strcpy(bst->nV->name, name);
hashTree->root = bst;
hashTree->nL = 0;
hashTree->nR = 0;
}
else
insertHelper(hashTree->root, val, name);
}
void insertHelper(TMAP_PTR hashTree, int val, char * name)
{
if(val < hashTree->root->nV->value)
{
if(hashTree->root->left == NULL)
{
hashTree->root->left = (NODE *)malloc(sizeof(NODE));
hashTree->root->left->nV = (NAME_VAL *) malloc(sizeof(NAME_VAL));
strcpy(hashTree->root->left->nV->name, name);
hashTree->root->nV->value = val;
(hashTree->nL)++;
}
else
insertHelper(hashTree->root->left, val, name);
}
else
{
if(hashTree->root->right == NULL)
{
hashTree->root->right = (NODE *)malloc(sizeof(NODE));
hashTree->root->right->nV = (NAME_VAL *)malloc(sizeof(NAME_VAL));
strcpy(hashTree->root->left->nV->name,name);
hashTree->root->nV->value = val;
(hashTree->nR)++;
}
else
insertHelper(hashTree->root->right, val, name);
}
}
I doubt this compiles. Is that the problem you're having?
From what I can see, you have declared insertHelper with the wrong type for its first parameter. It should take NODE* values, not TMAP_PTR values. That's because you always call it with nodes out of your tree.
So the first part of the function should look like this:
void insertHelper(NODE *node, int val, char * name)
{
if(val < node->nV->value)
{
if(node->left == NULL)
{
node->left = (NODE *)malloc(sizeof(NODE));
node->left->nV = (NAME_VAL *) malloc(sizeof(NAME_VAL));
strcpy(node->left->nV->name, name);
node->left->nV->value = val;
}
else
insertHelper(node->left, val, name);
}
//.....
Note that I removed the line:
(hashTree->nR)++;
It hardly even makes sense to track this information, unless maybe you do it at the node level.
But if you must, you could have insertHelper recursively return a positive or negative value to indicate what side it inserted on. But that doesn't makes sense. What is it on the right of? You may have inserted it on the right of a node that was in the left half of the tree.
If you store this information on each node, you can recursively update the node above as you return from insertHelper. Maybe that's what you were trying to do. Balanced tree implementations do something similar - AVL trees store the maximum depth of the tree at a node and use that to do branch rotations for rebalancing.
You'll have to adapt mine(It's almost standard C besides the unneeded template and class), but it's a similar algorithm: (I believe, I didn't look at any source for my own purposes.)
template<typename T>
class BST {
protected:
typedef struct node_t {
struct node_t * dir[2];
T data;
} node;
node * root;
void insert_node(node * active_node, T data){ //call with node *root;
int next = data < active_node->data ? 0 : 1;
if(active_node->dir[next] == NULL){
active_node->dir[next] = new node;
active_node->dir[next]->dir[0] = NULL;
active_node->dir[next]->dir[1] = NULL;
active_node->data = data;
} else
insert_node(active_node->dir[next], data);
}
public:
BST() : root(new node){root->dir[0] = NULL; root->dir[1] = NULL; root->data = 0;}
~BST(){}
}

Is it possible to have a linked list of different data types?

This is just another interview question.
Can we have a linked list of different data types, i.e. each element in a linked list can have different structure or union elements? If it's possible can you please explain with an example?
Well in a linked list you don't HAVE to link like for like structs together. As long as they have the appropriate forward and/or backwards pointers you are fine. For example:
struct BaseLink
{
BaseLink* pNext;
BaseLink* pPrev;
int typeId;
};
struct StringLink
{
BaseLink baseLink;
char* pString;
};
struct IntLink
{
BaseLink baseLink;
int nInt;
};
This way you'd have a linked list that goes from BaseLink to BaseLink. The extra data is not a problem. You want to see it as a StringLink? Then cast the BaseLink to a StringLink.
Just remember that you need some form of typeid in there so you know what to cast it to when you arrive at it.
Use union to create the datatype
union u_tag{
char ch;
int d;
double dl;
};
struct node {
char type;
union u_tag u;
struct node *next;
};
Use struct node to create linked list. type decides what is the datatype of the data.
Harsha T, Bangalore
You can use a union type:
enum type_tag {INT_TYPE, DOUBLE_TYPE, STRING_TYPE, R1_TYPE, R2_TYPE, ...};
struct node {
union {
int ival;
double dval;
char *sval;
struct recordType1 r1val;
struct recordType2 r2val;
...
} data;
enum type_tag dataType;
struct node *prev;
struct node *next;
};
Another method I've explored is to use a void* for the data and attach pointers to functions that handle the type-aware stuff:
/**
* Define a key type for indexing and searching
*/
typedef ... key_t;
/**
* Define the list node type
*/
struct node {
void *data;
struct node *prev;
struct node *next;
void *(*cpy)(void *); // make a deep copy of the data
void (*del)(void *); // delete the data
char *(*dpy)(void *); // format the data for display as a string
int (*match)(void *, key_t); // match against a key value
};
/**
* Define functions for handling a specific data type
*/
void *copyARecordType(void *data)
{
struct aRecordType v = *(struct aRecordType *) data;
struct aRecordType *new = malloc(sizeof *new);
if (new)
{
// copy elements of v to new
}
return new;
}
void deleteARecordType(void *data) {...}
char *displayARecordType(void *data) {...}
int matchARecordType(void *data, key_t key) {...}
/**
* Define functions for handling a different type
*/
void *copyADifferentRecordType(void *data) {...}
void deleteADifferentRecordType(void *data) {...}
char *displayADifferentRecordType(void *data) {...}
int matchADifferentRecordType(void *data, key_t key) {...}
/**
* Function for creating new list nodes
*/
struct node *createNode(void *data, void *(*cpy)(void *), void (*del)(void *),
char *(*dpy)(void *), int (*match)(void *, key_t))
{
struct node *new = malloc(sizeof *new);
if (new)
{
new->cpy = cpy;
new->del = del;
new->dpy = dpy;
new->match = match;
new->data = new->cpy(data);
new->prev = new->next = NULL;
}
return new;
}
/**
* Function for deleting list nodes
*/
void deleteNode(struct node *p)
{
if (p)
p->del(p->data);
free(p);
}
/**
* Add new node to the list; for this example, we just add to the end
* as in a FIFO queue.
*/
void addNode(struct node *head, void *data, void *(*cpy)(void*),
void (*del)(void *), char *(*dpy)(void *), int (*match)(void*, key_t))
{
struct node *new = createNode(data, cpy, del, dpy, match);
if (!head->next)
head->next = new;
else
{
struct node *cur = head->next;
while (cur->next != NULL)
cur = cur->next;
cur->next = new;
new->prev = cur;
}
}
/**
* Examples of how all of this would be used.
*/
int main(void)
{
struct aRecordType r1 = {...};
struct aDifferentRecordType r2 = {...};
struct node list, *p;
addNode(&list, &r1, copyARecordType, deleteARecordType, displayARecordType,
matchARecordType);
addNode(&list, &r2, copyADifferentRecordType, deleteADifferentRecordType,
displayADifferentRecordType, matchADifferentRecordType);
p = list.next;
while (p)
{
printf("Data at node %p: %s\n", (void*) p, p->dpy(p->data));
p = p->next;
}
return 0;
}
Obviously, I've left out some error checking and handling code from this example, and I don't doubt there are a host of problems with it, but it should be illustrative.
You can have each node in a linked list have a void* that points to your data. It's up to you how you determine what type of data that pointer is pointing to.
If you don't want to have to specify the type of every node in the list via the union solution you can always just store the data in a char* and take type-specific function pointers as parameters to type-sensitive operations such as printing or sorting the list.
This way you don't have to worry about what node is what type and can just cast the data however you like.
/* data types */
typedef struct list_node list_node;
struct list_node {
char *data;
list_node *next;
list_node *prev;
};
typedef struct list list;
struct list {
list_node *head;
list_node *tail;
size_t size;
};
/* type sensitive functions */
int list_sort(list *l, int (*compar)(const void*, const void*));
int list_print(list *l, void (*print)(char *data));
Yes, I do this by defining the list's element's value as a void pointer void*.
In order to know the type stored in each element of the list I also have a .type field in there, so I know how to dereference what the pointer is pointing to for each element.
struct node {
struct node* next;
int type;
void* value;
};
Here's a full example of this:
//
// An exercise to play with a struct that stores anything using a void* field.
//
#include <stdio.h>
#define TRUE 1
int TYPE_INT = 0;
int TYPE_STRING = 1;
int TYPE_BOOLEAN = 2;
int TYPE_PERSON = 3;
struct node {
struct node* next;
int type;
void* value;
};
struct person {
char* name;
int age;
};
int main(int args, char **argv) {
struct person aPerson;
aPerson.name = "Angel";
aPerson.age = 35;
// Define a linked list of objects.
// We use that .type field to know what we're dealing
// with on every iteration. On .value we store our values.
struct node nodes[] = {
{ .next = &nodes[1], .type = TYPE_INT , .value=1 },
{ .next = &nodes[2], .type = TYPE_STRING , .value="anyfing, anyfing!" },
{ .next = &nodes[3], .type = TYPE_PERSON , .value=&aPerson },
{ .next = NULL , .type = TYPE_BOOLEAN, .value=TRUE }
};
// We iterate through the list
for ( struct node *currentNode = &nodes[0]; currentNode; currentNode = currentNode->next) {
int currentType = (*currentNode).type;
if (currentType == TYPE_INT) {
printf("%s: %d\n", "- INTEGER", (*currentNode).value); // just playing with syntax, same as currentNode->value
} else if (currentType == TYPE_STRING) {
printf("%s: %s\n", "- STRING", currentNode->value);
} else if (currentType == TYPE_BOOLEAN) {
printf("%s: %d\n", "- BOOLEAN (true:1, false:0)", currentNode->value);
} else if (currentType == TYPE_PERSON) {
// since we're using void*, we end up with a pointer to struct person, which we *dereference
// into a struct in the stack.
struct person currentPerson = *(struct person*) currentNode->value;
printf("%s: %s (%d)\n","- TYPE_PERSON", currentPerson.name, currentPerson.age);
}
}
return 0;
}
Expected output:
- INTEGER: 1
- STRING: anyfing, anyfing!
- TYPE_PERSON: Angel (35)
- BOOLEAN (true:1, false:0): 1
As said, you can have a node this questionwith a void*. I suggest using something to know about your type :
typedef struct
{
/* linked list stuff here */
char m_type;
void* m_data;
}
Node;
See this question.
Actually, you don't have to put the pointer first in the structure, you can put it anywhere and then find the beginning fo the struct with a containerof() macro. The linux kernel does this with its linked lists.
http://isis.poly.edu/kulesh/stuff/src/klist/
I use these macros I wrote to make general linked lists. You just create your own struct and use the macro list_link somewhere as a member of the struct. Give that macro one argument naming the struct (without the struct keyword). This implements a doubly linked list without a dummy node (e.g. last node links back around to first node). The anchor is a pointer to the first node which starts out initialized by list_init(anchor) by giving it the lvalue (a dereferenced pointer to it is an lvalue). Then you can use the other macros in the header. Read the source for comments about each available macro functions. This is implemented 100% in macros.
http://phil.ipal.org/pre-release/list-0.0.5.tar.bz2
Yes,Sure You can insert any data type values in the linked list I've designed and its very simple to do so.I have used different constructors of node and boolean variables to check that which type value is inserted and then I do operation and command according to that value in my program.
//IMPLEMENTATION OF SINGLY LINKED LISTS
#include"iostream"
#include"conio.h"
#include <typeinfo>
using namespace std;
class node //struct
{
public:
node* nextptr;
int data;
////////////////////////////////just to asure that user can insert any data type value in the linked list
string ss;
char cc;
double dd;
bool stringTrue=0;
bool intTrue = 0;
bool charTrue = 0;
bool doubleTrue = 0;
////////////////////////////////just to asure that user can insert any data type value in the linked list
node()
{
nextptr = NULL;
}
node(int d)
{
data = d;
nextptr = NULL;
intTrue = 1;
}
////////////////////////////////just to asure that user can insert any data type value in the linked list
node(string s)
{
stringTrue = 1;
ss = s;
nextptr = NULL;
}
node(char c)
{
charTrue = 1;
cc = c;
nextptr = NULL;
}
node(double d)
{
doubleTrue = 1;
dd = d;
nextptr = NULL;
}
////////////////////////////////just to asure that user can insert any data type value in the linked list
//TO Get the data
int getintData()
{
return data;
}
string getstringData()
{
return ss;
}
double getdoubleData()
{
return dd;
}
char getcharData()
{
return cc;
}
//TO Set the data
void setintData(int d)
{
data = d;
}
void setstringData(string s)
{
ss = s;
}
void setdoubleData(double d)
{
dd = d;
}
void setcharData(char c)
{
cc = c;
}
char checkWhichInput()
{
if (intTrue == 1)
{
return 'i';
}
else if (stringTrue == 1)
{
return 's';
}
else if (doubleTrue == 1)
{
return 'd';
}
else if (charTrue == 1)
{
return 'c';
}
}
//////////////////////////////Just for the sake of implementing for any data type//////////////////////////////
node* getNextptr()
{
return nextptr;
}
void setnextptr(node* nptr)
{
nextptr = nptr;
}
};
class linkedlist
{
node* headptr;
node* addnodeatspecificpoition;
public:
linkedlist()
{
headptr = NULL;
}
void insertionAtTail(node* n)
{
if (headptr == NULL)
{
headptr = n;
}
else
{
node* rptr = headptr;
while (rptr->getNextptr() != NULL)
{
rptr = rptr->getNextptr();
}
rptr->setnextptr(n);
}
}
void insertionAtHead(node *n)
{
node* tmp = n;
tmp->setnextptr(headptr);
headptr = tmp;
}
int sizeOfLinkedList()
{
int i = 1;
node* ptr = headptr;
while (ptr->getNextptr() != NULL)
{
++i;
ptr = ptr->getNextptr();
}
return i;
}
bool isListEmpty() {
if (sizeOfLinkedList() <= 1)
{
return true;
}
else
{
false;
}
}
void insertionAtAnyPoint(node* n, int position)
{
if (position > sizeOfLinkedList() || position < 1) {
cout << "\n\nInvalid insertion at index :" << position;
cout <<".There is no index " << position << " in the linked list.ERROR.\n\n";
return;
}
addnodeatspecificpoition = new node;
addnodeatspecificpoition = n;
addnodeatspecificpoition->setnextptr(NULL);
if (headptr == NULL)
{
headptr = addnodeatspecificpoition;
}
else if (position == 0)
{
addnodeatspecificpoition->setnextptr(headptr);
headptr = addnodeatspecificpoition;
}
else
{
node* current = headptr;
int i = 1;
for (i = 1; current != NULL; i++)
{
if (i == position)
{
addnodeatspecificpoition->setnextptr(current->getNextptr());
current->setnextptr(addnodeatspecificpoition);
break;
}
current = current->getNextptr();
}
}
}
friend ostream& operator<<(ostream& output,const linkedlist& L)
{
char checkWhatInput;
int i = 1;
node* ptr = L.headptr;
while (ptr->getNextptr() != NULL)
{
++i;
checkWhatInput = ptr->checkWhichInput();
/// <summary>
switch (checkWhatInput)
{
case 'i':output <<ptr->getintData()<<endl;
break;
case 's':output << ptr->getstringData()<<endl;
break;
case 'd':output << ptr->getdoubleData() << endl;
break;
case 'c':output << ptr->getcharData() << endl;
break;
default:
break;
}
/// </summary>
/// <param name="output"></param>
/// <param name="L"></param>
/// <returns></returns>
ptr = ptr->getNextptr();
}
/// <summary>
switch (checkWhatInput)
{
case 'i':output << ptr->getintData() << endl;
break;
case 's':output << ptr->getstringData() << endl;
break;
case 'd':output << ptr->getdoubleData() << endl;
break;
case 'c':output << ptr->getcharData() << endl;
break;
default:
break;
}
/// </summary>
/// <param name="output"></param>
/// <param name="L"></param>
/// <returns></returns>
if (ptr->getNextptr() == NULL)
{
output << "\nNULL (There is no pointer left)\n";
}
return output;
}
~linkedlist() {
delete addnodeatspecificpoition;
}
};
int main()
{
linkedlist L1;
//Insertion at tail
L1.insertionAtTail(new node("dsaf"));
L1.insertionAtTail(new node("sadf"));
L1.insertionAtTail(new node("sfa"));
L1.insertionAtTail(new node(12));
L1.insertionAtTail(new node(67));
L1.insertionAtTail(new node(23));
L1.insertionAtTail(new node(45.677));
L1.insertionAtTail(new node(12.43556));
//Inserting a node at head
L1.insertionAtHead(new node(1));
//Inserting a node at any given point
L1.insertionAtAnyPoint(new node(999), 3);
cout << L1;
cout << "\nThe size of linked list after insertion of elements is : " << L1.sizeOfLinkedList();
}
The output is
1
dsaf
sadf
999
sfa
12
67
23
45.677
12.4356
Thats what you can use to create a linked list without worrying of data type
Just an FYI, In C# you can use Object as your data member.
class Node
{
Node next;
Object Data;
}
User can then use something like this to find out which Object the Node stores:
if (obj.GetType() == this.GetType()) //
{
}

Resources