0% found this document useful (0 votes)
15 views23 pages

Dsa Unit 2 (Ai)

The document provides an overview of trees in C++, detailing their properties, types, and implementations. It covers binary trees, binary search trees, and expression trees, including their structures, traversal methods, and manipulation algorithms such as insertion and deletion. Additionally, it discusses the applications and advantages of these data structures in various fields like databases and compilers.

Uploaded by

dheerajjha0276
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views23 pages

Dsa Unit 2 (Ai)

The document provides an overview of trees in C++, detailing their properties, types, and implementations. It covers binary trees, binary search trees, and expression trees, including their structures, traversal methods, and manipulation algorithms such as insertion and deletion. Additionally, it discusses the applications and advantages of these data structures in various fields like databases and compilers.

Uploaded by

dheerajjha0276
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 23

Trees in C++

A tree is a nonlinear data structure consisting of nodes connected by edges. It is a


widely used hierarchical data structure that follows a parent-child relationship.

Basic Properties of Trees


1.​ Root Node: The topmost node of the tree.
2.​ Parent & Child Nodes: A node that has children is called a parent node.
3.​ Siblings: Nodes that share the same parent.
4.​ Leaf Node: A node with no children.
5.​ Degree of a Node: The number of children a node has.
6.​ Height of a Tree: The number of edges on the longest path from root to a leaf.
7.​ Depth of a Node: The number of edges from the root to that node.
8.​ Subtree: Any node along with its descendants forms a subtree.
9.​ Binary Tree: A tree where each node has at most two children.
10.​Balanced Tree: A tree in which the height is minimized for efficient operations.

Implementation of Tree in C++


Trees can be implemented in C++ using structs and classes.

1. Basic Tree Node Structure


#include <iostream>
#include <vector>
using namespace std;

class TreeNode {
public:
int data;
vector<TreeNode*> children; // For general trees

TreeNode(int val) {
data = val;
}
};

2. Binary Tree Implementation

A Binary Tree is a tree where each node has at most two children.

#include <iostream>
using namespace std;

class Node {
public:
int data;
Node* left;
Node* right;

Node(int val) {
data = val;
left = right = nullptr;
}
};

// Preorder Traversal (Root -> Left -> Right)


void preorder(Node* root) {
if (root == nullptr) return;
cout << root->data << " ";
preorder(root->left);
preorder(root->right);
}

// Inorder Traversal (Left -> Root -> Right)


void inorder(Node* root) {
if (root == nullptr) return;
inorder(root->left);
cout << root->data << " ";
inorder(root->right);
}

// Postorder Traversal (Left -> Right -> Root)


void postorder(Node* root) {
if (root == nullptr) return;
postorder(root->left);
postorder(root->right);
cout << root->data << " ";
}

int main() {
Node* root = new Node(1);
root->left = new Node(2);
root->right = new Node(3);
root->left->left = new Node(4);
root->left->right = new Node(5);

cout << "Preorder: ";


preorder(root);
cout << "\nInorder: ";
inorder(root);
cout << "\nPostorder: ";
postorder(root);

return 0;
}

Types of Trees
1.​ Binary Search Tree (BST) - A binary tree where left child < root < right child.
2.​ Balanced Trees (AVL, Red-Black Tree) - Ensures logarithmic time complexity.
3.​ Heap (Min-Heap, Max-Heap) - A complete binary tree used for priority queues.
4.​ Trie (Prefix Tree) - Used for storing and searching strings efficiently.
5.​ Segment Tree & Fenwick Tree - Used for range queries.

Let me know if you need more explanations on any specific type of tree! 🚀
Binary Trees in C++
A Binary Tree is a hierarchical data structure in which each node has at most two
children:

●​ Left Child
●​ Right Child

Each node stores a value and pointers to its left and right children.

Types of Binary Trees


1.​ Full Binary Tree: Every node has either 0 or 2 children.
2.​ Perfect Binary Tree: A binary tree in which all leaf nodes are at the same level.
3.​ Complete Binary Tree: All levels are completely filled except the last level,
which is filled from left to right.
4.​ Balanced Binary Tree: The difference between the heights of left and right
subtrees of every node is at most 1 (e.g., AVL Tree).
5.​ Binary Search Tree (BST): A binary tree in which left child < root < right child.

