Unit 03 - Non Linear Data Structure
Unit 03 - Non Linear Data Structure
In C++, a tree is a hierarchical data structure consisting of nodes connected by edges. Each
node has a value (or data), and the edges represent the relationships between nodes. Trees
are widely used in computer science for various applications such as representing
hierarchical data, organizing data for efficient searching and sorting, and implementing
various algorithms.
TYPES OF TREE:
Binary Tree:
• A binary tree is a hierarchical data structure where each node has at most two
children, referred to as the left child and the right child.
Ternary Tree:
• A ternary tree is a tree data structure where each node has up to three
children.
• Ternary trees are less common than binary trees but can be useful in certain
applications, such as decision trees or tries where each node represents a
character and has three branches for the next character.
N-ary Tree:
• An N-ary tree is a tree data structure where each node can have a variable
number of children, not limited to two as in binary or three as in ternary trees.
• N-ary trees are versatile and can be used in various applications such as file
systems, organization hierarchies, and representing hierarchical data
structures.
• Unlike binary trees, which have a fixed number of children per node, the
number of children in an N-ary tree can vary from node to node.
In C++, binary trees can be represented using various techniques. Two common
methods are:
Sure, let's create a simple node-based representation of a binary tree in C++. In this representation,
we'll define a `TreeNode` structure to represent each node of the binary tree. Each `TreeNode` will
have an integer value and pointers to its left and right children.
```cpp
#include <iostream>
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode* createSampleTree() {
return root;
// Function to traverse the binary tree in pre-order and print the values of nodes
preOrderTraversal(node->left);
preOrderTraversal(node->right);
int main() {
preOrderTraversal(root);
std::cout << std::endl;
delete root->left->left;
delete root->left->right;
delete root->left;
delete root->right;
delete root;
return 0;
```
- We define a `TreeNode` structure to represent each node of the binary tree. Each node contains an
integer value (`val`) and pointers to its left and right children (`left` and `right`, respectively).
- We create a sample binary tree using the `createSampleTree` function, which creates nodes and
connects them to form a binary tree.
- We define a `preOrderTraversal` function to perform a pre-order traversal of the binary tree. Pre-
- In the `main` function, we create a sample binary tree, then traverse it using pre-order traversal and
print the values of nodes.
```
This output indicates that the pre-order traversal of the sample binary tree visits nodes in the order:
root, left subtree, right subtree.
Sure, let's create a simple array-based representation of a binary tree in C++. In this representation,
we'll store the binary tree in an array where each element of the array represents a node of the tree.
```cpp
#include <iostream>
#include <vector>
int main() {
// /\
// 2 3
// /\
// 4 5
levelOrderTraversal(tree);
return 0;
```
Explanation of the code:
- We define a vector `tree` to store the binary tree. Each element of the vector represents a node of
the tree, and the position of each element in the vector determines its relationship with other nodes.
```
/\
2 3
/\
4 5
```
- In the `main` function, we create the binary tree using the vector `tree`, and then traverse it using
level-order traversal and print the values of nodes.
```
```
This output indicates that the level-order traversal of the binary tree visits nodes in the order: root,
left child, right child, left child of the left child, right child of the left child, and so on.
Certainly! Here are the main types of binary trees in C++ along with visual representations:
- Definition: A binary tree where the left child of each node contains a value less than the node's
value, and the right child contains a value greater than the node's value.
- Visual Representation:
```
/\
3 8
/\/\
2 46 9
```
- Definition: A binary tree in which the height difference between the left and right subtrees of any
node is at most one.
```
/\
3 8
/\/\
2 46 9
```
- Definition: A binary tree in which every level, except possibly the last, is completely filled, and all
nodes are as far left as possible.
- Visual Representation:
```
/\
2 3
/\/
4 56
```
- Definition: A binary tree in which every node other than the leaves has two children.
- Visual Representation:
```
/\
2 3
/\
4 5
```
- Definition: A binary tree in which all internal nodes have exactly two children, and all leaves are at
the same level.
- Visual Representation:
```
/\
2 3
/\/\
4 56 7
```
- Definition: A binary tree in which each parent node has only one associated child node.
- Visual Representation:
```
4
```
These are the main types of binary trees in C++, each with its own characteristics and use cases. The
visual representations provide a clear picture of the structure of each type of binary tree.
Sure, let's discuss three common types of binary tree traversals: Inorder, Preorder, and Postorder. I'll
provide brief explanations along with visual representations for each.
- In an inorder traversal, the nodes of the binary tree are visited in the order: left subtree, current
node, right subtree.
- In other words, for each node, the left subtree is visited first, then the current node is visited, and
finally, the right subtree is visited.
- Inorder traversal is commonly used to retrieve the elements of a binary search tree in sorted order.
- Visual representation:
```
/\
2 3
/\
4 5
Inorder Traversal: 4, 2, 5, 1, 3
```
- In a preorder traversal, the nodes of the binary tree are visited in the order: current node, left
subtree, right subtree.
- In other words, for each node, the current node is visited first, then its left subtree is visited, and
finally, its right subtree is visited.
- Preorder traversal is commonly used to create a copy of the tree or to evaluate expressions in
expression trees.
- Visual representation:
```
/\
2 3
/\
4 5
Preorder Traversal: 1, 2, 4, 5, 3
```
- In a postorder traversal, the nodes of the binary tree are visited in the order: left subtree, right
subtree, current node.
- In other words, for each node, its left subtree is visited first, then its right subtree is visited, and
finally, the current node is visited.
- Postorder traversal is commonly used to delete nodes from a tree or to get postfix notation in
expression trees.
- Visual representation:
```
/\
2 3
/\
4 5
Postorder Traversal: 4, 5, 2, 3, 1
```
These visual representations illustrate the order in which nodes are visited during each type of
traversal. Each traversal has its own unique order, making them suitable for different applications
based on the order in which you need to process the nodes of the tree.
A Binary Search Tree is a data structure used in computer science for organizing
and storing data in a sorted manner. Each node in a Binary Search Tree has at
most two children, a left child and a right child, with the left child containing
values less than the parent node and the right child containing values greater than
the parent node. This hierarchical structure allows for
efficient searching, insertion, and deletion operations on the data stored in the
tree.
APPLICATION OF BST:
Applications of BST
Last Updated : 09 Mar, 2023AAAAA
AAAAAAAAAAAAAAAAAAAAA
••
Binary Search Tree (BST) is a data structure that is commonly used to
implement efficient searching, insertion, and deletion operations. The key
feature of a BST is that it is a binary tree, where each node has at most two
child nodes, and the value of each node is greater than all the values in its left
subtree and less than all the values in its right subtree. This means that the left
subtree of a node contains values that are smaller than the node’s value, and
the right subtree contains values that are larger. Due to this property, BSTs
allow for efficient searching by repeatedly dividing the search space in half,
which makes it an important data structure in computer science and many
other fields.
• The left subtree of a node contains only nodes with keys lesser than
the node’s key.
• The right subtree of a node contains only nodes with keys greater
than the node’s key.
• The left and right subtree each must also be a binary search tree.
There must be no duplicate nodes.
GENERAL TREE:
••
BINARY TREE VS GENERAL TREE :
In C++, both general trees and binary trees are fundamental data structures used for organizing and
representing hierarchical data. However, they have different characteristics and are suitable for
different types of applications. Let's compare general trees and binary trees in C++:
1. **Definition**:
- A general tree is a hierarchical data structure where each node can have zero or more children.
2. **Implementation**:
- In C++, a general tree can be implemented using structures or classes to represent nodes, where
each node contains a value and a list (e.g., vector) of child pointers.
3. **Traversal**:
- Traversing a general tree may involve techniques like depth-first search (DFS) or breadth-first
search (BFS) to visit nodes in a specific order.
4. **Applications**:
- General trees are used to represent hierarchical data structures such as organization charts, file
systems, XML/HTML documents, and syntax trees in compilers.
1. **Definition**: - A binary tree is a hierarchical data structure where each node has at most two
children: a left child and a right child.
- Binary trees are characterized by their recursive structure, which is conducive to various
algorithms and operations.
2. **Implementation**:
- In C++, a binary tree is often implemented using structures or classes to represent nodes, with
each node containing a value and pointers to its left and right children.
3. **Traversal**:
- Common traversal techniques for binary trees include inorder, preorder, and postorder traversals,
each visiting nodes in a specific order relative to their children.
4. **Applications**:
- Binary trees have various applications, including binary search trees (BSTs) for efficient searching,
heaps for priority queue implementations, expression trees for evaluating arithmetic expressions,
and Huffman coding for data compression.
### Comparison:
- **Structure**: General trees can have any number of children per node, while binary trees have at
most two children per node.
- **Traversal**: Traversing a general tree may require specialized algorithms, while binary trees have
well-defined traversal orders like inorder, preorder, and postorder.
- **Space Complexity**: Binary trees can have more compact representations, especially for
complete binary trees, compared to general trees, which may have irregular structures.
- **Performance**: Certain operations, such as searching, insertion, and deletion, may be more
efficient in binary trees like BSTs due to their balanced nature compared to general trees.
In summary, general trees are more flexible and can represent a wider range of hierarchical
structures, while binary trees offer specific advantages for certain applications, particularly those
requiring efficient search and manipulation operations.
Certainly! Converting a general tree to a binary tree involves transforming a tree where nodes can
have any number of children into a binary tree where each node has at most two children. One
common approach is to represent the general tree as a binary tree by making use of the 'left-child,
right-sibling' representation.
In this representation:
- Each node in the binary tree represents a node in the original general tree.
- The left child of a node in the binary tree corresponds to its first child in the general tree.
- The right sibling of a node in the binary tree corresponds to the next sibling of the node in the
general tree.
Here's how you can implement this conversion in C++:
```cpp
#include <iostream>
#include <vector>
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
};
struct Node {
int val;
std::vector<Node*> children;
Node(int x) : val(x) {}
};
if (root == nullptr) {
return nullptr;
if (!root->children.empty()) {
binaryRoot->left = convertToBinaryTree(root->children[0]);
currentBinaryNode->right = convertToBinaryTree(root->children[i]);
currentBinaryNode = currentBinaryNode->right;
return binaryRoot;
if (root == nullptr) {
return;
inorderTraversal(root->left);
inorderTraversal(root->right);
int main() {
generalRoot->children.push_back(new Node(2));
generalRoot->children.push_back(new Node(3));
generalRoot->children[0]->children.push_back(new Node(4));
generalRoot->children[0]->children.push_back(new Node(5));
generalRoot->children[1]->children.push_back(new Node(6));
// Convert the general tree to a binary tree
inorderTraversal(binaryRoot);
return 0;
```
Output:
```
```
- We define structures `TreeNode` and `Node` to represent nodes of the binary tree and the general
tree, respectively.
- We define a function `convertToBinaryTree` to recursively convert the general tree to a binary tree
using the 'left-child, right-sibling' representation.
- We traverse the general tree, creating the corresponding binary tree nodes and setting the left child
and right sibling pointers accordingly.
- We define an `inorderTraversal` function to perform an inorder traversal of the resulting binary tree
and print its elements.
- In the `main` function, we create a sample general tree, convert it to a binary tree, and then
traverse and print the binary tree in inorder traversal order.
APPLICATION OF TREES:
KEY POINTS
2-3 TREE:
A 2-3 Tree is a tree data structure where every node with children has
either two children and one data element or three children and two data
elements. A node with 2 children is called a 2-NODE and a node with 3
children is called a 3-NODE. A 4-NODE, with three data elements, may be
temporarily created during manipulation of the tree but is never persistently
stored in the tree.
Nodes on the outside of the tree i.e. the leaves have no children and have
either one or two data elements. All its leaves must be on the same level so
that a 2-3 tree is always height balanced. A 2-3 tree is a special case of
a B-Tree of order 3. Below is an example of a 2-3 tree.
In the context of trees, including binary trees like AVL trees, "balance" generally refers to how evenly
distributed or weighted the branches or subtrees are relative to each other. There are two common
types of balance in trees:
1. **Height Balance**:
- Height balance, also known as "height-balanced" or "height-balancing," refers to ensuring that the
heights of subtrees from any given node in the tree are roughly equal.
- In a height-balanced tree, the difference in height between the left and right subtrees of any node
is limited to some predefined threshold (often 1 or 2).
- Maintaining height balance in trees helps ensure efficient operations like searching, insertion, and
deletion, as it keeps the tree relatively shallow and prevents performance degradation to O(n) time
complexity.
- Examples of height-balanced trees include AVL trees, which are self-balancing binary search trees
where the height difference between the left and right subtrees of any node (the balance factor) is
kept within a certain range, typically -1, 0, or 1.
2. **Weight Balance**:
- In a weight-balanced tree, the number of nodes or elements in the left and right subtrees of any
node is approximately equal or within some predefined range.
- Weight-balanced trees aim to evenly distribute the workload among subtrees, which can help
maintain performance and prevent skewed or degenerate tree structures.
- Examples of weight-balanced trees include B-trees and red-black trees, which ensure that the
number of elements in subtrees satisfies certain criteria to keep the tree balanced.
Both height balance and weight balance are crucial concepts in designing and implementing efficient
tree-based data structures. While height balance focuses on the structural properties of the tree,
weight balance focuses on the distribution of data within the tree. These concepts often go hand in
hand, with various tree structures and algorithms designed to maintain both types of balance to
achieve optimal performance and efficiency in operations.
GRAPH IN C++;
Components of a Graph:
• Vertices: Vertices are the fundamental units of the graph.
Sometimes, vertices are also known as vertex or nodes. Every
node/vertex can be labeled or unlabeled.
• Edges: Edges are drawn or used to connect two nodes of the
graph. It can be ordered pair of nodes in a directed graph. Edges
can connect any two nodes in any possible way. There are no
rules. Sometimes, edges are also known as arcs. Every edge can
be labelled/unlabelled.
V = {a, b, c, d, e}
Operations of Graphs
The primary operations of a graph include creating a graph with
vertices and edges, and displaying the said graph. However, one
of the most common and popular operation performed using
graphs are Traversal, i.e. visiting every vertex of the graph in a
specific order.
There are two types of traversals in Graphs −
The DFS traversal uses the stack data structure to keep track of
the unvisited nodes.
The DFS traversal uses the queue data structure to keep track of
the unvisited nodes.
Representation of Graphs
While representing graphs, we must carefully depict the elements
(vertices and edges) present in the graph and the relationship
between them. Pictorially, a graph is represented with a finite set
of nodes and connecting links between them. However, we can
also represent the graph in other most commonly used ways, like
−
• Adjacency Matrix
• Adjacency List
Adjacency Matrix
Adjacency List
Types of graph
There are two basic types of graph −
• Directed Graph
• Undirected Graph
Undirected Graph
Spanning Tree
A spanning tree is a subset of an undirected graph that contains
all the vertices of the graph connected with the minimum number
of edges in the graph. Precisely, the edges of the spanning tree is
a subset of the edges in the original graph.
Shortest Path
The shortest path in a graph is defined as the minimum cost
route from one vertex to another. This is most commonly seen in
weighted directed graphs but are also applicable to undirected
graphs.
Example
Following are the implementations of this operation in various
programming languages −
C C++ JavaPython
#include <bits/stdc++.h>
using namespace std;
#define V 5
// declaring vertices
int end;
struct vertex *next;
};
struct Edge {
// declaring edges
int end, start;
};
struct graph *create_graph (struct Edge edges[], int x){
int i;
struct graph *graph = (struct graph *) malloc (sizeof (struct graph));
for (i = 0; i < V; i++) {
graph->point[i] = NULL;
}
for (i = 0; i < x; i++) {
int start = edges[i].start;
int end = edges[i].end;
struct vertex *v = (struct vertex *) malloc (sizeof (struct vertex));
v->end = end;
v->next = graph->point[start];
graph->point[start] = v;
}
return graph;
}
int main (){
struct Edge edges[] = { {0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 4}, {2, 4}, {2, 3}, {3, 1} };
int n = sizeof (edges) / sizeof (edges[0]);
struct graph *graph = create_graph (edges, n);
int i;
cout<<"The graph created is: ";
for (i = 0; i < V; i++) {
struct vertex *ptr = graph->point[i];
while (ptr != NULL) {
cout << "(" << i << " -> " << ptr->end << ")\t";
ptr = ptr->next;
}
cout << endl;
}
return 0;
}
Output
The graph created is:
(1 -> 3) (1 -> 0)
(2 -> 1) (2 -> 0)
(3 -> 2) (3 -> 0)
(4 -> 2) (4 -> 1)