You are on page 1of 38

2-3 TREES

Pretest:

Explanation:
A 2-3 Tree is a type of tree in data structures in which every node of the tree is either a 2
node or 3 nodes. It is a special type of B-Tree with order 3.A 2 node in the tree is one which
has one data part and two child nodes. A 3 node in the tree is one which has two data parts
and three child nodes.

Quiz:
Post Test:

Code:

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

typedef struct Node {


    int keys[2];
    struct Node* children[3];
    int numKeys;
} Node;

Node* createNode(int key) {


    Node* newNode = (Node*)malloc(sizeof(Node));
    newNode->keys[0] = key;
    newNode->numKeys = 1;
    for (int i = 0; i < 3; i++) {
        newNode->children[i] = NULL;
    }
    return newNode;
}

void insert(Node** root, int key) {


    if (*root == NULL) {
        *root = createNode(key);
    } else {
        if ((*root)->numKeys == 1) {
            if (key < (*root)->keys[0]) {
                insert(&((*root)->children[0]), key);
            } else {
                insert(&((*root)->children[1]), key);
            }
        } else {
            if (key < (*root)->keys[0]) {
                insert(&((*root)->children[0]), key);
            } else if (key > (*root)->keys[1]) {
                insert(&((*root)->children[2]), key);
            } else {
                insert(&((*root)->children[1]), key);
            }
        }
    }
}

void printTree(Node* root) {


    if (root == NULL) {
        return;
    }
    if (root->numKeys == 1) {
        printTree(root->children[0]);
        printf("%d ", root->keys[0]);
        printTree(root->children[1]);
    } else {
        printTree(root->children[0]);
        printf("%d ", root->keys[0]);
        printTree(root->children[1]);
        printf("%d ", root->keys[1]);
        printTree(root->children[2]);
    }
}

int main() {
    Node* root = NULL;
    int numKeys;
    printf("Enter the number of keys: ");
    scanf("%d", &numKeys);

    printf("Enter the keys:\n");


    for (int i = 0; i < numKeys; i++) {
        int key;
        scanf("%d", &key);
        insert(&root, key);
    }

    printf("2-3 Tree in-order traversal: ");


    printTree(root);
    printf("\n");

    return 0;
}

Output:
BFS
Pre Test:

Explanation:
 Breadth-first search is a graph traversal algorithm that starts traversing the graph from the
root node and explores all the neighbouring nodes. Then, it selects the nearest node and
explores all the unexplored nodes. While using BFS for traversal, any node in the graph can
be considered as the root node.

There are many ways to traverse the graph, but among them, BFS is the most commonly used
approach. It is a recursive algorithm to search all the vertices of a tree or graph data structure.
BFS puts every vertex of the graph into two categories - visited and non-visited. It selects a
single node in a graph and, after that, visits all the nodes adjacent to the selected node.
Quiz:
Pretest:

Code:
#include<stdio.h>
#include<stdlib.h>
 
#define MAX 100  
 
#define initial 1
#define waiting 2
#define visited 3
 
int n;    
int adj[MAX][MAX];
int state[MAX];
void create_graph();
void BF_Traversal();
void BFS(int v);
 
int queue[MAX], front = -1,rear = -1;
void insert_queue(int vertex);
int delete_queue();
int isEmpty_queue();
 
int main()
{
create_graph();
BF_Traversal();
return 0;
}
 
void BF_Traversal()
{
int v;
for(v=0; v<n; v++)
state[v] = initial;
printf("Enter Start Vertex for BFS: \n");
scanf("%d", &v);
BFS(v);
}
 
void BFS(int v)
{
int i;
insert_queue(v);
state[v] = waiting;
while(!isEmpty_queue())
{
v = delete_queue( );
printf("%d ",v);
state[v] = visited;
for(i=0; i<n; i++)
{
if(adj[v][i] == 1 && state[i] == initial)
{
insert_queue(i);
state[i] = waiting;
}
}
}
printf("\n");
}
 
void insert_queue(int vertex)
{
if(rear == MAX-1)
printf("Queue Overflow\n");
else
{
if(front == -1)
front = 0;
rear = rear+1;
queue[rear] = vertex ;
}
}
 
int isEmpty_queue()
{
if(front == -1 || front > rear)
return 1;
else
return 0;
}
 
int delete_queue()
{
int delete_item;
if(front == -1 || front > rear)
{
printf("Queue Underflow\n");
exit(1);
}
delete_item = queue[front];
front = front+1;
return delete_item;
}
 