Binary Tree Representation in C++


Binary trees are implemented using classes and pointers in C++.

Node Structure
#include <iostream>
using namespace std;

class Node {
public:
int data;
Node* left;
Node* right;

Node(int val) {
data = val;
left = right = nullptr;
}
};
Binary Tree Traversal
Binary tree traversal means visiting every node of the tree in a specific order.

1. Depth-First Search (DFS)


a) Preorder Traversal (Root → Left → Right)

1.​ Visit the root.


2.​ Traverse the left subtree.
3.​ Traverse the right subtree.

void preorder(Node* root) {


if (root == nullptr) return;
cout << root->data << " ";
preorder(root->left);
preorder(root->right);
}

b) Inorder Traversal (Left → Root → Right)

1.​ Traverse the left subtree.


2.​ Visit the root.
3.​ Traverse the right subtree.

void inorder(Node* root) {


if (root == nullptr) return;
inorder(root->left);
cout << root->data << " ";
inorder(root->right);
}

c) Postorder Traversal (Left → Right → Root)

1.​ Traverse the left subtree.


2.​ Traverse the right subtree.
3.​ Visit the root.

void postorder(Node* root) {


if (root == nullptr) return;
postorder(root->left);
postorder(root->right);
cout << root->data << " ";
}

2. Breadth-First Search (BFS) or Level Order Traversal


This traversal visits nodes level by level.

#include <queue>

void levelOrder(Node* root) {


if (root == nullptr) return;

queue<Node*> q;
q.push(root);

while (!q.empty()) {
Node* temp = q.front();
q.pop();
cout << temp->data << " ";

if (temp->left) q.push(temp->left);
if (temp->right) q.push(temp->right);
}
}

Binary Tree Manipulation Algorithms


1. Insertion in Binary Tree
Insertion follows different rules depending on the type of binary tree.

Insertion in a Binary Search Tree (BST)


Node* insertBST(Node* root, int val) {
if (root == nullptr) return new Node(val);

if (val < root->data)


root->left = insertBST(root->left, val);
else
root->right = insertBST(root->right, val);

return root;
}

2. Deletion in a Binary Search Tree


There are three cases when deleting a node in a BST:

1.​ Node has no child (Leaf Node) → Simply delete it.


2.​ Node has one child → Replace it with its child.
3.​ Node has two children → Replace with its inorder successor (smallest in the
right subtree).

Node* findMin(Node* root) {


while (root->left != nullptr) root = root->left;
return root;
}

Node* deleteNode(Node* root, int val) {


if (root == nullptr) return root;

if (val < root->data)


root->left = deleteNode(root->left, val);
else if (val > root->data)
root->right = deleteNode(root->right, val);
else {
// Case 1 & 2: Node with one or no child
if (root->left == nullptr) {
Node* temp = root->right;
delete root;
return temp;
}
else if (root->right == nullptr) {
Node* temp = root->left;
delete root;
return temp;
}

// Case 3: Node with two children


Node* temp = findMin(root->right);
root->data = temp->data;
root->right = deleteNode(root->right, temp->data);
}
return root;
}

3. Finding Height of a Binary Tree


int findHeight(Node* root) {
if (root == nullptr) return -1; // If height is considered as edges
return max(findHeight(root->left), findHeight(root->right)) + 1;
}

4. Searching in a Binary Search Tree


bool searchBST(Node* root, int key) {
if (root == nullptr) return false;
if (root->data == key) return true;
if (key < root->data) return searchBST(root->left, key);
return searchBST(root->right, key);
}
Example Usage
int main() {
Node* root = nullptr;

root = insertBST(root, 10);


insertBST(root, 5);
insertBST(root, 15);
insertBST(root, 3);
insertBST(root, 7);
insertBST(root, 12);
insertBST(root, 18);

cout << "Inorder Traversal: ";


inorder(root);

cout << "\nDeleting node 10\n";


root = deleteNode(root, 10);

cout << "Inorder Traversal after deletion: ";


inorder(root);

return 0;
}

