DSA Unit 3
/notesGraphs and Trees
Binary Search Tree (BST)
Using Linked List (Malloc)
struct Node* createNode(int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
struct Node* insert(struct Node* root, int value) {
if (root == NULL) {
return createNode(value);
}
if (value > root->data) {
root->right = insert(root->right, value);
}
else if (value < root->data) {
root->left = insert(root->left, value);
}
return root;
}
// Deletion
struct Node* findMin(struct Node* node) {
struct Node* current = node;
while (current && current->left != NULL) {
current = current->left;
}
return current;
}
struct Node* deleteNode(struct Node* root, int value) {
if (root == NULL) return root;
if (value < root->data) {
root->left = deleteNode(root->left, value);
}
else if (value > root->data) {
root->right = deleteNode(root->right, value);
}
else {
// Case 1 & 2: One Child or No Child
if (root->left == NULL) {
struct Node* temp = root->right;
free(root);
return temp;
}
else if (root->right == NULL) {
struct Node* temp = root->left;
free(root);
return temp;
}
// Case 3: Two Children
struct Node* temp = findMin(root->right);
root->data = temp->data;
root->right = deleteNode(root->right, temp->data);
}
return root;
} BST Using Array
In Zero-Based Indexing (Root at 0): For parent node at i, left child at 2i + 1 and right child at 2i + 2. Parent of node at i is [(i-1)/2].
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 20
int tree[MAX_SIZE];
void initTree() {
for (int i = 0; i < MAX_SIZE; i++) {
tree[i] = 0;
}
}
void insert(int value) {
int currentIndex = 0;
while (currentIndex < MAX_SIZE) {
if (tree[currentIndex] == 0) {
tree[currentIndex] = value;
printf("Inserted %d at index %d\n", value, currentIndex);
return;
}
if (value < tree[currentIndex]) {
currentIndex = 2 * currentIndex + 1;
} else {
currentIndex = 2 * currentIndex + 2;
}
}
printf("Error: Tree is full, cannot insert %d\n", value);
}
void inorder(int index) {
if (index >= MAX_SIZE || tree[index] == 0) {
return;
}
inorder(2 * index + 1);
printf("%d ", tree[index]);
inorder(2 * index + 2);
} Binary Expression Trees
Binary expression trees represent mathematical expressions where operands are leaves and operators are internal nodes. We can construct them from postfix expressions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node {
char data;
struct Node *left, *right;
};
#define MAX_STACK_SIZE 100
struct Node* stack[MAX_STACK_SIZE];
int top = -1;
void push(struct Node* node) {
if (top < MAX_STACK_SIZE - 1) {
stack[++top] = node;
}
}
struct Node* pop() {
if (top >= 0) {
return stack[top--];
}
return NULL;
}
struct Node* newNode(char val) {
struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
temp->data = val;
temp->left = temp->right = NULL;
return temp;
}
int isOperand(char c) {
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
}
struct Node* buildExpressionTree(char* postfix) {
int i = 0;
while (postfix[i] != '\0') {
char symbol = postfix[i];
if (isOperand(symbol)) {
push(newNode(symbol));
} else {
struct Node* operatorNode = newNode(symbol);
operatorNode->right = pop();
operatorNode->left = pop();
push(operatorNode);
}
i++;
}
return pop();
}
void inorder(struct Node* root) {
if (root) {
if (!isOperand(root->data)) printf("(");
inorder(root->left);
printf("%c", root->data);
inorder(root->right);
if (!isOperand(root->data)) printf(")");
}
} Threaded Binary Trees
Full Threaded Binary Tree (Inorder)
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *left;
struct Node *right;
int lthread;
int rthread;
};
struct Node* leftmost(struct Node* node) {
if (node == NULL) return NULL;
while (node->lthread == 0) {
node = node->left;
}
return node;
}
struct Node* InorderSuccessor(struct Node* ptr) {
if (ptr->rthread == 1) {
return ptr->right;
}
ptr = ptr->right;
while (ptr != NULL && ptr->lthread == 0) {
ptr = ptr->left;
}
return ptr;
}
void inorderTraversal(struct Node* root) {
if (root == NULL) {
printf("Tree is empty.\n");
return;
}
struct Node* ptr = leftmost(root);
while (ptr != NULL) {
printf("%d ", ptr->data);
ptr = InorderSuccessor(ptr);
}
printf("\n");
} Right Threaded Binary Tree (Inorder)
struct Node {
int data;
struct Node *left;
struct Node *right;
int rthread;
};
struct Node* leftmost(struct Node* node) {
if (node == NULL) return NULL;
while (node->left != NULL) {
node = node->left;
}
return node;
}
struct Node* InorderSuccessor(struct Node* ptr) {
if (ptr->rthread == 1) {
return ptr->right;
}
ptr = ptr->right;
while (ptr != NULL && ptr->left != NULL) {
ptr = ptr->left;
}
return ptr;
}
void inorderTraversal(struct Node* root) {
if (root == NULL) {
printf("Tree is empty.\n");
return;
}
struct Node* ptr = leftmost(root);
while (ptr != NULL) {
printf("%d ", ptr->data);
ptr = InorderSuccessor(ptr);
}
printf("\n");
} Heaps
Heaps are special binary trees with two key properties:
- Structural Property: Must be a complete binary tree (except last level may not be full)
- Heap Property: Parent value should be greater than (max heap) or less than (min heap) both children
- No rule that left > right
- If there are n nodes, height is ⌊log₂n⌋
- First n/2 elements are parents, last n/2 are children
Max Heap - Bottom-Up Heapification
#include <stdio.h>
#include <stdlib.h>
void heapBottomUp(int H[], int n) {
for (int i = n / 2; i >= 1; i--) {
int k = i;
int v = H[k];
int heap = 0;
while (heap == 0 && 2 * k <= n) {
int j = 2 * k;
if (j < n) {
if (H[j] < H[j + 1]) {
j = j + 1;
}
}
if (v >= H[j]) {
heap = 1;
} else {
H[k] = H[j];
k = j;
}
}
H[k] = v;
}
}
int main() {
int arr[10] = {0, 4, 1, 3, 2, 16, 9, 10, 14, 8};
int N = 9;
printf("Original array (1-indexed): ");
for (int i = 1; i <= N; i++) {
printf("%d ", arr[i]);
}
printf("\n");
heapBottomUp(arr, N);
printf("Max Heap array (1-indexed): ");
for (int i = 1; i <= N; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
} Max Heap - Top-Down Approach + Heap Sort
#include <stdio.h>
#define MAX 50
int main() {
int a[MAX];
int i, c, p, n, elt;
printf("Enter the number of elements (max %d):\n", MAX);
if (scanf("%d", &n) != 1 || n <= 0 || n > MAX) {
printf("Invalid number of elements.\n");
return 1;
}
printf("Enter the elements:\n");
for (i = 0; i < n; i++) {
if (scanf("%d", &a[i]) != 1) {
printf("Error reading element.\n");
return 1;
}
}
// HEAPIFY: BUILD MAX HEAP (Top-Down / Sift-Up)
for (i = 1; i < n; i++) {
elt = a[i];
c = i;
p = (c - 1) / 2;
while (c > 0 && a[p] < elt) {
a[c] = a[p];
c = p;
p = (c - 1) / 2;
}
a[c] = elt;
}
printf("\nElements of Max Heap:\n");
for (i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
// HEAP SORT
for (i = n - 1; i > 0; i--) {
elt = a[i];
a[i] = a[0];
a[0] = elt;
elt = a[0];
p = 0;
while (2 * p + 1 < i) {
c = 2 * p + 1;
if (c + 1 < i && a[c] < a[c + 1]) {
c = c + 1;
}
if (elt >= a[c]) {
break;
}
a[p] = a[c];
p = c;
}
a[p] = elt;
}
printf("\nSorted elements (Heap sort):\n");
for (i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
} Min Heap - Bottom-Up
void heapBottomUpMin(int H[], int n) {
for (int i = n / 2; i >= 1; i--) {
int k = i;
int v = H[k];
int heap = 0;
while (heap == 0 && 2 * k <= n) {
int j = 2 * k;
if (j < n) {
if (H[j] > H[j + 1]) {
j = j + 1;
}
}
if (v <= H[j]) {
heap = 1;
} else {
H[k] = H[j];
k = j;
}
}
H[k] = v;
}
} Min Heap - Top-Down
// HEAPIFY: BUILD MIN HEAP (Top-Down / Sift-Up)
for (i = 1; i < n; i++) {
elt = a[i];
c = i;
p = (c - 1) / 2;
while (c > 0 && a[p] > elt) {
a[c] = a[p];
c = p;
p = (c - 1) / 2;
}
a[c] = elt;
}
// MIN HEAP SORT
for (i = n - 1; i > 0; i--) {
elt = a[i];
a[i] = a[0];
a[0] = elt;
elt = a[0];
p = 0;
while (2 * p + 1 < i) {
c = 2 * p + 1;
if (c + 1 < i && a[c] > a[c + 1]) {
c = c + 1;
}
if (elt <= a[c]) {
break;
}
a[p] = a[c];
p = c;
}
a[p] = elt;
} Priority Queues Using Heaps
Descending Priority Queue (Max Heap)
#include <stdio.h>
#include <stdlib.h>
#define MAX 50
typedef struct priq {
int pq[MAX];
int n;
} PQ;
void init(PQ *pt) {
pt->n = 0;
}
void disp(PQ *pt) {
int i;
printf("Queue elements: ");
for (i = 0; i < pt->n; i++) {
printf("%d ", pt->pq[i]);
}
printf("\n");
}
int enqueue(PQ *pt, int e) {
int p, c;
if (pt->n >= MAX) return 0;
c = pt->n;
p = (c - 1) / 2;
while (c > 0 && pt->pq[p] < e) {
pt->pq[c] = pt->pq[p];
c = p;
p = (c - 1) / 2;
}
pt->pq[c] = e;
pt->n = pt->n + 1;
return 1;
}
int dequeue(PQ *pt, int *ele) {
int p, c;
if (pt->n == 0) return 0;
*ele = pt->pq[0];
int elt = pt->pq[pt->n - 1];
pt->n = pt->n - 1;
p = 0;
c = 2 * p + 1;
if (c + 1 < pt->n && pt->pq[c] < pt->pq[c + 1]) {
c = c + 1;
}
if (c >= pt->n) {
c = -1;
}
while (c >= 0 && elt < pt->pq[c]) {
pt->pq[p] = pt->pq[c];
p = c;
c = 2 * p + 1;
if (c + 1 < pt->n && pt->pq[c + 1] > pt->pq[c]) {
c = c + 1;
}
if (c >= pt->n) c = -1;
}
pt->pq[p] = elt;
return 1;
} Ascending Priority Queue (Min Heap)
int enqueue_min(PQ *pt, int e) {
int p, c;
if (pt->n >= MAX) return 0;
c = pt->n;
p = (c - 1) / 2;
while (c > 0 && pt->pq[p] > e) {
pt->pq[c] = pt->pq[p];
c = p;
p = (c - 1) / 2;
}
pt->pq[c] = e;
pt->n = pt->n + 1;
return 1;
}
int dequeue_min(PQ *pt, int *ele) {
int p, c;
if (pt->n == 0) return 0;
*ele = pt->pq[0];
int elt = pt->pq[pt->n - 1];
pt->n = pt->n - 1;
p = 0;
c = 2 * p + 1;
if (c + 1 < pt->n && pt->pq[c] > pt->pq[c + 1]) {
c = c + 1;
}
if (c >= pt->n) {
c = -1;
}
while (c >= 0 && elt > pt->pq[c]) {
pt->pq[p] = pt->pq[c];
p = c;
c = 2 * p + 1;
if (c + 1 < pt->n && pt->pq[c + 1] < pt->pq[c]) {
c = c + 1;
}
if (c >= pt->n) c = -1;
}
pt->pq[p] = elt;
return 1;
}