Data Structures and Algorithms in C: Implementations and Explanations

1) Implementing a Linear Queue in C

#include<stdio.h>
int q[10], ch, size, front=-1, rear=-1, item, i;
void enqueue()
{
if(rear == size – 1)
printf(“Queue Overflow “);
else
{
if(front== – 1)
front = 0;
printf(“Insert the element in queue : “);
scanf(“%d”, &item);
q[rear++] = item;
}
}
void dequeue()
{
if(front== -1 || front>rear)
{
 printf(“Queue Underflow “);
}
else
{
printf(“Element deleted from queue is : %d “, q[front]);
front++;
 }
}
void display()
{
if(front== -1 || front>rear)
{
 printf(“Queue is empty “);
}
else
{
printf(“Queue is : “);
for(i = front; i < rear; i++)
printf(“%d “, q[i]);
}
}
void main()
{
printf(“Enter the size of STACK “);
scanf(“%d”, &size);
for(;;)
{
printf(“1. Insert 2. Delete 3. DISPLAY “);
printf(“Enter the Choice: “);
scanf(“%d”,&ch);
switch(ch)
{
case 1: enqueue(); break;
 case 2: dequeue();break;
case 3: display(); break;
 }
}
}

2) Implementing a Stack of Integers Using a Singly Linked List in C

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* next;
};

struct Node* createNode(int value) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = NULL;
    return newNode;
}

void insertFront(struct Node** head, int value) {
    struct Node* newNode = createNode(value);
    newNode->next = *head;
    *head = newNode;
}


void display(struct Node* head) {
    struct Node* temp = head;
    while (temp != NULL) {
        printf(“%d “, temp->data);
        temp = temp->next;
    }
    printf(” “);
}

int main() {
    struct Node* head = NULL;
    int choice, value;

    while (1) {
        printf(“Enter your choice: “);
        printf(“1 – Insert at the front “);
        printf(“2 – Display and count nodes “);
        printf(“3 – Exit “);
        scanf(“%d”, &choice);

        switch (choice) {
            case 1:
                printf(“Enter data: “);
                scanf(“%d”, &value);
                insertFront(&head, value);
                break;
            case 2:
                printf(“Linked list contents: “);
                display(head);
                break;
            case 3:
                printf(“Exiting program. “);
                exit(0);
            default:
                printf(“Invalid choice. Try again. “);
        }
    }

    return 0;
}

3) Implementing a Circular Queue in C

#include <stdio.h>
#define MAX_SIZE 10

int cq[MAX_SIZE], front = -1, rear = -1;

int isFull() {
    return (rear + 1) % MAX_SIZE == front;
}

int isEmpty() {
    return front == -1;
}

void enQueue() {
    if (isFull()) {
        printf(“CIRCULAR QUEUE IS FULL “);
        return;
    }
    int item;
    printf(“Enter an element: “);
    scanf(“%d”, &item);
    if (front == -1)
        front = 0;
    rear = (rear + 1) % MAX_SIZE;
    cq[rear] = item;
    printf(“Inserted “);
}

void deQueue() {
    if (isEmpty()) {
        printf(“CIRCULAR QUEUE IS EMPTY “);
        return;
    }
    printf(“Deleted element: %d “, cq[front]);
    if (front == rear)
        front = rear = -1;
    else
        front = (front + 1) % MAX_SIZE;
}

void display() {
    if (isEmpty()) {
        printf(“CIRCULAR QUEUE IS EMPTY “);
        return;
    }
    printf(“Items: “);
    for (int i = front; i != rear; i = (i + 1) % MAX_SIZE)
        printf(“%d “, cq[i]);
    printf(“%d “, cq[rear]);
}

int main() {
    int ch;
    for (;;) {
        printf(“1. Insert 2. Delete 3. Display 4. Exit “);
        printf(“Enter your choice: “);
        scanf(“%d”, &ch);
        switch (ch) {
            case 1: enQueue(); break;
            case 2: deQueue(); break;
            case 3: display(); break;
            case 4: printf(“Exiting program. “); return 0;
            default: printf(“Invalid choice. Try again. “);
        }
    }
}

4) C Functions for Searching and Concatenating Singly Linked Lists

i) Search