Conclusion
●​ Binary trees efficiently store hierarchical data.
●​ Traversals (DFS & BFS) help in tree processing.
●​ Manipulation operations like insertion, deletion, and searching are crucial for
tree-based applications.

Would you like more details on any specific part? 😊


Expression Trees
An Expression Tree is a binary tree used to represent mathematical expressions. The
operands (numbers or variables) are stored in the leaf nodes, while the operators (+,
-, *, /) are stored in the internal nodes.

Structure of an Expression Tree


●​ Operands (Numbers or Variables) → Stored as leaf nodes.
●​ Operators (+, -, *, /) → Stored as internal nodes.
●​ The tree is evaluated using postorder traversal (Left → Right → Root).

Example: Expression 3 + 5 * 2
+
/\
3 *
/\
5 2

●​ The multiplication (*) is performed before the addition (+) due to operator
precedence.

Building an Expression Tree


An expression tree is usually constructed from Postfix (Reverse Polish Notation)
expressions.

Algorithm to Construct an Expression Tree from Postfix Expression

1.​ Scan the postfix expression from left to right.


2.​ If the character is an operand, create a node and push it to the stack.
3.​ If the character is an operator, pop two nodes from the stack, create a new node
with the operator, and set the popped nodes as its left and right children.
4.​ Push the new node back to the stack.
5.​ After scanning, the stack contains the root of the expression tree.

C++ Implementation
#include <iostream>
#include <stack>
using namespace std;

// Node structure for the expression tree


class Node {
public:
char data;
Node* left;
Node* right;

Node(char val) {
data = val;
left = right = nullptr;
}
};

// Function to check if a character is an operator


bool isOperator(char c) {
return (c == '+' || c == '-' || c == '*' || c == '/');
}

// Function to construct an expression tree from a postfix expression


Node* constructTree(string postfix) {
stack<Node*> st;

for (char c : postfix) {


if (!isOperator(c)) {
st.push(new Node(c)); // Operand node
} else {
Node* newNode = new Node(c);
newNode->right = st.top(); st.pop();
newNode->left = st.top(); st.pop();
st.push(newNode);
}
}

return st.top();
}

// Postorder traversal (used for evaluation)


void postorder(Node* root) {
if (root == nullptr) return;
postorder(root->left);
postorder(root->right);
cout << root->data << " ";
}

// Inorder traversal (prints the expression in human-readable form)


void inorder(Node* root) {
if (root == nullptr) return;
if (isOperator(root->data)) cout << "(";
inorder(root->left);
cout << root->data;
inorder(root->right);
if (isOperator(root->data)) cout << ")";
}

int main() {
string postfix = "35*2+"; // Represents: (3 + (5 * 2))
Node* root = constructTree(postfix);

cout << "Inorder Expression: ";


inorder(root);
cout << "\nPostorder Traversal: ";
postorder(root);

return 0;
}

Output
Inorder Expression: (3+(5*2))
Postorder Traversal: 3 5 2 * +

Usage of Expression Trees


1.​ Evaluating Mathematical Expressions
○​ Used in compilers, calculators, and interpreters to evaluate
expressions efficiently.
2.​ Syntax Trees in Compilers
○​ Used for parsing expressions and generating machine code.
3.​ Symbolic Mathematics
○​ Used in computer algebra systems like Mathematica and SymPy.
4.​ Expression Optimization
○​ Helps optimize expressions by reducing unnecessary computations.
5.​ Boolean Algebra
○​ Used in logical expressions and digital circuit design.

Binary Search Trees (BST)


A Binary Search Tree (BST) is a binary tree with the following properties:

1.​ The left subtree of a node contains only nodes with values less than the node.
2.​ The right subtree of a node contains only nodes with values greater than the
node.
3.​ The left and right subtrees must also be binary search trees.

Example BST
10
/ \
5 15
/\ / \
2 7 12 18

●​ Left subtree: All nodes are < 10


●​ Right subtree: All nodes are > 10

