Ds Lab File
Ds Lab File
LAB MANUAL
Submitted by:
Akshath Dubey
2K20/EN/11
Submitted to:
Ms. Shweta Meena
Assistant Professor
Department of Software Engineering
Delhi Technological University
Shahbad Daulatpur, Main Bawana Road, Delhi-110042
Novemeber, 2022
INDEX
EXPERIMENT-1
• Code:
#include <iostream>
using namespace std;
// function to print an array
void printArray(int *arr, int size) {
for (int k=0; k<size; k++){
cout<<arr[k]<<" ";
}
}
// selection sort function
void selectionSort(int *arr, int size) {
for (int step=0; step<size; step++) {
int min_idx=step;
for (int i=step+1; i<size; i++) {
if (arr[i]<arr[min_idx]) {
min_idx=i;
}
}
int temp=arr[step];
arr[step]=arr[min_idx];
arr[min_idx]=temp;
}
}
// driver code
int main()
{
int size;
cout<<"Enter size of array : "<<endl;
cin>>size;
int *arr = new int[size];
cout<<"Enter elements"<<endl;
for (int j=0; j<size; j++) {
cin>>arr[j];
}
selectionSort(arr, size);
cout<<"Sorted array is : "<<endl;
printArray(arr, size);
return 0;
}
• Output:
• Learning: Amongst various sorting techniques, we learnt the
implementation of selection sort in this experiment. Since we used 2 nested
loops in the code, we can say that the time complexity of selection sort is
O(N2) (same in all cases since checking all elements is compulsory) and the
only extra memory required for the implementation is that taken up by
temporary variables, hence space complexity becomes O(1). We can
conclude that selection sort can be used when a small list is to be sorted
and where the cost of swapping doesn’t matter.
EXPERIMENT-2
• Algorithms:
• Set i and j = 0.
• Traverse the array using j till j = array size-i-2.
• During the traversal, if array[j]>array[j+1], swap these adjacent
elements.
• Increment i by 1.
• Repeat steps 2 to 4 till i = array size -1.
• Code:
#include <iostream>
using namespace std;
// function to print an array
void printArray(int *arr, int size) {
for (int k=0; k<size; k++){
cout<<arr[k]<<" ";
}
}
// bubble sort function
void bubbleSort(int *arr, int size) {
for (int i=0; i<size; i++) {
for (int j=0; j<size-i-1; j++) {
if (arr[j]>arr[j+1]) {
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
// driver code
int main()
{
int size;
cout<<"Enter size of array : "<<endl;
cin>>size;
int *arr = new int[size];
cout<<"Enter elements"<<endl;
for (int j=0; j<size; j++) {
cin>>arr[j];
}
bubbleSort(arr, size);
cout<<"Sorted array is : "<<endl;
printArray(arr, size);
return 0;
}
• Output:
• Learning: Just like selection sort, the time complexity of the above
implemented bubble sort is also O(N2) in all cases. But we can optimize
bubble sort by stopping the algorithm incase the inner loop doesn’t cause
any swap i.e., when bubble sort is applied to an already sorted array, the
time complexity in this case would be reduced to O(N). The extra memory
required is only for storing temporary variables, hence space complexity is
O(1).
EXPERIMENT-3
• Introduction: In insertion sort, the array is virtually split into a sorted and
an unsorted part. Values from the unsorted part are picked and placed at
their correct position in the sorted part. With each iteration, the sorted
portion increases in size whereas the unsorted portion reduces. We start by
assuming that first element is already sorted, move to the first element of
the unsoted portion and insert it on its right place in sorted subarray and so
on with the remaining elements.
• Algorithms:
• Mark first element as sorted.
• Set step = 1.
• Extract the element present at step position and store it in key.
• Traverse the sorted subarray using j starting from its last index till 0. If
the sorted element is greater than key, shift its position to right by 1.
• Repeat step 4 untill we find an element smaller than key value
present in the sorted portion.
• Place key at the position j+1.
• Increment step by 1.
• Repeat steps 3 to 7 till step = array size-1.
• Code:
#include <iostream>
using namespace std;
// function to print an array
void printArray(int *arr, int size) {
for (int k=0; k<size; k++){
cout<<arr[k]<<" ";
}
}
// insertion sort function
void insertionSort(int *arr, int size) {
for (int step=1; step<size; step++) {
int key=arr[step]; //first element of the unsorted subarray
int j=step-1; //last index within the sorted subarray
while (j>=0 && key<arr[j]) {
arr[j+1]=arr[j];
j--;
}
arr[j+1]=key; //placing key at its correct position in the sorted portion
}
}
// driver code
int main()
{
int size;
cout<<"Enter size of array : "<<endl;
cin>>size;
int *arr = new int[size];
cout<<"Enter elements"<<endl;
for (int j=0; j<size; j++) {
cin>>arr[j];
}
insertionSort(arr, size);
cout<<"Sorted array is : "<<endl;
printArray(arr, size);
return 0;
}
• Output:
• Learning: From the insertion sort implementation, we get the intution that
maximum time would be needed to sort an array which is in reverse sorted
form and it takes minimum time when elements are already sorted. Thus,
best case time complexity is O(N) and average or worst case being O(N2).
Insertion sort can be highly useful when input array is almost sorted, only
few elements are misplaced in complete big array. Just like selection sort
and bubble sort, it is an in-place sorting algorithm.
EXPERIMENT-4
• Push operation:
• If nextIndex=capacity:
i. Initialise a new array (newData) on the heap memory of twice
the current capacity.
ii. Copy the elements of previous data array to the newData array
using loop.
iii. Update the capacity with twice its initial value.
iv. Delete the previous data array from heap.
v. Assign newData pointer value to data member.
• Place the element given by user at nextIndex location of data array.
• Update nextIndex to nextIndex+1.
• Pop operation:
• If nextIndex=0:
i. Print “Stack is empty”.
ii. Exit.
• Update nextIndex to nextIndex-1.
• Code: (Template is used in the stack class in order to be able to add any
data type in the stack)
Stack Class:
template <typename T> // Templates initialised
class StackUsingArray {
// //Privately declared class members
T *data; // Template type of data used
int nextIndex; // To keep the track of current top index
int capacity; // To keep the track of total capacity of data array
public :
//constructor to initialise the values
StackUsingArray() {
data = new T[4]; // Dynamic array created serving as stack
nextIndex = 0;
capacity = 4;
}
//To return number of elements present in stack
int size() {
return nextIndex;
}
//To check if stack is empty
bool empty() {
return nextIndex == 0;
}
// insert element
void push(T element) {
if(nextIndex == capacity) {
T *newData = new T[2 * capacity]; //Capacity
doubled
for(int i = 0; i < capacity; i++) {
newData[i] = data[i]; //Elements copied
}
capacity *= 2;
delete [] data;
data = newData;
}
data[nextIndex] = element;
nextIndex++; //Size incremented
}
// delete element
void pop() {
//Before deletion we check if stack is not empty to prevent underflow
if (empty()) {
cout << "Stack is empty " << endl;
}
nextIndex--;
}
//To return the top element of the stack
T top() {
if (empty()) { // checked for empty stack to prevent
overflow
cout << "Stack is empty " << endl;
return 0;
}
return data[nextIndex - 1];
}
};
Driver code:
#include <iostream>
using namespace std;
#include "StackUsingArray.h"
int main()
{
StackUsingArray<int> s;
s.push(10);
s.push(20);
s.push(30);
s.push(40);
s.push(50);
cout<<s.top()<<endl;
s.pop();
cout<<s.top()<<endl;
s.pop();
cout<<s.top()<<endl;
cout<<s.size()<<endl;
cout<<s.empty()<<endl;
return 0;
}
• Output:
• Learning: Through this experiment we learnt the implementation of
different stack operations using array as a container. We used a dynamic
array for the implementation so as to avoid experiencing “Stack Overflow”
and add as many elements as we wish to. We also understood the utility of
templates that help us make our class more generic in terms of handling
different data types.
EXPERIMENT-5
• Algorithms:
• Create
• Set choice=1, head=NULL and tail=NULL.
• Build a node from the data element provided by user and
store its pointer in newnode variable.
• Check if linked list is empty, if head=NULL:
• Update head and tail to newnode.
• Else
• Link newnode to the tail node.
• Update tail to newnode.
• Ask the user if they wish to continue adding elements.
• If choice entered by user=1, repeat steps 2 to 5.
• Else exit the loop.
• Return head of the linked list.
• Insert
• Build a node from the data element provided by user and
store its pointer in newnode variable.
• Set temp=head.
• If position entered by the user is 1 (means element is to be
added in the starting of the linked list):
• Attach head of the linked list after newnode.
• Update head with newnode.
• Return the updated head and exit.
• Set i=1.
• Repeat steps 6 and 7 while temp!=NULL and i<position-1.
• Move temp to temp->next.
• Increment i by 1.
• If temp!=NULL:
• Attach newnode to the Nodes present at temp and
temp->next.
• Return head and exit.
• Delete
• Set temp=head.
• If position entered by the user is 1 (means element present at
head is to be deleted):
• Move head to head->next.
• Delete node present at temp.
• Return the updated head and exit.
• Set i=1.
• Repeat steps 5 and 6 while temp!=NULL and i<position-1.
• Move temp to temp->next.
• Increment i by 1.
• If temp!=NULL and temp->next!=NULL:
• Attach the node present at temp->next->next to the
Node present at temp.
• Delete the Node present at temp->next.
• Return head and exit.
• Code:
#include <iostream>
using namespace std;
// node class
class Node {
public:
int data;
Node* next;
Node (int data) {
this->data=data;
next=NULL;
}
};
// function to print elements of a linked list as well as its size
void printLL(Node* head) {
int count=0;
Node*temp=head;
while (temp!=NULL) {
cout<<temp->data<<" ";
temp=temp->next;
count++;
}
cout<<"\nSize of linked list is "<<count<<"\n";
}
// create linked list function
Node* createLL() {
int element;
int choice=1;
Node* head=NULL;
Node* tail=NULL;
while(choice==1) {
cout<<"Enter element\n";
cin>>element;
Node *newnode=new Node(element);
if (head==NULL) {
head=newnode;
tail=newnode;
}
else {
tail->next=newnode;
tail=tail->next;
}
cout<<"Do you wish to continue?\n1: yes, 0: no\n";
cin>>choice;
}
return head;
}
//insert element function
Node* insert(Node* head, int pos, int data) {
Node* newnode=new Node(data);
Node* temp=head;
if (pos==1){
newnode->next=head;
head=newnode;
return head;
}
int i=1;
while (temp!=NULL && i<pos-1) {
temp=temp->next;
i++;
}
if (temp!=NULL) {
newnode->next=temp->next;
temp->next=newnode;
}
return head;
}
// delete element function
Node* deduct(Node* head, int pos) {
Node*temp=head;
if (pos==1) {
head=head->next;
delete temp;
return head;
}
int i=1;
while (temp!=NULL && i<pos-1) {
temp=temp->next;
i++;
}
if (temp!=NULL && temp->next!=NULL) {
Node* a=temp->next;
Node* b=a->next;
temp->next=b;
delete a;
}
return head;
}
// driver code
Node*head=NULL;
int main()
{ int choice;
int pos, data;
while(true) {
cout<<"Choose from the following:\n1: Create a linked list\n2: Print the
elements and size of the linked list\n3: Insert an element\n4: Delete an
element\n";
cin>>choice;
if (choice==1) {
head=createLL();
}
else if (choice==2) {
printLL(head);
}
else if (choice==3) {
cout<<"At which position do you want the element to be entered?\n";
cin>>pos;
cout<<"Which element you want to be entered?\n";
cin>>data;
head=insert(head, pos, data);
}
else if (choice==4) {
cout<<"Which position element is to be deleted?\n";
cin>>pos;
head=deduct(head, pos);
}
else {break;}
}
return 0;
}
• Outputs:
• Learning: Through this experimment, we learnt that linked list overcomes
the following shortcomings of arrays:
• To create a linked list, we don’t need to fix its size beforehand, the
user can enter as many elements as he/she wants at the runtime. On
the other hand, arrays have fixed size that is required to be initialised
at the time of declaration. Any addition of extra elements would
throw an error (index out of range).
• Even deletion of elements from the array is an expensive task as it
would require the complete shift of further elements by some
positions to the left as the data is stored in the form of contiguous
memory blocks.
EXPERIMENT-6
• P HYPERLINK "https://www.geeksforgeeks.org/stack-push-and-pop-
in-c-stl/"ush
• Create node containing the provided element and store its
address in newnode variable.
• Attach head after this new node created.
• Update head to newnode.
• Increment len by 1 and exit.
• Pop
• Check for stack underflow condition; if len=0:
• Print “Stack is empty” and exit.
• Set temp=head.
• Move head to head->next.
• Delete node present at temp.
• Decrement len by 1 and exit.
• Top
• Check for stack underflow condition; if len=0:
• Print “Stack is empty”.
• Return 0 and exit.
• Return data present at head node and exit.
• Empty
• If len=0, return true.
• Else return false.
}
else if (choice == 4) {
cout<<"Size of the stack is "<<s.size()<<endl;
}
else if (choice==5) {
cout<<s.empty()<<endl;
}
else {break;}
}
return 0;
}
• Output:
• Learning: In this experiment, we implemented all the operations of stack
using a linked list which execute in O(1) time. For the user, everything
remains same whether the container used is array or linked list i.e. no
difference is created in the utility of functions.
Experiment 7
I. Program Objective: Write a program to perform the following operations
on doubly linked list.
I. Create ()
ii. Insert element at beginning
iii. Insert element at end
iv. Insert element at position entered by user
v. Delete element at the beginning
vi. Delete element at the end
vii. Delete element at the position entered by user
viii. Display elements of linked list
ix. Display elements in reverse order
x. Reverse the elements
xi. Display size of the linked list
II. Introduction:Doubly linked list is a type of data structure that is made up of
nodes that are created using self-referential structures. Each of these nodes
contain three parts, namely the data and the reference to the next list node
and the reference to the previous list node.
Only the reference to the first list node is required to access the whole
linked list. This is known as the head. The last node in the list points to
nothing so it stores NULL in that part. Also, the doubly linked list can be
traversed in both directions as each node points to its previous and next
node.
III. ALGORITHM-Insertion
1) Add a node at the front:
The new node is always added before the head of the given Linked List. And
newly added node becomes the new head of DLL. For example, if the given
Linked List is 1->0->1->5 and we add an item 5 at the front, then the Linked
List becomes 5->1->0->1->5. Let us call the function that adds at the front of
the list push(). The push() must receive a pointer to the head pointer
because the push must change the head pointer to point to the new node
Time Complexity: O(1)
Auxiliary Space: O(1)
2) Add a node after a given node:
We are given a pointer to a node as prev_node, and the new node is
inserted after the given node.
Time Complexity: O(1)
Auxiliary Space: O(1)
class Node {
public:
int data;
Node* next;
Node* prev;
};
new_node->data = new_data;
new_node->next = (*head_ref);
new_node->prev = NULL;
if ((*head_ref) != NULL)
(*head_ref)->prev = new_node;
(*head_ref) = new_node;
}
void insertAfter(Node* prev_node, int new_data)
{
if (prev_node == NULL) {
cout << "the given previous node cannot be NULL";
return;
}
Node* new_node = new Node();
new_node->data = new_data;
new_node->next = prev_node->next;
prev_node->next = new_node;
new_node->prev = prev_node;
if (new_node->next != NULL)
new_node->next->prev = new_node;
}
void append(Node** head_ref, int new_data)
{
Node* new_node = new Node();
Node* last = *head_ref;
new_node->next = NULL;
if (*head_ref == NULL) {
new_node->prev = NULL;
*head_ref = new_node;
return;
}
last->next = new_node;
new_node->prev = last;
return;
}
void deleteNode(Node** head_ref, Node* del)
{
if (*head_ref == del)
*head_ref = del->next;
if (del->next != NULL)
del->next->prev = del->prev;
if (del->prev != NULL)
del->prev->next = del->next;
free(del);
return;
}
void deleteNodeAtGivenPos(struct Node** head_ref, int n)
{
if (*head_ref == NULL || n <= 0)
return;
if (current == NULL)
return;
deleteNode(head_ref, current);
}
void reverseList(Node** head)
{
Node* left = *head, * right = *head;
swap(left->data, right->data);
left = left->next;
right = right->prev;
}
}
int findSize(struct Node *node)
{
int res = 0;
while (node != NULL)
{
res++;
node = node->next;
}
return res;
}
void printList(Node* node)
{
Node* last;
cout << "\nTraversal in forward direction \n";
while (node != NULL) {
cout << " " << node->data << " ";
last = node;
node = node->next;
}
cout << "\nTraversal in reverse direction \n";
while (last != NULL) {
cout << " " << last->data << " ";
last = last->prev;
}
}
int main()
{
Node* head = NULL;
append(&head, 6);
push(&head, 7);
push(&head, 1);
append(&head, 9);
push(&head, 5);
push(&head, 2);
append(&head, 4);
insertAfter(head->next, 8);
deleteNode(&head, head);
deleteNode(&head, head->next);
deleteNode(&head, head->next);
printList(head);
int n=2;
deleteNodeAtGivenPos(&head, n);
return 0;
}
V. OUTPUT
EXPERIMENT-8
• Program Objective: Write a program to perform the following operations
on an array.
• Create an array
• Insert an element in an array
• Delete an element from the array
• Print elements of array in reverse order
• Reverse elements of array
• Display elements of array
• Display size of the array
• Introduction: An array is a collection of items of same data type stored at
contiguous memory locations. This makes it easier to calculate the position
of each element by simply adding an offset to a base value, i.e., the
memory location of the first element of the array (generally denoted by the
name of the array). Just like the case of linked lists, various operations can
be performed on arrays as well which are shown below.
• Algorithms:
• Create an array
• Ask for the array capacity and number of elements to be
entered from the user.
• If number of elements entered exceeds the capacity of array,
exit.
• Create an array of the provided capacity value and store its
pointer in arr.
• Run a loop for the total number of elements entered by the
user and ask for the input at each position.
• Return arr pointer and exit.
• Insert an element in an array
• Ask for position and the element to be entered from the user.
• If number of elements present in the array = capacity of the
array, print “Array is full” and exit.
• If position entered by the user is greater than the number of
elements present in the array, ask user to give a valid position
and exit.
• Shift elements from the end to their right position by 1 uptill
the element present at the position entered by user.
• Insert provided element at the position asked.
• Increment the number of elements by 1 and exit.
• Delete an element from the array
• If number of elements present in the array=0, print “Array is
empty” and exit.
• If position provided by the user at which the element is to be
deleted is greater than or equal to the total number of
elements present in the array currently, ask the user to give a
valid position and exit.
• Starting from the element present at position+1, shift all
elements to the left by 1 till the last element.
• Decrement total number of elements present by 1 and exit.
• Print elements of array in reverse order
• If number of elements present in the array=0, print “Array is
empty” and exit.
• Starting from i=number of elements present in array-1 till i=0,
print element present at the ith position of array.
• Exit.
• Reverse elements of array
• If number of elements present in the array=0, print “Array is
empty” and exit.
• Initialise i=0 and j= number of elements present in array-1.
• Repeat step 4, 5 while i< number of elements present in
array/2.
• Swap elements present at i and jth position.
• Increment i by 1 and decrement j by 1.
• Exit.
• Display elements of array
• If number of elements present in the array=0, print “Array is
empty” and exit.
• Initialise i=0 and traverse the array till i= number of elements
present in the array-1.
• Print the elements present at the respective ith positions.
• Exit.
• Display size of the array
• Print the value of global variable num.
• Code:
#include <iostream>
using namespace std;
// global variables
int *arr = NULL;
int size=0; //capacity of the array
int num=0; //number of elements within the array
// create function
int* createArr(int &size, int &num) {
if (num>size) {
cout<<"No. of elements exceed the capacity of array"<<endl;
return NULL;
}
arr=new int[size];
cout<<"Enter elements "<<endl;
for (int i=0; i<num; i++) {
cin>>arr[i];
}
cout<<"Array created!"<<endl;
return arr;
}
// insert element function
void insert(int pos, int element, int &num, int &size, int *arr) {
if (num==0) {
cout<<"Create an array first"<<endl;
return;
}
else if (num==size) {
cout<<"Array is full"<<endl;
return;
}
else if (pos>num) {
cout<<"Enter valid position"<<endl;
return;
}
for (int i=num; i>=pos; i--) {
arr[i]=arr[i-1];
}
arr[pos]=element;
num++;
cout<<"Element entered!"<<endl;
}
// delete element function
void deduct(int pos, int &num, int* arr){
if (num==0) {
cout<<"Array is empty"<<endl;
return;
}
else if (pos>=num) {
cout<<"Enter valid position"<<endl;
return;
}
for (int i=pos;i<num;i++) {
arr[i]=arr[i+1];
}
num--;
cout<<"Element deleted!"<<endl;
}
// print in reverse function
void printRev(int &num, int* arr){
if (num==0) {
cout<<"Array is empty"<<endl;
return;
}
for (int i=num-1; i>=0; i--) {
cout<<arr[i]<<" ";
}
cout<<endl;
}
// reverses original array
void revArr(int &num, int *arr){
if (num==0) {
cout<<"Array is empty"<<endl;
return;
}
int i, j;
for (i=0, j=num-1; i<num/2; i++, j--) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
cout<<"Array reversed!"<<endl;
}
// prints array elements, total number of elements and the current capacity
of the array
void printArr(int &num, int* arr, int &size){
if (num==0) {
cout<<"Array is empty"<<endl;
return;
}
for (int i=0; i<num; i++) {
cout<<arr[i]<<" ";
}
cout<<endl<<"Number of elements within the array: "<<num<<" and
current capacity of the array: "<<size<<endl;
}
// driver code
int main()
{
int choice, pos, element;
while (true) {
cout<<"Choose from the following: \n1: Create an array 2: Insert an
element 3: Delete an element 4: Print elements in reverse order 5:
Reverse elements of array 6: Print elements and size of array"<<endl;
cin>>choice;
if (choice==1) {
cout<<"Enter capacity of the array: ";
cin>>size;
cout<<"No. of elements to be entered: ";
cin>>num;
arr=createArr(size, num);
}
else if (choice==2) {
cout<<"At which position you want to enter the element? ";
cin>>pos;
cout<<"Enter element ";
cin>>element;
insert(pos, element, num, size, arr);
}
else if(choice==3) {
cout<<"Which position element is to be deleted? ";
cin>>pos;
deduct(pos, num, arr);
}
else if(choice==4) {
printRev(num, arr);
}
else if(choice==5) {
revArr(num, arr);
}
else if(choice==6) {
printArr(num, arr, size);
}
else {break;}
}
return 0;
}
• Output:
• Learning: Through this experiment we performed various operations on an
array having fixed capacity. We saw that, for insert and delete operations,
elements need to be shifted rendering the worst case time complexity of
these operations to be O(N). However, accessing an element present at a
specific position ‘i’ in the array is a super-fast operation unlike in the case of
linked lists where we have to traverse the list to get the desired data
element.
EXPERIMENT-10
Objective : WAP to transform a infix expression to postfix
expression
Theory:
Infix: The typical mathematical form of expression that we encounter
generally is known as
infix notation. In infix form, an operator is written in between two operands.
For example:
An expression in the form of A * ( B + C ) / D is in infix form. This expression
can be simply
decoded as: “Add B and C, then multiply the result by A, and then divide it by
D for the final
answer.”
Postfix: In postfix expression, an operator is written after its operands. This
notation is also
known as “Reverse Polish notation”.
For example, The above expression can be written in the postfix form as A B
C + * D /. This type
of expression cannot be simply decoded as infix expressions.
Algorithm:
• Step 1 : Scan the Infix Expression from left to right.
• Step 2 : If the scanned character is an operand, append it with final Infix to
Postfix string.
• Step 3 : Else,
o Step 3.1 : If the precedence order of the scanned(incoming) operator is
greater than the precedence order of the operator in the stack (or the
stack is empty or the stack contains a ‘(‘ or ‘[‘ or ‘{‘), push it on stack.
• Step 3.2 : Else, Pop all the operators from the stack which are greater than
or
equal to in precedence than that of the scanned operator. After doing that
Push
the scanned operator to the stack. (If you encounter parenthesis while
popping
then stop there and push the scanned operator in the stack.)
• Step 4 : If the scanned character is an ‘(‘ or ‘[‘ or ‘{‘, push it to the stack.
• Step 5 : If the scanned character is an ‘)’or ‘]’ or ‘}’, pop the stack and and
output
it until a ‘(‘ or ‘[‘ or ‘{‘ respectively is encountered, and discard both the
parenthesis.
• Step 6 : Repeat steps 2-6 until infix expression is scanned.
• Step 7 : Print the output
• Step 8 : Pop and output from the stack until it is not empty.
Code :
#include <iostream>
#include <stack>
using namespace std;
int precedence(char c)
{
if (c == '^')
{
return 3;
}
else if (c == '*' || c == '/')
{
return 2;
}
else if (c == '+' || c == '-')
{
return 1;
}
else
{
return -1;
}
}
int main()
{
string s = "(x+y*z)+b/c" ;
stack<char> input ;
string pfix ;
for (int i = 0; i < s.length(); i++)
{
if((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z'))
{
pfix += s[i];
}
else if (s[i] == '(')
{
input.push(s[i]);
}
else if (s[i] == ')')
{
while (!input.empty() && input.top() != '(')
{
pfix += input.top();
input.pop();
}
if (!input.empty())
{
input.pop();
}
}
else
{
while (!input.empty() && precedence(input.top()) > precedence(s[i]))
{
pfix += input.top();
}
input.push(s[i]);
}
}
while (!input.empty())
{
pfix += input.top();
input.pop();
}
cout << "Final postfix expression : "<<pfix <<endl ;
}
Input/Output :
EXPERIMENT-11
Objective : WAP to transform a infix expression to prefix expression
Theory:
Infix: The typical mathematical form of expression that we encounter
generally is known as
infix notation. In infix form, an operator is written in between two operands.
For example:
An expression in the form of A * ( B + C ) / D is in infix form. This expression
can be simply
decoded as: “Add B and C, then multiply the result by A, and then divide it by
D for the final
answer.”
Prefix: In prefix expression, an operator is written before its operands. This
notation is also
known as “Polish notation”.
For example, The above expression can be written in the prefix form as / * A
+ B C D. This type
of expression cannot be simply decoded as infix expressions.
Algorithm:
Step 01: reverse the infix expression
Step 02: for each character ch of reversed infix expression, do
if ch = opening parenthesis, then
convert ch to closing parenthesis
else if ch = closing parenthesis, then
convert ch to opening parenthesis
Step 03: postfix := find transformed infix expression to postfix expression
prefix := reverse recently calculated postfix form
Step 04: print prefix
Step 05: End
Code :
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;
int precedence(char c)
{
if (c == '^')
{
return 3;
}
else if (c == '*' || c == '/')
{
return 2;
}
else if (c == '+' || c == '-')
{
return 1;
}
else
{
return -1;
}
}
int main()
{ string s = "(a+b/c)+d/e";
reverse(s.begin(), s.end());
stack<char> input;
string prefix;
for (int i = 0; i < s.length(); i++)
{
if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z'))
{
prefix += s[i];
}
else if (s[i] == ')')
{
input.push(s[i]);
}
else if (s[i] == '(')
{
while (!input.empty() && input.top() != ')')
{
prefix += input.top();
input.pop();
}
if (!input.empty())
{
input.pop();
}
}
else
{
while (!input.empty() && precedence(input.top()) > precedence(s[i]))
{
prefix += input.top();
input.pop();
}
input.push(s[i]);
}
}
while (!input.empty())
{prefix += input.top();
input.pop();
}
reverse(prefix.begin(), prefix.end());
cout <<"Final Prefix expression : "<< prefix << endl;
return 0;
}
Input/Output :
EXPERIMENT-12
• Introduction: Binary Search Tree is a special type of binary tree that has a
specific order of elements in it. It follows three basic properties:-
• All elements in the left subtree of a node should have a value lesser
than the node’s value.
• All elements in the right subtree of a node should have a
value greater than the node’s value.
• Both the left and right subtrees should be binary search trees too.
Since BST is a type of Binary Tree, the same operations are performed in
BST too. Although, insertion and deletion in BST are much stricter with
predetermined conventions so that even after performing an operation, the
properties of BST are not violated.
• Algorithms:
• Search a given node
• Check if the root is NULL, return False if it is NULL.
• Else, compare the data present at root node with the search
query.
• element == root->data: return True
• element > root->data: recurse for right subtree
• element < root->data: recurse for left subtree
• Insertion of node
• If the root is NULL, create a new node with value element and
return it.
• Else, Compare element with root->data.
• element > root->data: recurse for right subtree
• element < root->data: recurse for left subtree
• Deletion of node
• Check if the root is NULL, if it is, just return the root itself. It's
an empty tree.
• element > root->data: recurse for right subtree
• element < root->data: recurse for left subtree
• If both above conditions above false, this means root->data=
item.
• Now we first need to check how many children does root have.
• CASE 1: No Child → Just delete root or deallocate space
occupied by it
• CASE 2: One Child →Replace root by its child
• CASE 3: Two Children
• Find the inorder successor of the root (Its the smallest
element of its right subtree). Let's call it new_root.
• Replace root by its inorder successor.
• Now recurse to the right subtree and delete new_root.
• Return the root and exit.
• Code:
#include <iostream>
using namespace std;
// tree node class
class BinaryTreeNode {
public:
int data;
BinaryTreeNode *left;
BinaryTreeNode *right;
BinaryTreeNode (int data) {
this->data=data;
left=NULL;
right=NULL;
}
};
// BST class
class BST {
BinaryTreeNode* root;
bool search(BinaryTreeNode* root, int data) {
if (root==NULL) {return false;}
if (root->data==data) {return true;}
else if (root->data>data) {return search(root->left, data);}
else {return search(root->right, data);}
}
BinaryTreeNode* insert(BinaryTreeNode* root,int data) {
if (root==NULL) {
BinaryTreeNode* node = new BinaryTreeNode (data);
return node;
}
if (root->data>=data) {
BinaryTreeNode* l = insert(root->left, data);
root->left = l;
}
else {
BinaryTreeNode* r = insert(root->right, data);
root->right = r;
}
return root;
}
// finds minimum element of a BST
int minimum (BinaryTreeNode* root) {
if (root->left !=NULL) {
return minimum (root->left);
}
else if (root->left ==NULL) {
return root->data;
}
}
BinaryTreeNode* deduct (BinaryTreeNode* root, int data) {
if (root==NULL) {return NULL;}
if (root->data>data) {
BinaryTreeNode* l = deduct(root->left, data);
root->left = l;
}
else if (root->data<data) {
BinaryTreeNode* r = deduct(root->right, data);
root->right = r;
}
else {
if (root->left==NULL && root->right==NULL) {
delete root;
return NULL;
}
else if (root->left!=NULL && root->right==NULL) {
BinaryTreeNode* temp = root->left;
delete root;
return temp;
}
else if (root->left==NULL && root->right!=NULL) {
BinaryTreeNode* temp = root->right;
delete root;
return temp;
}
else {
int mini=minimum(root->right);
root->data = mini;
BinaryTreeNode* ans = deduct(root->right, mini);
root->right=ans;
}
}
return root;
}
void print(BinaryTreeNode* root) {
if (root==NULL) {return;}
if (root->left!=NULL && root->right!=NULL)
{cout<<root->data<<":"<<"L"<<":"<<root->left-
>data<<","<<"R"<<":"<<root->right->data<<endl;
print(root->left);
print(root->right);}
else {cout<<root->data<<":"<<endl;}
}
// public functions of BST class
public:
BST (){
this->root=NULL;
}
void deduct(int data) {
BinaryTreeNode* new_root = deduct(root, data);
this->root= new_root;
return;
}
void print() {
print(this->root);
}
void insert(int data) {
BinaryTreeNode* new_root = insert(root, data);
this->root = new_root;
return;
}
bool search(int data) {
return search(root, data);
}
};
// driver code
int main()
{ BST b;
int choice, element;
while (true) {
cout<<"Choose from the following: \n1: Insert 2: Search 3: Delete 4:
Print BST"<<endl;
cin>>choice;
if (choice==1) {
cout<<"Enter element ";
cin>>element;
b.insert(element);
}
else if (choice==2) {
cout<<"Enter element to be searched ";
cin>>element;
if (b.search(element)) {cout<<"Element is present"<<endl;}
else {cout<<"Element is not present"<<endl;}
}
else if(choice==3) {
cout<<"Which element is to be deleted? "<<endl;
cin>>element;
b.deduct(element);
}
else if(choice==4) {
b.print();
}
else {break;}
}
return 0;
}
• Output:
• Learning: In a normal binary tree, if we need to search for an element, we
need to traverse the entire tree (in the worst case). But in BST, we can
optimize this by using its search property to our advantage. Time
complexity of all the operations performed on BST is O(h) where ‘h’ is the
height of BST. In the worst case this can become O(N) for a skewed BST. On
the other hand if the BST is balanced, height would be log(N) and time
complexity for all operations would be then be O(log(N)).