void search(struct node *head, int key)
{
 struct node *temp = head;
 while(temp != NULL)
 {
 if(temp->data == key)
 printf(“key found”);
 temp = temp->next;
 }
 printf(“key not found”);
}


ii) Concatenation

void Concat(struct Node *first, struct Node *second)
{
 struct Node *p = first;
 while (p->next != NULL)
 {
 p = p->next;
 }
 p->next = second;
 second = NULL;
}

5) Defining and Representing a Binary Tree

Definition:

A binary tree T is defined as a finite set of nodes such that, ll  T is empty or  T consists of a root and two disjoint binary trees called the left sub tree and the right sub tree. ll wOfzdRptNwyywAAAABJRU5ErkJggg==

Array Representation:

 A tree can be represented using an array, which is called sequential representation. ll  The nodes are numbered from 1 to n, and one dimensional array can be used to store the nodes. ll  Position 0 of this array is left empty and the node numbered i is mapped to position i of the array ll IqZRJ0rik1dW6KkHSSNcIaZWW7hOeoNKqKIqiKIqSBXG1+sqn4E1rr+ifSwG9nbnAta8cL7ljprTdFe9ChFdmUPD0+CqtiqIoiqIoSqZHB2IpiqIoiqIomR6VVkVRFEVRFCXTo9KqKIqiKIqiZHpUWhVFURRFUZRMj0qroiiKoiiKkulRaVUURVEURVEyPSqtiqIoiqIoSqZHpVVRFEVRFEXJ9Ki0KoqiKIqiKJkelVZFURRFURQl06PSqiiKoiiKomRygP8DzD3tqXmXPT0AAAAASUVORK5CYII=

Linked Representation:

 Each node has three fields, ll  LeftChild – which contains the address of left subtree ll  RightChild – which contains the address of right subtree.ll   Data – which contains the actual information ll wdEUY5YgS0xcQAAAABJRU5ErkJggg==

6) Chained Hashing: Definition, Pros, Cons, and Example

Chained hashing, also known as separate chaining, is a technique used to resolve collisions in hash tables. When two or more keys hash to the same index (known as a collision), chained hashing handles these collisions by maintaining a linked list of all elements that hash to the same index. ll

Pros of Chained Hashing:

1. Simple Implementation: Chained hashing is relatively easy to implement. ll 2. Efficient Insertion and Deletion: Insertion and deletion operations are efficient in chained hashing. ll 3. Dynamic Data Sets: Chained hashing allows for dynamic resizing of the hash table.. ll 4. Adaptability: Chained hashing can handle a wide range of input data distributionsll

Cons of Chained Hashing:

1. Memory Overhead: Chained hashing can have a higher memory overhead compared to other collision resolution techniques. 2. Cache Inefficiency: cache inefficiency, particularly for large hash tables with long linked lists. 3. Performance Degradation: 4. Poor Worst-Case Performance: Chained hashing does not guarantee constant-time performance for lookup operations in the worst case ll

Constructing a Chained Hash Table

To insert the keys: 7, 24, 18, 52, 36, 54, 11, 23 in a chained hash table of 9 memory locations using h(k) = k mod m, we would proceed as follows: ll 1. **Calculate Hash Values:** Apply the hash function h(k) = k mod 9 to each key. ll 2. **Create Linked Lists:** For each hash value, create a linked list to store the keys that hash to that index. ll 3. **Insert Keys:** Insert each key into the corresponding linked list based on its hash value. ll The resulting chained hash table would look like this: ll Index | Keys ll ——- | ——– ll 0 | 7, 23 ll 1 | 11 ll 2 | 18 ll 3 | 24 ll 4 | 36 ll 5 | 52 ll 6 | 54 ll 7 | ll 8 | ll

7) Leftist Trees: Definition, C Declaration, and Example

A leftist tree is a binary tree such that if it is not empty, then shortest (LeftChild (x) >= shortest (RightChild (x ) for every internal node x. ll

C Declaration

 ll struct node llll  int data; ll struct node *left; ll struct node *right; ll int rank; ll 
}; wPJPbjILDvXpgAAAABJRU5ErkJggg==


8) Zjnbfq8+UhIAAAAASUVORK5CYII=