Operations on a BST
1. Insertion in a BST
Node* insertBST(Node* root, int val) {
if (root == nullptr) return new Node(val);

if (val < root->data)


root->left = insertBST(root->left, val);
else
root->right = insertBST(root->right, val);

return root;
}

2. Searching in a BST
bool searchBST(Node* root, int key) {
if (root == nullptr) return false;
if (root->data == key) return true;
if (key < root->data) return searchBST(root->left, key);
return searchBST(root->right, key);
}

3. Deletion in a BST

●​ Case 1: Node has no child → Simply remove it.


●​ Case 2: Node has one child → Replace it with its child.
●​ Case 3: Node has two children → Replace it with the inorder successor
(smallest node in the right subtree).

Node* findMin(Node* root) {


while (root->left != nullptr) root = root->left;
return root;
}

Node* deleteNode(Node* root, int val) {


if (root == nullptr) return root;

if (val < root->data)


root->left = deleteNode(root->left, val);
else if (val > root->data)
root->right = deleteNode(root->right, val);
else {
if (root->left == nullptr) {
Node* temp = root->right;
delete root;
return temp;
} else if (root->right == nullptr) {
Node* temp = root->left;
delete root;
return temp;
}

Node* temp = findMin(root->right);


root->data = temp->data;
root->right = deleteNode(root->right, temp->data);
}
return root;
}

Advantages of BST
1.​ Efficient Searching - O(log N) time complexity for balanced BSTs.
2.​ Sorted Order - Inorder traversal of BST gives elements in ascending order.
3.​ Dynamic Set Operations - Supports insert, delete, and search efficiently.

Applications of BST
1.​ Database Indexing
○​ BSTs (like B-Trees) are used in database indexing.
2.​ Auto-suggestions in Search Engines
○​ BSTs help in prefix-based search.
3.​ Symbol Table in Compilers
○​ Used to store variable/function names efficiently.
4.​ Memory Management
○​ Used in malloc() / free() memory allocation algorithms.
5.​ File System Organization
○​ File system hierarchies follow BST-like structures.
Conclusion
●​ Expression Trees are used in expression evaluation and compiler syntax trees.
●​ BSTs are efficient for searching, insertion, and deletion.
●​ BSTs have applications in databases, memory management, and search
engines.

Would you like me to cover Balanced BSTs (AVL, Red-Black Trees) as well? 🚀
Heaps and Priority Queues in C++
A Heap is a complete binary tree where the values are arranged based on a specific
order property. It is mainly used for priority queues and efficient retrieval of the
minimum or maximum element.

Types of Heaps
1.​ Min-Heap
○​ The smallest element is at the root.
○​ Every parent node is smaller than or equal to its children.

Example:​
10
/ \
20 15
/ \

○​
2.​ 30 40​

3.​ Max-Heap
○​ The largest element is at the root.
○​ Every parent node is greater than or equal to its children.

Example:​
50
/ \
30 40
/ \

○​
4.​ 20 10​

Heap Operations
1.​ Insertion (O(log N))​

○​ Insert the element at the end of the heap.


○​ Heapify Up: Swap with its parent until the heap property is restored.
2.​ Deletion (O(log N))​

○​ The root element (min or max) is removed.


○​ The last element is moved to the root.
○​ Heapify Down: Swap with the smaller (min-heap) or larger (max-heap)
child.
3.​ Heapify (O(log N))​

○​ Restores the heap property by adjusting the elements.


4.​ Extract Min / Max (O(log N))​

○​ Removes and returns the minimum (Min-Heap) or maximum (Max-Heap)


element.

Implementation of Min-Heap in C++


A heap is efficiently implemented using an array or a vector.

1. Min-Heap using Array


