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

Data Structure and Algorithms: Samhitha Madala 12214200

The document provides an overview of data structures and algorithms, emphasizing their importance in software development and problem-solving. It covers various topics including algorithm analysis, recursion, and different data structures such as arrays, trees, and graphs, along with their associated algorithms. Additionally, it introduces a mini project for creating a Sudoku solver using C++, demonstrating practical applications of the concepts discussed.

Uploaded by

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

Data Structure and Algorithms: Samhitha Madala 12214200

The document provides an overview of data structures and algorithms, emphasizing their importance in software development and problem-solving. It covers various topics including algorithm analysis, recursion, and different data structures such as arrays, trees, and graphs, along with their associated algorithms. Additionally, it introduces a mini project for creating a Sudoku solver using C++, demonstrating practical applications of the concepts discussed.

Uploaded by

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

DATA STRUCTURE AND

ALGORITHMS
SELF-PACED

SAMHITHA MADALA
12214200
INTRODUCTION

 In today's rapidly evolving technological landscape, data structures and algorithms form
the backbone of efficient problem-solving and software development. Whether we are a
beginner stepping into the world of programming or an experienced developer looking to
deepen your understanding, mastering data structures and algorithms is crucial. These
foundational concepts are not only essential for writing optimized and effective code but
also for acing technical interviews, competing in coding competitions, and understanding
the inner workings of various software applications.
 Course Scope: This course covered topics like algorithm analysis, recursion, various data structures (arrays, trees,
graphs), and algorithmic paradigms (dynamic programming, greedy algorithms).
 Importance: Understanding DSA is crucial for solving computational problems efficiently and is applied in a
wide range of real-world applications, including search engines, databases, and artificial intelligence systems
Analysis Algorithm:
1. Best case: This represents the minimum time an algorithm will take to complete, given the
best possible input of size n.
2. Worst case: This represents the maximum time an algorithm will take to complete, given the
worst possible input of size n. It provides an upper bound on the running time.
3. Average case: This measures the expected time an algorithm will take to complete, averaged
over all
Linear possible
Search inputs
(in an unsorted array): of size n. It provides a realistic
Quicksort: estimate of an algorithm's performance.
o Best Case: O(1) (element is at the first position) o Best Case: O(n log n) (pivot divides array into two equal halves)
o Average Case: O(n) (element is equally likely to be anywhere) o Average Case: O(n log n)
o Worst Case: O(n) (element is at the last position or not present) o Worst Case: O(n^2)

Binary Search (in a sorted array): Merge Sort:


o Best Case: O(1) (element is at the middle position) o Best Case: O(n log n)
o Average Case: O(log n) o Average Case: O(n log n)
o Worst Case: O(log n) o Worst Case: O(n log n)

Bubble Sort: Insertion Sort:


o Best Case: O(n) (array is already sorted) o Best Case: O(n) (array is already sorted)
o Average Case: O(n^2) o Average Case: O(n^2)
o Worst Case: O(n^2) (array is in reverse order) o Worst Case: O(n^2) (array is in reverse order)
Big O Notation (O)
Big O notation provides an upper bound on the time (or space) complexity of an algorithm. It
describes the worst-case scenario by showing how the runtime increases as the input size n
grows.

Big Theta Notation (Θ)


Big Theta notation provides a tight bound on the time (or space) complexity of an algorithm. It
describes both the upper and lower bounds, showing the exact rate of growth.

Big Omega Notation (Ω)


Big Omega notation provides a lower bound on the time (or space) complexity of an algorithm.
It describes the best-case scenario by showing the minimum time the algorithm will take.

Little O Notation (o)


Little o notation provides a strict upper bound on the time complexity. It means that f(n)
grows strictly slower than g(n) as n approaches infinity.

Little Omega Notation (ω)