void create_graph()
{
int count,max_edge,origin,destin;
 
printf("Enter number of vertices : ");
scanf("%d",&n);
max_edge = n*(n-1);
 
for(count=1; count<=max_edge; count++)
{
printf("Enter edge %d( -1 -1 to quit ) : ",count);
scanf("%d %d",&origin,&destin);
 
if((origin == -1) && (destin == -1))
break;
 
if(origin>=n || destin>=n || origin<0 || destin<0)
{
printf("Invalid edge!\n");
count--;
}
else
{
adj[origin][destin] = 1;
}
}
}
Output:
DFS:
Pre Test:

Explanation:
Depth-First Search or DFS algorithm is a recursive algorithm that uses the
backtracking principle. It entails conducting exhaustive searches of all nodes by
moving forward if possible and backtracking, if necessary. To visit the next node, pop
the top node from the stack and push all of its nearby nodes into a stack.

Quiz:
Post Test:
Code:
#include<stdio.h>
#include<stdlib.h>
#define MAXVALUE 100
#define initialValue 1
#define visitedValue 2
int node;
int adjacent[MAXVALUE][MAXVALUE];
int state[MAXVALUE];
void DFSTraversal();
void DFS(int vertex);
void createGraph();
int stack[MAXVALUE];
int topValue = -1;
void pushNode(int vertex);
int popNode();
int isEmpty();
int main()
{
    createGraph();
    DFSTraversal();
}
void DFSTraversal()
{
    int vertex;
    for(vertex=0; vertex<node; vertex++)
    state[vertex]=initialValue;
    printf("\nEnter start node for DFS : ");
    scanf("%d",&vertex);
    DFS(vertex);
    printf("\n");
}
void DFS(int vertex)
{
int i;
pushNode(vertex);
while(!isEmpty())
{
vertex = popNode();
if(state[vertex]==initialValue)
{
printf("%d ",vertex);
state[vertex]=visitedValue;
}
for(i=node-1; i>=0; i--)
{
if(adjacent[vertex][i]==1 && state[i]==initialValue)
pushNode(i);
}
}
}
void pushNode(int vertex)
{
if(topValue == (MAXVALUE-1)){
printf("\n Error: Stack Overflow\n");
return;
}
topValue=topValue+1;
stack[topValue] = vertex;
}
int popNode()
{
int vertex;
if(topValue == -1)
{
printf("\nStack Underflow\n");
exit(1);
}
else
{
vertex = stack[topValue];
topValue=topValue-1;
return vertex;
}
}
int isEmpty( )
{
if(topValue == -1)
return 1;
else
return 0;
}
void createGraph()
{
int i,maxEdges,originNode,destinNode;
printf("\nEnter number of nodes : ");
scanf("%d",&node);
maxEdges=node*(node-1);
for(i=1;i<=maxEdges;i++)
{
printf("\nEnter edge %d( -3 -3 to quit ) : ",i);
scanf("%d %d",&originNode,&destinNode);
if( (originNode == -3) && (destinNode == -3) )
break;
if( originNode>= node || destinNode>= node || originNode<0 || destinNode<0)
{
printf("\nInvalid Edge/ Node!\n");
i--;
}
else
{
adjacent[originNode][destinNode] = 1;
}
}
}
Output:
DIJIKSTRA’S ALGORITHM
Pre Test:

Explanation:

Dijkstra’s Shortest Path Algorithm is a popular algorithm for finding the shortest path


between different nodes in a graph. It was proposed in 1956 by a computer scientist
named Edsger Wybe Dijkstra. Often used in routing, this algorithm is implemented as a
subroutine in another graph algorithm. In this post, I have included a pseudo code
and source code for Dijkstra’s Algorithm in C along with a brief introduction to this
algorithm.

Dijkstra’s algorithm finds the solution for the single-source shortest path problems only
when all the edge weights are non-negative on a weighted, directed graph. In other words,
the graph is weighted and directed with the first two integers being the number of vertices
and edges that must be followed by pairs of vertices having an edge between them.

