DSA Notes
DSA Notes
1. 1D Array:
o A one-dimensional array is a collection of data items that are
stored in contiguous memory locations, typically used to store a
list of elements of the same data type. The elements are accessed
using a single index.
o Example: int arr[5] = {1, 2, 3, 4, 5};
2. 2D Array:
o A two-dimensional array is an array of arrays. It's used to store
data in a matrix form where elements are accessed using two
indices (row and column).
o Example: int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}.
Strings
1. strlen (String Length)
The strlen function returns the length of a C-style string (a character array),
excluding the null terminator (\0).
Explanation:
• We declare a character array str[] initialized with "Hello World".
• We use strlen(str) to get the length of the string, which counts all
characters except for the null terminator at the end.
• The result is then printed using cout.
Example:
#include <iostream>
#include <cstring> // For strlen
int main() {
char str[] = "Hello World";
int length = strlen(str); // Calculates the length of the string
cout << "Length of the string: " << length << endl; // Output: 11
return 0;
}
int main() {
char str1[20] = "Hello"; // str1 has extra space for concatenation
char str2[] = " World";
return 0;
}
int main() {
char source[] = "Hello";
char destination[20]; // Destination array has enough space
return 0;
}
Explanation:
• The reverseString() function uses a loop to swap the characters at the
start and end of the string, gradually moving towards the middle.
• The length of the string is calculated using strlen(str).
• Once the string is reversed, the function terminates, and we print the
reversed string.
Example:
#include <iostream>
#include <cstring> // For strlen
int main() {
char str[] = "Hello";
return 0;
}
5. strupr (Convert to Uppercase)
C++ does not have a built-in strupr function, but we can convert each
character to uppercase using the toupper function.
Explanation:
• The toUppercase() function iterates through the string and uses
toupper() to convert each character to uppercase.
• toupper() is part of the <cctype> library and converts a character to its
uppercase equivalent.
• After conversion, the modified string is printed.
Example:
#include <iostream>
#include <cctype> // For toupper
int main() {
char str[] = "hello";
toUppercase(str); // Converts the string to uppercase
cout << "Uppercase string: " << str << endl; // Output: HELLO
return 0;
}
6. strlwr (Convert to Lowercase)
Similar to strupr, C++ does not have a strlwr function, but we can convert each
character to lowercase using tolower.
Explanation:
• The toLowercase() function iterates through the string and uses
tolower() to convert each character to lowercase.
• tolower() is part of the <cctype> library and converts a character to its
lowercase equivalent.
• After conversion, the modified string is printed.
Example:
#include <iostream>
#include <cctype> // For tolower
int main() {
char str[] = "HELLO";
toLowercase(str); // Converts the string to lowercase
cout << "Lowercase string: " << str << endl; // Output: hello
return 0;
}
7. strcmp (String Comparison)
The strcmp function compares two C-style strings lexicographically. It returns:
• 0 if the strings are equal.
• A positive value if the first string is greater than the second.
• A negative value if the second string is greater than the first.
Explanation:
• We declare two-character arrays str1[] and str2[] for comparison.
• strcmp(str1, str2) compares the strings and returns an integer value
based on the lexicographical order.
• We check the result and print whether the strings are equal, or if one is
greater than the other.
Example:
#include <iostream>
#include <cstring> // For strcmp
int main() {
char str1[] = "Hello";
char str2[] = "World";
if (result == 0) {
cout << "Strings are equal" << endl;
} else if (result > 0) {
cout << "String 1 is greater" << endl;
} else {
cout << "String 2 is greater" << endl;
}
return 0;
}
Functions
3. Function Definition
A function definition provides the actual body (implementation) of the
function. It includes the code that gets executed when the function is called.
Syntax:
return_type function_name(parameter_list) {
// Function body
}
• Return type: Specifies the type of value the function will return.
• Function name: The name of the function.
• Parameter list: The list of parameters the function accepts.
• Function body: The block of code that defines what the function does.
It can include statements, expressions, and control flow structures.
Example:
int add(int a, int b) { // Function definition
return a + b; // Returns the sum of a and b
}
Here, the add function takes two integers as input, computes their sum, and
returns the result.
// Function declaration
int add(int a, int b);
int main() {
// Function call
int result = add(10, 20);
cout << "Sum: " << result << endl;
return 0;
}
// Function definition
int add(int a, int b) {
return a + b;
}
Explanation:
1. Function Declaration: int add(int a, int b); informs the compiler that a
function named add exists.
2. Function Call: add(10, 20); calls the add function, passing 10 and 20 as
arguments.
3. Function Definition: int add(int a, int b) { return a + b; } is the actual
code that computes the sum of a and b.
In this example, the function add calculates the sum of two integers and
returns the result.
Pointers.
A pointer is a variable that holds the memory address of another variable.
Pointers are one of the most powerful features of C++ because they allow for
efficient manipulation of memory and the ability to work with dynamic data
structures like linked lists and trees.
1. Compile-Time Memory Location
Compile-time memory refers to the memory allocated by the compiler at the
time of program compilation. Variables that are declared statically or globally
are allocated memory at compile-time. This type of memory allocation is
determined before the program runs.
Example:
int x = 10; // 'x' is allocated memory at compile time
int *ptr = &x; // 'ptr' is a pointer to 'x', storing its memory address
Here, the memory address of x is determined at compile-time. The pointer ptr
holds the address of x. Since x is a normal variable (not dynamically
allocated), its memory is fixed during compilation and cannot change at
runtime.
Explanation:
• Compile-time memory is typically used for variables with a fixed size
known at compile time, like local variables or global variables.
• The pointer ptr stores the address of the variable x, which is allocated by
the compiler.
• No memory management operations (like new or delete) are required
for compile-time memory.
2. Objects:
An object is an instance of a class. When a class is defined, no memory is
allocated until an object of that class is created. Objects contain specific data
represented by the class's attributes and can use the functions defined in the
class.
Creating Objects: You can create an object using the class name followed by
the object name.
Example:
int main() {
Car myCar; // Creating an object of the Car class
myCar.brand = "Toyota"; // Assigning values to data members
myCar.year = 2020;
class Convert {
char infix[30]; // Array to hold the infix expression
char postfix[30]; // Array to hold the postfix expression
char stk[30]; // Stack for operators
int top; // Stack pointer
public:
Convert() {
top = -1; // Initialize stack pointer
}
void read() {
cout << "Enter the infix expression: ";
cin >> infix; // Read the infix expression
}
void display() {
cout << "Infix Expression: " << infix << endl;
cout << "Postfix Expression: " << postfix << endl;
}
void infpost() {
int i = 0, j = 0; // Initialize indexes for infix and postfix
char symb;
char pop() {
char obj = stk[top];
top--; // Decrease stack pointer
return obj; // Return the popped object
}
};
int main() {
Convert C;
C.read(); // Read the infix expression
C.infpost(); // Convert infix to postfix
C.display(); // Display the results
return 0;
}
Postfix Evaluation
#include <iostream>
#define MAX 100
using namespace std;
class PostfEval {
private:
char postfix[MAX]; // Array to hold the postfix expression
int top; // Stack pointer
int stk[MAX]; // Stack for evaluation
public:
PostfEval() {
top = -1; // Initialize stack pointer
}
void push(int n) {
if (top + 1 == MAX) {
cout << "Stack is full\n";
return;
}
stk[++top] = n; // Push the value onto the stack
}
int pop() {
if (top != -1)
return stk[top--]; // Return the top value and decrement stack pointer
else {
cout << "Stack is empty\n";
return -1; // Indicate an error
}
}
void read() {
cout << "Enter a postfix expression (digits and operators): ";
cin >> postfix; // Read the postfix expression
}
int convert() {
int i = 0;
while (postfix[i] != '\0') {
// Check if the character is a digit
if (isdigit(postfix[i])) {
push(postfix[i] - '0'); // Convert char to int and push
} else {
// If the character is an operator, evaluate
int op2 = pop(); // Pop two operands from the stack
int op1 = pop(); // Pop the first operand
int result = evaloper(postfix[i], op1, op2); // Evaluate the operation
push(result); // Push the result back onto the stack
}
i++; // Move to the next character
}
return pop(); // Return the final result
}
int main() {
PostfEval eval; // Create an instance of PostfEval
eval.read(); // Read the postfix expression
int val = eval.convert(); // Convert and evaluate the expression
cout << "The value after evaluation is: " << val << endl; // Display the result
return 0;
}
Decimal To Binary
#include <iostream>
#define MAX 100
using namespace std;
class Stack
{
public:
int top;
int stk[MAX];
Stack()
{
top = -1;
}
void push(int obj)
{
stk[++top] = obj;
}
int pop()
{
int obj;
return obj = stk[top--];
}
};
int main() {
Stack s;
int num,n,rem,binary=0;
cout<<"Enter The Number To Be Converted: ";
cin>>num;
n = num;
while(n != 0)
{
rem = n % 2;
s.push(rem);
n = n / 2;
}
while(s.top != -1)
{
binary = binary * 10 + s.pop();
}
cout<<"Binary Value of "<<num<<" is: "<<binary;
return 0;
}
Queues
1. Linear Queue
A linear queue is a data structure that follows the First In First Out (FIFO)
principle. In a linear queue, elements are added at the rear and removed from
the front.
Key Characteristics:
• Fixed size: The size of a linear queue is usually fixed.
• Waste of space: Once elements are dequeued, the space becomes
wasted, as it cannot be reused without moving all elements.
Operations:
• Enqueue: Add an element to the rear of the queue.
• Dequeue: Remove an element from the front of the queue.
Example Implementation:
#include <iostream>
#define MAX 5
using namespace std;
class Queue {
int front, rear;
int queue[MAX];
public:
Queue()
{
front = rear = -1;
}
void fnInsert()
{
int obj;
if (rear == MAX - 1)
{
cout << "Queue is full" << endl;
}
cout<<"Enter Element To Be Inserted: ";
cin>>obj;
if (front == -1)
front = 0; // First element
queue[++rear] = obj; // Add element
}
void fnDelete()
{
int obj;
if (front == -1 || front > rear)
{
cout << "Queue is empty" << endl;
}
obj = queue[front++];
cout<<"Deleted ELement Is: "<<obj<<endl;// Display action
}
void display()
{
if (front == -1)
{
cout << "Queue is empty" << endl;
}
cout << "Queue elements: ";
for (int i = front; i <= rear; i++) {
cout << queue[i] << " ";
}
cout << endl;
}
};
int main() {
Queue q;
char ch;
while(1)
{
cout<<"MENU\n";
cout<<"1.Insert\n";
cout<<"2.Delete\n";
cout<<"3.Display\n";
cout<<"4.Exit\n";
cout<<"Enter Your Choice: \n";
cin>>ch;
switch(ch)
{
case '1':q.fnInsert();
break;
case '2':q.fnDelete();
break;
case '3':q.display();
break;
case '4':exit(0);
break;
}
}
return 0;
}
2. Circular Queue
A circular queue is an extension of a linear queue where the last position is
connected back to the first position to form a circle. This eliminates the
problem of wasted space.
Key Characteristics:
• Efficient use of space: It reuses the space of dequeued elements.
• Requires two pointers: Front and rear pointers are used to manage the
positions.
Operations:
• Enqueue: Add an element to the rear.
• Dequeue: Remove an element from the front.
Example Implementation:
#include <iostream>
#define MAX 5
using namespace std;
class CircularQueue {
int front, rear, queue[MAX];
public:
CircularQueue() {
front = rear = -1;
}
void fnInsert() {
int obj;
if ((rear + 1) % MAX == front) {
cout << "Queue is full" << endl;
return;
}
cout << "Enter Element To Be Inserted: ";
cin >> obj;
if (front == -1)
front = 0; // Set front on first insertion
rear = (rear + 1) % MAX; // Circular increment
queue[rear] = obj; // Add element after rear has been updated
}
void fnDelete() {
if (front == -1) {
cout << "Queue is empty" << endl;
return;
}
int obj = queue[front];
if (front == rear) {
// If only one element was present
front = rear = -1;
} else {
front = (front + 1) % MAX; // Circular increment
}
cout << "Deleted Element Is: " << obj << endl;
}
void display() {
if (front == -1) {
cout << "Queue is empty" << endl;
return;
}
cout << "Queue elements: ";
if (front <= rear) {
// If the queue has not wrapped
for (int i = front; i <= rear; i++) {
cout << queue[i] << " ";
}
} else {
// If the queue has wrapped
for (int i = front; i < MAX; i++) {
cout << queue[i] << " ";
}
for (int i = 0; i <= rear; i++) {
cout << queue[i] << " ";
}
}
cout << endl;
}
};
int main() {
CircularQueue q;
char ch;
while (true) {
cout << "MENU\n";
cout << "1. Insert\n";
cout << "2. Delete\n";
cout << "3. Display\n";
cout << "4. Exit\n";
cout << "Enter Your Choice: ";
cin >> ch;
switch (ch) {
case '1': q.fnInsert(); break;
case '2': q.fnDelete(); break;
case '3': q.display(); break;
case '4': exit(0); break;
default: cout << "Invalid choice, please try again." << endl;
}
}
return 0;
}
3. Double-Ended Queue (Deque)
A double-ended queue (deque) is a data structure that allows insertion and
deletion of elements from both ends, i.e., the front and the rear.
Key Characteristics:
• Supports operations at both ends.
• Can be implemented using arrays or linked lists.
Operations:
• Enqueue Front: Add an element at the front.
• Enqueue Rear: Add an element at the rear.
• Dequeue Front: Remove an element from the front.
• Dequeue Rear: Remove an element from the rear.
Example Implementation:
• InputRestrictedDeque
#include <iostream>
#define MAX 5
using namespace std;
class InputRestrictedDeque {
int front, rear, queue[MAX];
public:
InputRestrictedDeque() {
front = rear = -1;
}
void fnInsert() {
if (rear == MAX - 1) {
cout << "Deque is full, cannot insert at rear" << endl;
return;
}
int obj;
cout << "Enter Element To Be Inserted at Rear: ";
cin >> obj;
if (front == -1)
front = 0; // Set front on first insertion
queue[++rear] = obj; // Insert at rear
}
void fnDeleteFront() {
if (front == -1) {
cout << "Deque is empty" << endl;
return;
}
int obj = queue[front];
if (front == rear) {
// If only one element was present
front = rear = -1;
} else {
front++; // Move front forward
}
cout << "Deleted Element from Front Is: " << obj << endl;
}
void fnDeleteRear() {
if (front == -1) {
cout << "Deque is empty" << endl;
return;
}
int obj = queue[rear];
if (front == rear) {
// If only one element was present
front = rear = -1;
} else {
rear--; // Move rear backward
}
cout << "Deleted Element from Rear Is: " << obj << endl;
}
void display() {
if (front == -1) {
cout << "Deque is empty" << endl;
return;
}
cout << "Deque elements: ";
for (int i = front; i <= rear; i++) {
cout << queue[i] << " ";
}
cout << endl;
}
};
int main() {
InputRestrictedDeque dq;
char ch;
while (true) {
cout << "MENU\n";
cout << "1. Insert at Rear\n";
cout << "2. Delete from Front\n";
cout << "3. Delete from Rear\n";
cout << "4. Display\n";
cout << "5. Exit\n";
cout << "Enter Your Choice: ";
cin >> ch;
switch (ch) {
case '1': dq.fnInsert(); break;
case '2': dq.fnDeleteFront(); break;
case '3': dq.fnDeleteRear(); break;
case '4': dq.display(); break;
case '5': exit(0); break;
default: cout << "Invalid choice, please try again." << endl;
}
}
return 0;
}
• OutputRestrictedDeque
#include <iostream>
#define MAX 5
using namespace std;
class OutputRestrictedDeque {
int front, rear, queue[MAX];
public:
OutputRestrictedDeque() {
front = rear = -1;
}
void fnInsertFront() {
if (front == 0) {
cout << "Deque is full, cannot insert at front" << endl;
return;
}
int obj;
cout << "Enter Element To Be Inserted at Front: ";
cin >> obj;
if (front == -1)
front = 0; // Set front on first insertion
else
front--; // Move front backward
void fnInsertRear() {
if (rear == MAX - 1) {
cout << "Deque is full, cannot insert at rear" << endl;
return;
}
int obj;
cout << "Enter Element To Be Inserted at Rear: ";
cin >> obj;
if (rear == -1)
rear = 0; // Set rear on first insertion
else
rear++; // Move rear forward
void fnDelete() {
if (front == -1) {
cout << "Deque is empty" << endl;
return;
}
int obj = queue[front];
if (front == rear) {
// If only one element was present
front = rear = -1;
} else {
front++; // Move front forward
}
cout << "Deleted Element from Front Is: " << obj << endl;
}
void display() {
if (front == -1) {
cout << "Deque is empty" << endl;
return;
}
cout << "Deque elements: ";
for (int i = front; i <= rear; i++) {
cout << queue[i] << " ";
}
cout << endl;
}
};
int main() {
OutputRestrictedDeque dq;
char ch;
while (true) {
cout << "MENU\n";
cout << "1. Insert at Front\n";
cout << "2. Insert at Rear\n";
cout << "3. Delete from Front\n";
cout << "4. Display\n";
cout << "5. Exit\n";
cout << "Enter Your Choice: ";
cin >> ch;
switch (ch) {
case '1': dq.fnInsertFront(); break;
case '2': dq.fnInsertRear(); break;
case '3': dq.fnDelete(); break;
case '4': dq.display(); break;
case '5': exit(0); break;
default: cout << "Invalid choice, please try again." << endl;
}
}
return 0;
}
4. Priority Queue
A priority queue is an abstract data type where each element has a priority
assigned to it. Elements with higher priority are dequeued before elements
with lower priority.
Key Characteristics:
• Can be implemented using arrays, linked lists, or heaps.
• Not strictly FIFO: The order of dequeuing depends on priority.
Operations:
• Enqueue: Add an element with a priority.
• Dequeue: Remove and return the element with the highest priority.
Example Implementation:
#include <iostream>
#define MAX 5
using namespace std;
class PriorityQueue {
int front, rear;
int queue[MAX];
public:
PriorityQueue() {
front = rear = -1;
}
void fnInsert() {
if (rear == MAX - 1) {
cout << "Priority Queue is full" << endl;
return;
}
int obj;
cout << "Enter Element To Be Inserted: ";
cin >> obj;
if (front == -1)
front = 0; // Set front on first insertion
void fnDelete() {
if (front == -1) {
cout << "Priority Queue is empty" << endl;
return;
}
void display() {
if (front == -1) {
cout << "Priority Queue is empty" << endl;
return;
}
cout << "Priority Queue elements:" << endl;
for (int i = front; i <= rear; i++) {
cout << "Element: " << queue[i] << endl;
}
}
};
int main() {
PriorityQueue pq;
char ch;
while (true) {
cout << "MENU\n";
cout << "1. Insert\n";
cout << "2. Delete (min value)\n";
cout << "3. Display\n";
cout << "4. Exit\n";
cout << "Enter Your Choice: ";
cin >> ch;
switch (ch) {
case '1': pq.fnInsert(); break;
case '2': pq.fnDelete(); break;
case '3': pq.display(); break;
case '4': exit(0); break;
default: cout << "Invalid choice, please try again." << endl;
}
}
return 0;
}
Summary:
• Linear Queue: Basic FIFO structure; elements added at the rear and
removed from the front.
• Circular Queue: A circular version of a linear queue, allowing efficient
space usage.
• Double-Ended Queue (Deque): Allows insertion and deletion at both
ends.
• Priority Queue: Elements are dequeued based on priority, not strictly
FIFO.
These data structures are fundamental in computer science and have various
applications in algorithms and systems.