Little omega notation provides a strict lower bound on the time complexity. It indicates
that f(n) grows strictly faster than g(n).
Bitwise Magic
Bitwise operations are a powerful and efficient way to manipulate data at the binary level. They
operate directly on the individual bits of data, making them faster than arithmetic operations
for certain tasks.

 AND (&): Sets each bit to 1 if both corresponding bits are 1.


 OR (|): Sets each bit to 1 if at least one of the corresponding bits is 1
 XOR (^): Sets each bit to 1 if only one of the corresponding bits is 1 (exclusive OR).
 NOT (~): Inverts all the bits (0 becomes 1, and 1 becomes 0).
 Left Shift (<<): Shifts bits to the left, filling with 0s on the right.
 Right Shift (>>): Shifts bits to the right, discarding bits on the right.

Recursion
Recursion is a powerful programming technique where a function calls itself to solve smaller
instances of the same problem. It is widely used in algorithms, data structures, and problem-
solving in general.

Applications:

1. Sorting Algorithms: Algorithms like Quick Sort and Merge Sort use recursion to sort elements.
2. Tree Traversals: Pre-order, in-order, and post-order traversals of binary trees are naturally
3. Backtracking: Problems like solving a maze, N-Queens, and Sudoku use recursion to explore
different possibilities.
4. Divide and Conquer: Algorithms that split problems into smaller sub-problems (like binary
search) often use recursion.
5. Dynamic Programming: Some problems use a recursive approach with memorization to
optimize repetitive sub-problem calculations.

ARRAYS
Arrays are a fundamental data structure in programming that allow you to store a fixed-size
sequential collection of elements of the same type.

Types of Arrays:

1. Single-Dimensional Arrays: These are the most common type of arrays. They represent a list
of items of the same type, like a list of numbers or a list of names.
2. Multi-Dimensional Arrays: These arrays can have more than one dimension.
3. Character Arrays: Special type of arrays that hold characters, often used to store strings of
text.
Traversal: Visiting each element in the array to perform some action, like printing the
values or processing them.

Insertion: Adding a new element to the array. In static arrays, this typically involves shifting elements to make space, which can be time-consuming.

Deletion: Removing an element from the array, which usually requires shifting elements to fill the gap.

Searching: Finding the position of a particular element in the array. Linear search (checking each element one by one) and binary search (efficiently
searching sorted arrays) are common search methods.

Sorting: Arranging the elements of the array in a certain order (e.g., ascending or descending). Common sorting algorithms include bubble sort,
selection sort, merge sort, and quicksort.

SEARCHING
Searching is a fundamental operation in computer science used to find the position of a
specific element within a data structure, such as an array.

Linear Search: Linear search (or sequential search) involves checking each element in the array
sequentially until the desired element is found or the end of the array is reached.
Binary Search: Binary search is an efficient algorithm for finding an element in a sorted array by
repeatedly dividing the search interval in half.
Jump Search: Jump search is used on sorted arrays. It divides the array into blocks and performs a linear
search within the block where the target element might be found.
Interpolation Search: Interpolation search is an improvement over binary search for uniformly distributed
data. It estimates the position of the target element based on the value of the element and the target.
Exponential Search: Exponential search is used on sorted arrays. It first finds a range where the target
element might be located and then performs binary search within that range.

SORTING
Sorting is a fundamental operation in computer science that involves arranging elements in a
specific order, typically ascending or descending. Sorting algorithms are crucial for optimizing
performance in various applications, such as searching, data processing, and more.

Bubble Sort: Bubble Sort is a simple comparison-based algorithm that repeatedly steps through the list,
compares adjacent elements, and swaps them if they are in the wrong order.
Selection Sort: Selection Sort divides the array into two parts: a sorted section and an unsorted section. It
repeatedly selects the smallest (or largest) element from the unsorted section and moves it to the end of
the sorted section.
Insertion Sort: Insertion Sort builds the final sorted array one item at a time by repeatedly picking the
next item and inserting it into its correct position within the already sorted section.
Merge Sort: Merge Sort is a divide-and-conquer algorithm that divides the array into two halves, sorts
each half, and then merges the sorted halves to produce the final sorted array.
Quick Sort: Quick Sort is a divide-and-conquer algorithm that picks an element as a pivot and partitions
the array into elements less than the pivot and elements greater than the pivot. It then recursively sorts the
partitions.
Heap Sort: Heap Sort is based on the heap data structure. It first builds a max-heap (or min- heap), then
repeatedly extracts the maximum (or minimum) element from the heap and reconstructs the heap.