In the source code for Dijkstra’s algorithm in C, the inputs are asked as the source, target,
and the weight of the path between two nodes. Before going through the source code for
Dijkstra’s algorithm in C, here’s a look at the algorithm itself and a pseudo code based on
the algorithm.
Quiz:
Post Test:
Code:
#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<string.h>
#include<math.h>
#define IN 99
#define N 6
int dijkstra(int cost[][N], int source, int target);
int main()
{
    int cost[N][N],i,j,w,ch,co;
    int source, target,x,y;
    printf("DIJKSTRA'S ALGORITHM in C\n");
    for(i=1;i< N;i++)
    for(j=1;j< N;j++)
    cost[i][j] = IN;
    for(i=1;i< N;i++)
    {
        for(j=i+1;j< N;j++)
        {
            printf("Enter the weight path between nodes %d and %d: ",i,j);
            scanf("%d",&w);
            cost [i][j] = cost[j][i] = w;
        }
        printf("\n");
    }
    printf("\nEnter the source:");
    scanf("%d", &source);
    printf("\nEnter the target");
    scanf("%d", &target);
    co = dijkstra(cost,source,target);
    printf("\nThe Shortest Path: %d",co);
}
int dijkstra(int cost[][N],int source,int target)
{
    int dist[N],prev[N],selected[N]={0},i,m,min,start,d,j;
    char path[N];
    for(i=1;i< N;i++)
    {
        dist[i] = IN;
        prev[i] = -1;
    }
    start = source;
    selected[start]=1;
    dist[start] = 0;
    while(selected[target] ==0)
    {
        min = IN;
        m = 0;
        for(i=1;i< N;i++)
        {
            d = dist[start] +cost[start][i];
            if(d< dist[i]&&selected[i]==0)
            {
                dist[i] = d;
                prev[i] = start;
            }
            if(min>dist[i] && selected[i]==0)
            {
                min = dist[i];
                m = i;
            }
        }
        start = m;
        selected[start] = 1;
    }
    start = target;
    j = 0;
    while(start != -1)
    {
        path[j++] = start + 64;
        start = prev[start];
    }
    path[j]='\0';
    strrev(path);
    printf("%s", path);
    return dist[target];
}
Output:
Floyd Warshall
Pre test:

Explanation:
The Floyd-Warshall algorithm is a shortest path algorithm for graphs. Like the Bellman-Ford
algorithm or the Dijkstra's algorithm, it computes the shortest path in a graph. However,
Bellman-Ford and Dijkstra are both single-source, shortest-path algorithms. This means they
only compute the shortest path from a single source. Floyd-Warshall, on the other hand,
computes the shortest distances between every pair of vertices in the input graph.

Post Test:
Code:
#include <stdio.h>
#define INF 999

void printMatrix(int nV,int matrix[][nV]);

void floydWarshall(int nV, int graph[][nV]) {


  int matrix[nV][nV], i, j, k;

  for (i = 0; i < nV; i++)


    for (j = 0; j < nV; j++)
      matrix[i][j] = graph[i][j];

  for (k = 0; k < nV; k++) {


    for (i = 0; i < nV; i++) {
      for (j = 0; j < nV; j++) {
        if (matrix[i][k] + matrix[k][j] < matrix[i][j])
          matrix[i][j] = matrix[i][k] + matrix[k][j];
      }
    }
  }
  printMatrix(nV,matrix);
}

void printMatrix(int nV,int matrix[][nV]) {


  for (int i = 0; i < nV; i++) {
    for (int j = 0; j < nV; j++) {
      if (matrix[i][j] == INF)
        printf("%4s", "INF");
      else
        printf("%4d", matrix[i][j]);
    }
    printf("\n");
  }
}
int main() {
  int nV;
  printf("Enter 999 as infinity\n");
  printf("Enter matrix size:");
  scanf("%d",&nV);
  int a[nV][nV];
  printf("Enter elements:\n");
  for(int i=0;i<nV;i++)
  {
      for(int j=0;j<nV;j++)
      {
          scanf("%d",&a[i][j]);
      }
  }
  floydWarshall(nV,a);
}

Output:
HASH TABLES
Pre Test:

Explanation:
Hashing is a technique used in computer science and cryptography to efficiently map data of
arbitrary size to fixed-size values, typically integers, in such a way that accessing, storing,
and retrieving data becomes faster and more efficient. The primary purpose of hashing is to
quickly locate a data record given its search key, by transforming the key into an index that
corresponds to the location of the data in a data structure, often referred to as a hash table or
hash map.
Quiz:
Code:

Closed Hashing(Linear Probing):


#include <stdio.h>
#include<stdlib.h>
#define TABLE_SIZE 10