#include <iostream>
#include <vector>
using namespace std;
class MinHeap {
vector<int> heap;

void heapifyUp(int index) {


int parent = (index - 1) / 2;
if (index > 0 && heap[index] < heap[parent]) {
swap(heap[index], heap[parent]);
heapifyUp(parent);
}
}

void heapifyDown(int index) {


int left = 2 * index + 1;
int right = 2 * index + 2;
int smallest = index;

if (left < heap.size() && heap[left] < heap[smallest]) smallest = left;


if (right < heap.size() && heap[right] < heap[smallest]) smallest = right;

if (smallest != index) {
swap(heap[index], heap[smallest]);
heapifyDown(smallest);
}
}

public:
void insert(int val) {
heap.push_back(val);
heapifyUp(heap.size() - 1);
}

int extractMin() {
if (heap.empty()) return -1;
int minVal = heap[0];
heap[0] = heap.back();
heap.pop_back();
heapifyDown(0);
return minVal;
}
void display() {
for (int val : heap) cout << val << " ";
cout << endl;
}
};

int main() {
MinHeap h;
h.insert(10);
h.insert(20);
h.insert(5);
h.insert(30);
h.insert(15);

cout << "Min-Heap: ";


h.display();

cout << "Extracted Min: " << h.extractMin() << endl;

cout << "After Extraction: ";


h.display();

return 0;
}

Output
Min-Heap: 5 15 10 30 20
Extracted Min: 5
After Extraction: 10 15 20 30

Implementation of Max-Heap in C++


Same logic as Min-Heap, but in heapifyUp and heapifyDown, we check greater
values instead of smaller.

class MaxHeap {
vector<int> heap;

void heapifyUp(int index) {


int parent = (index - 1) / 2;
if (index > 0 && heap[index] > heap[parent]) {
swap(heap[index], heap[parent]);
heapifyUp(parent);
}
}

void heapifyDown(int index) {


int left = 2 * index + 1;
int right = 2 * index + 2;
int largest = index;

if (left < heap.size() && heap[left] > heap[largest]) largest = left;


if (right < heap.size() && heap[right] > heap[largest]) largest = right;

if (largest != index) {
swap(heap[index], heap[largest]);
heapifyDown(largest);
}
}

public:
void insert(int val) {
heap.push_back(val);
heapifyUp(heap.size() - 1);
}

int extractMax() {
if (heap.empty()) return -1;
int maxVal = heap[0];
heap[0] = heap.back();
heap.pop_back();
heapifyDown(0);
return maxVal;
}
};
Priority Queue in C++
A Priority Queue is an abstract data structure similar to a queue, but elements are
processed based on priority rather than order of arrival.

Types of Priority Queues


1.​ Max Priority Queue → Returns the largest element first.
2.​ Min Priority Queue → Returns the smallest element first.

Priority Queue using STL (C++ Standard Library)

C++ provides a built-in priority_queue container.

1. Max Priority Queue (Default)


#include <iostream>
#include <queue>
using namespace std;

int main() {
priority_queue<int> pq; // Max-Heap by default

pq.push(10);
pq.push(20);
pq.push(5);
pq.push(30);

cout << "Max Priority Queue: ";


while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
return 0;
}

Output
Max Priority Queue: 30 20 10 5

2. Min Priority Queue

Use priority_queue<int, vector<int>, greater<int>> to create a


Min-Heap.

priority_queue<int, vector<int>, greater<int>> pq;

Min Priority Queue Example


#include <iostream>
#include <queue>
using namespace std;

int main() {
priority_queue<int, vector<int>, greater<int>> pq; // Min-Heap

pq.push(10);
pq.push(20);
pq.push(5);
pq.push(30);

cout << "Min Priority Queue: ";


while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
return 0;
}

Output
Min Priority Queue: 5 10 20 30

Applications of Heaps and Priority Queues


1.​ Dijkstra's Shortest Path Algorithm (Priority Queue is used for the shortest
path).
2.​ Heap Sort (Efficient O(N log N) sorting algorithm).
3.​ Job Scheduling (CPU scheduling, task scheduling).
4.​ Graph Algorithms (Prim's Minimum Spanning Tree).
5.​ Data Compression (Huffman Coding) (Used in text compression like ZIP files).
6.​ Dynamic Median Finding (Used in stock price tracking).
7.​ Memory Management (Garbage collection in programming languages).

Conclusion
●​ Heaps are specialized complete binary trees for efficient min/max retrieval.
●​ Priority Queues use heaps for priority-based processing.
●​ STL priority_queue provides an easy-to-use heap implementation.

You might also like