Hashing
Hashing is a technique used to map data to a fixed-size value or index, known as a hash code,
using a hash function. It is commonly used in hash tables for efficient data retrieval.

1. Hash Function: Converts input into a fixed-size code, determining data's location in a hash table.
2. Hash Table: A structure that stores key-value pairs using hashing for efficient retrieval.
3. Collision Handling: Resolves hash collisions using methods like chaining or open addressing.
4. Load Factor: Ratio of stored elements to table size, influencing the likelihood of collisions.
5. Applications: Used in database indexing, caching, and data deduplication for fast data access.

Linked List
A linked list is a linear data structure where elements, called nodes, are stored in non-
contiguous memory locations. Each node contains data and a reference (or link) to the next
node in the sequence.

Types:
1. Singly Linked List: Each node has a single link to the next node.
2. Doubly Linked List: Each node has two links, one to the next node and one to the previous node.
3. Circular Linked List: The last node links back to the first node, forming a circle.
Stack
A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. Elements are
added and removed from one end, called the top of the stack.

Operations:
1. Push: Add an element to the top of the stack.
2. Pop: Remove the top element from the stack.
3. Peek (or Top): Retrieve the top element without removing it.
4. IsEmpty: Check if the stack is empty.

Queue
A queue is a linear data structure that follows the First In, First Out (FIFO) principle. Elements
are added at the rear (or end) and removed from the front.

Operations:
1. Enqueue: Add an element to the rear of the queue.
2. Dequeue: Remove an element from the front of the queue.
3. Front: Retrieve the front element without removing it.

Deque (Double-Ended Queue)


A deque is a linear data structure that allows elements to be added or removed from both ends
(front and rear). It combines features of both stacks and queues.
1. AddFirst: Add an element to the front.
2. AddLast: Add an element to the rear.
3. RemoveFirst: Remove an element from the front.
4. RemoveLast: Remove an element from the rear.
5. PeekFirst: Retrieve the front element without removing it.
6. PeekLast: Retrieve the rear element without removing it.

Tree
A tree is a hierarchical data structure consisting of nodes connected by edges. It has a root
node and zero or more subtrees, each represented as a tree itself. Trees are used to represent
hierarchical relationships and organize data in a structured way.
1. Binary Tree: Each node has at most two children (left and right).
2. Binary Search Tree (BST): A binary tree where each node’s left subtree contains values less than the node, and the right
subtree contains values greater than the node.
3. N-ary Tree: A tree where each node can have up to NNN children.

Heap
A heap is a specialized tree-based data structure that satisfies the heap property. It can be a
max-heap or min-heap.

1. Max-Heap: The key at each node is greater than or equal to the keys of its children. The maximum key is at the root.
2. Min-Heap: The key at each node is less than or equal to the keys of its children. The minimum key is at the root.
Graph
A graph is a collection of nodes (vertices) and edges connecting pairs of nodes. Graphs can
represent various structures and relationships.
1. Directed Graph (Digraph): Edges have a direction, going from one vertex to another.
2. Undirected Graph: Edges have no direction, and the connection is mutual.
3. Weighted Graph: Edges have weights or costs associated with them.
4. Unweighted Graph: Edges have no weights.

Algorithms:

Depth-First Search (DFS): Explores as far as possible along each branch before backtracking.
Breadth-First Search (BFS): Explores all neighbours at the present depth before moving on to nodes at the next depth
level.
Dijkstra’s Algorithm: Finds the shortest path from a source node to all other nodes in a weighted graph.
Kruskal’s Algorithm: Finds the Minimum Spanning Tree (MST) for a weighted graph.
Prim’s Algorithm: Another algorithm for finding the MST.
Greedy Algorithms: Greedy algorithms build up a solution piece by piece, always choosing the next piece that offers the
most immediate benefit.
Dynamic Programming: Dynamic programming is a technique used to solve problems by breaking them down into
simpler subproblems and storing the results to avoid redundant computations.
INTRODUCTION TO THE MINI PROJECT 1: SUDOKU SOLVER USING DSA AND C++