int h[TABLE_SIZE]={NULL};

void insert()
{

 int key,index,i,flag=0,hkey;
 printf("\nenter a value to insert into hash table\n");
 scanf("%d",&key);
 hkey=key%TABLE_SIZE;
 for(i=0;i<TABLE_SIZE;i++)
    {

     index=(hkey+i)%TABLE_SIZE;

     if(h[index] == NULL)


     {
        h[index]=key;
         break;
     }

    }

    if(i == TABLE_SIZE)

     printf("\nelement cannot be inserted\n");


}
void search()
{

 int key, index,i,flag=0,hkey;


 printf("\nenter search element\n");
 scanf("%d",&key);
 hkey=key%TABLE_SIZE;
 for(i=0;i<TABLE_SIZE; i++)
 {
    index=(hkey+i)%TABLE_SIZE;
    if(h[index]==key)
    {
      printf("value is found at index %d",index);
      break;
    }
 }
  if(i == TABLE_SIZE)
    printf("\n value is not found\n");
}
void display()
{

  int i;

  printf("\nelements in the hash table are \n");

  for(i=0;i< TABLE_SIZE; i++)

  printf("\nat index %d \t value =  %d",i,h[i]);

}
main()
{
    int opt,i;
    while(1)
    {
        printf("\nPress 1. Insert\t 2. Display \t3. Search \t4.Exit \n");
        scanf("%d",&opt);
        switch(opt)
        {
            case 1:
                insert();
                break;
            case 2:
                display();
                break;
            case 3:
                search();
                break;
            case 4:exit(0);
        }
    }
}

Output:
Code: (Open Hashing)
#include <stdio.h>
#include <stdlib.h>

#define TABLE_SIZE 10

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

struct Node* h[TABLE_SIZE] = {NULL};

void insert() {
int key;
printf("\nEnter a value to insert into hash table: ");
scanf("%d", &key);

int hkey = key % TABLE_SIZE;

struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));


newNode->data = key;
newNode->next = NULL;

if (h[hkey] == NULL) {
h[hkey] = newNode;
} else {
struct Node* temp = h[hkey];
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
}

void search() {
int key;
printf("\nEnter the search element: ");
scanf("%d", &key);

int hkey = key % TABLE_SIZE;

struct Node* temp = h[hkey];


while (temp != NULL) {
if (temp->data == key) {
printf("Value is found in the hash table.\n");
return;
}
temp = temp->next;
}

printf("Value is not found in the hash table.\n");


}

void display() {
printf("\nElements in the hash table are:\n");

for (int i = 0; i < TABLE_SIZE; i++) {


printf("At index %d: ", i);
struct Node* temp = h[i];
while (temp != NULL) {
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
}

int main() {
int opt;

while (1) {
printf("\nPress 1. Insert\t 2. Display \t3. Search \t4. Exit\n");
scanf("%d", &opt);

switch (opt) {
case 1:
insert();
break;
case 2:
display();
break;
case 3:
search();
break;
case 4:
exit(0);
}
}

return 0;
}
Output:
Topological Sort:
Pre Test:

Explanation:
Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering of vertices such that
for every directed edge u v, vertex u comes before v in the ordering. Topological Sorting for
a graph is not possible if the graph is not a DAG. In DFS, we print a vertex and then
recursively call DFS for its adjacent vertices. In topological sorting, we need to print a vertex
before its adjacent vertices. In topological sorting,

Quiz:
Post Test:

Code:
#include <stdio.h>
 
int main(){
int i,j,k,n,a[10][10],indeg[10],flag[10],count=0;
 
printf("Enter the no of vertices:\n");
scanf("%d",&n);
 
printf("Enter the adjacency matrix:\n");
for(i=0;i<n;i++){
printf("Enter row %d\n",i+1);
for(j=0;j<n;j++)
scanf("%d",&a[i][j]);
}
 
for(i=0;i<n;i++){
        indeg[i]=0;
        flag[i]=0;
    }
 
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            indeg[i]=indeg[i]+a[j][i];
 
    printf("\nThe topological order is:");
 
    while(count<n){
        for(k=0;k<n;k++){
            if((indeg[k]==0) && (flag[k]==0)){
                printf("%d ",(k+1));
                flag [k]=1;
            }
 
            for(i=0;i<n;i++){
                if(a[i][k]==1)
                    indeg[k]--;
            }
        }
        count++;
    }
    return 0;
}
Output:

You might also like