The goal of this mini project is to develop a console-based Sudoku solver using C++. This
project leverages fundamental data structures and algorithms (DSA) to efficiently solve Sudoku
puzzles. It will help us understand how backtracking works, apply basic data structures, and
improving our problem-solving skills in C++.

Project Overview : Sudoku Solver:


Sudoku is a logic-based number-placement puzzle played on a 9x9 grid. The
goal is to fill the grid such that each row, each column, and each 3x3 sub grid contains the digits
1 to 9 without repetition. The solver will use backtracking to find a valid solution.

Key Features to Implement


1. Game Board Representation:
o Use a data structure (such as a 2D array) to represent the 9x9 Sudoku grid.
2. Game Logic:
o Move Validation: Ensure that a number placed in a cell follows Sudoku rules.
o Backtracking Algorithm: Implement a recursive function to fill the board with
valid numbers.
o Solution Display: Show the solved Sudoku grid once the solution is found.
3. User Interaction:
o Allow the user to input an incomplete Sudoku puzzle.
o Display the board before and after solving.
o Inform the user if the puzzle has no valid solution.
Data Structures and Algorithms Used
1. Array:
o Representation of the Board: Use a 2D array to store the Sudoku grid, where each
cell contains a number from 1 to 9 or remains empty.
2. Functions:
o printGrid(): A function to display the current state of the board.
o isValid(): A function to check whether a number can be placed in a given cell.
o solveSudoku(): A recursive function implementing backtracking to solve the
puzzle.
3. Backtracking Algorithm:
o Try placing a number from 1 to 9 in an empty cell.
o Check if the placement is valid.
o Recursively solve the rest of the board.
o If no solution is found, backtrack and try another number.
4. Game Execution:
o Load a partially filled Sudoku board.
o Solve the board using backtracking.
o Display the solved puzzle or inform the user if no solution exists.

CODE:

#include <iostream>
using namespace std;
#define N 9
bool isValid(int grid[N][N], int row, int col, int num) {
for (int x = 0; x < N; x++) {
if (grid[row][x] == num || grid[x][col] == num)
return false;
}
} if (grid[row][col] == 0 && isValid(grid, row, col, num)) {
return true; grid[row][col] = num;
} } else {
bool isSolved(int grid[N][N]) { cout << "Invalid move. Try again." << endl;
for (int i = 0; i < N; i++) { }
for (int j = 0; j < N; j++) { } else {
if (grid[i][j] == 0) cout << "Invalid input. Try again." << endl;
return false; }
} }
} cout << "Congratulations! You solved the Sudoku." << endl;
return true; printGrid(grid);
} return 0;
void printGrid(int grid[N][N]) { }
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cout << grid[i][j] << " ";
}
cout << endl;
}
}
int main() {
int grid[N][N] = {
{5, 3, 0, 0, 7, 0, 0, 0, 0},
{6, 0, 0, 1, 9, 5, 0, 0, 0},
{0, 9, 8, 0, 0, 0, 0, 6, 0},
{8, 0, 0, 0, 6, 0, 0, 0, 3},
{4, 0, 0, 8, 0, 3, 0, 0, 1},
{7, 0, 0, 0, 2, 0, 0, 0, 6},
{0, 6, 0, 0, 0, 0, 2, 8, 0},
{0, 0, 0, 4, 1, 9, 0, 0, 5},
{0, 0, 0, 0, 8, 0, 0, 7, 9}
};
while (!isSolved(grid)) {
printGrid(grid);
int row, col, num;
cout << "Enter row (0-8), column (0-8), and number (1-9): ";
cin >> row >> col >> num;
if (row >= 0 && row < N && col >= 0 && col < N && num >= 1 && num <= 9) {
OUTPUT
INTRODUCTION TO THE MINI PROJECT 2: TIC-TAC-TOE USING DSA AND C++

Tic-Tac-Toe Game: Tic-Tac-Toe is a classic two-player game played on a 3x3 grid. Players take turns marking a cell with
either an 'X' or an 'O'. The player who places three of their marks in a row (horizontally, vertically, or diagonally) wins. If all
cells are filled without any player winning, the game ends in a draw.

Key Features to Implement


1. Game Board Representation:
o Use a data structure to represent the 3x3 game board. For example, a 2D array or a
vector of vectors in C++.

2. Game Logic:
o Move Validation: Ensure that a move is valid (e.g., the chosen cell is empty and within bounds).
o Win Checking: Check if there is a winner after each move. This involves checking rows, columns, and diagonals.
o Draw Checking: Determine if the game is a draw (i.e., the board is full, and no player has won).

3. Player Interaction:
o Allow two players to take turns making moves.
o Provide a way to display the current state of the board after each move. o Announce the winner or if the game is a draw.

4. Game Restart:
o Provide an option to restart the game after it ends.
Data Structures and Algorithms Used
1. Array or Vector:
o Representation of the Board: Use a 2D array or a vector of vectors to store the
game state. Each cell can hold 'X', 'O', or be empty.

2. Functions:
o Display Board: A function to print the current state of the board.
o Make Move: A function to handle player moves and update the board.
o Check Win: A function to check if a player has won the game.
o Check Draw: A function to determine if the game has ended in a draw.

3. Input Validation:
o Ensure valid user input (e.g., check if the chosen cell is available).

4. Game Loop:
o Implement the main game loop that alternates between players and checks for
game end conditions.

CODE:
#include <iostream> #include <vector>
using namespace std;
// Constants for the board size const int SIZE = 3;
const char EMPTY = ' ';
const char PLAYER_X = 'X'; const char PLAYER_O = 'O';
// FuncNon prototypes
void printBoard(const vector<vector<char>>& board);
bool isBoardFull(const vector<vector<char>>& board);
bool checkWin(const vector<vector<char>>& board, char player);
bool makeMove(vector<vector<char>>& board, int row, int col, char player); if (board[i][j] == EMPTY) return false; }
bool isMoveValid(const vector<vector<char>>& board, int row, int col); }
int main() { return true; }
vector<vector<char>> board(SIZE, vector<char>(SIZE, EMPTY)); bool checkWin(const vector<vector<char>>& board, char player) { // Check rows and columns
char currentPlayer = PLAYER_X; for (int i = 0; i < SIZE; ++i) {
bool gameWon = false; if ((board[i][0] == player && board[i][1] == player && board[i][2] == player) || (board[0][i] == player &&
while (!isBoardFull(board) && !gameWon) { printBoard(board); board[1][i] == player && board[2][i] == player)) {
int row, col; return true; }
// Get move from player }
cout << "Player " << currentPlayer << ", enter your move (row and column): "; cin >> row >> col; // Check diagonals
// Adjust for 0-based index row--; if ((board[0][0] == player && board[1][1] == player && board[2][2] == player) || (board[0][2] == player
col--; && board[1][1] == player && board[2][0] == player)) {
// Validate and make the move return true; }
if (isMoveValid(board, row, col)) { return false; }
makeMove(board, row, col, currentPlayer); if (checkWin(board, currentPlayer)) { bool makeMove(vector<vector<char>>& board, int row, int col, char player) {
gameWon = true; if (row >= 0 && row < SIZE && col >= 0 && col < SIZE && board[row][col] == EMPTY) {
printBoard(board); board[row][col] = player;
cout << "Player " << currentPlayer << " wins!" << endl; return true; }
}else{ return false; }
// Switch player bool isMoveValid(const vector<vector<char>>& board, int row, int col) {
currentPlayer = (currentPlayer == PLAYER_X) ? PLAYER_O : PLAYER_X; } return row >= 0 && row < SIZE && col >= 0 && col < SIZE && board[row][col] == EMPTY; }
}else{
cout << "Invalid move, try again." << endl; }
}
if (!gameWon) { printBoard(board);
cout << "The game is a draw!" << endl; }
return 0; }
void printBoard(const vector<vector<char>>& board) { for (int i = 0; i < SIZE; ++i) {
for (int j = 0; j < SIZE; ++j) { cout << board[i][j];
if (j < SIZE - 1) cout << " | "; }
cout << endl;
if (i < SIZE - 1) cout << "---------" << endl; }
}
bool isBoardFull(const vector<vector<char>>& board) { for (int i = 0; i < SIZE; ++i) {
for (int j = 0; j < SIZE; ++j) {
OUTPUT
END

You might also like