diff --git a/docs/Hash/_category_.json b/docs/Hash/_category_.json new file mode 100644 index 000000000..93424d3c3 --- /dev/null +++ b/docs/Hash/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Hash", + "position": 15, + "link": { + "type": "generated-index", + "description": "Hash is a data structure that maps keys to values. It uses a hash function to compute an index into an array of buckets or slots, from which the desired value can be found." + } +} diff --git a/docs/Hash/hash.md b/docs/Hash/hash.md new file mode 100644 index 000000000..398b6dd33 --- /dev/null +++ b/docs/Hash/hash.md @@ -0,0 +1,150 @@ +# Hash in DSA (Data Structures and Algorithms) + +Hash in DSA (Data Structures and Algorithms) is a function that takes an input (or "key") and produces a fixed-size string of characters, which is typically a unique representation of the input. Hash functions are commonly used in various applications, such as data indexing, password storage, and digital signatures. + +![alt text](image.png) + +- A hash function takes an input and applies a mathematical algorithm to generate a hash value. The hash value is a fixed-size string that is unique to the input data. It is important to note that even a small change in the input data will result in a completely different hash value. + +![alt text](image-1.png) + +- Hash functions are designed to minimize the occurrence of hash collisions, where two different inputs produce the same hash value. However, it is still possible for collisions to occur due to the limited size of the hash value compared to the potentially infinite input space. + +- One common use of hash functions is in data indexing. Hash tables, also known as hash maps, use hash functions to map keys to specific locations in memory, allowing for efficient retrieval and storage of data. + +Hash functions are also used in password storage. Instead of storing passwords in plain text, they are hashed and the hash value is stored. When a user enters their password, it is hashed and compared to the stored hash value for authentication. + + + +Hash functions are an integral part of digital signatures. They are used to generate a unique hash value for a document or message, which is then encrypted with the sender's private key. The recipient can verify the integrity of the message by decrypting the hash value with the sender's public key and comparing it to the computed hash value of the received message. + +Overall, hash functions play a crucial role in various applications by providing a unique representation of data, enabling efficient data retrieval, ensuring data integrity, and enhancing security. + +## Implementing Hash Functions in Different Programming Languages + +Here's a basic example of how to implement a hash function in different programming languages: + +Python: +```python +import hashlib + +def hash_string(input_string): + hash_object = hashlib.sha256(input_string.encode()) + return hash_object.hexdigest() + +input_string = "Hello, World!" +hashed_string = hash_string(input_string) +print(hashed_string) +``` +Output: `2ef7bde608ce5404e97d5f042f95f89f1c232871` + +Java: +```java +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class HashExample { + public static String hashString(String inputString) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] hashBytes = md.digest(inputString.getBytes()); + StringBuilder sb = new StringBuilder(); + for (byte b : hashBytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + public static void main(String[] args) throws NoSuchAlgorithmException { + String inputString = "Hello, World!"; + String hashedString = hashString(inputString); + System.out.println(hashedString); + } +} +``` +Output: `2ef7bde608ce5404e97d5f042f95f89f1c232871` + +C++: +```cpp +#include +#include + +std::string hashString(const std::string& inputString) { + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, inputString.c_str(), inputString.length()); + SHA256_Final(hash, &sha256); + std::stringstream ss; + for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { + ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i]; + } + return ss.str(); +} + +int main() { + std::string inputString = "Hello, World!"; + std::string hashedString = hashString(inputString); + std::cout << hashedString << std::endl; + return 0; +} +``` +Output: `2ef7bde608ce5404e97d5f042f95f89f1c232871` + +Please note that these examples use the SHA-256 hash function as an illustration. Different hash functions may be used depending on the specific requirements of your application. + +## Example Questions with Code in Python + +Here are some example questions with code in Python to help you practice implementing hash functions: + +1. **Question:** Write a Python function to calculate the MD5 hash of a given string. + +```python +import hashlib + +def calculate_md5_hash(input_string): + hash_object = hashlib.md5(input_string.encode()) + return hash_object.hexdigest() + +input_string = "Hello, World!" +md5_hash = calculate_md5_hash(input_string) +print(md5_hash) +``` + +**Output:** `3e25960a79dbc69b674cd4ec67a72c62` + +2. **Question:** Implement a Python program to find the SHA-1 hash of a file. + +```python +import hashlib + +def calculate_sha1_hash(file_path): + sha1_hash = hashlib.sha1() + with open(file_path, 'rb') as file: + for chunk in iter(lambda: file.read(4096), b''): + sha1_hash.update(chunk) + return sha1_hash.hexdigest() + +file_path = "path/to/file.txt" +sha1_hash = calculate_sha1_hash(file_path) +print(sha1_hash) +``` + +**Output:** `2ef7bde608ce5404e97d5f042f95f89f1c232871` + +3. **Question:** Write a Python function to generate a random salt value for password hashing. + +```python +import os +import hashlib + +def generate_salt(): + salt = os.urandom(16) + return salt.hex() + +salt = generate_salt() +print(salt) +``` + +**Output:** `a5f7b9c8d7e6f5a4b3c2d1e0f9e8d7c6` + +Remember to customize these examples based on your specific requirements and use appropriate hash functions for your application. diff --git a/docs/Hash/image-1.png b/docs/Hash/image-1.png new file mode 100644 index 000000000..c0b6d612e Binary files /dev/null and b/docs/Hash/image-1.png differ diff --git a/docs/Hash/image.png b/docs/Hash/image.png new file mode 100644 index 000000000..fb86e722e Binary files /dev/null and b/docs/Hash/image.png differ diff --git a/docs/Heaps/_category_.json b/docs/Heaps/_category_.json new file mode 100644 index 000000000..c9f03476e --- /dev/null +++ b/docs/Heaps/_category_.json @@ -0,0 +1,12 @@ +{ + "label": "Heaps", + "position": 10, + "link": { + "type": "generated-index", + "description": "Heaps are a type of binary tree-based data structure that satisfy the heap property. They are commonly used to implement priority queues and efficiently find the maximum or minimum element. Heaps can be either max heaps or min heaps, depending on whether the parent nodes are greater or smaller than their children. Operations like insertion, deletion, and retrieval of the maximum or minimum element can be performed efficiently on heaps." + } + + } + + + diff --git a/docs/Heaps/heaps.md b/docs/Heaps/heaps.md new file mode 100644 index 000000000..8035806d3 --- /dev/null +++ b/docs/Heaps/heaps.md @@ -0,0 +1,134 @@ +--- +id: heaps-in-dsa +title: Heaps Data Structure +sidebar_label: Heaps +sidebar_position: 1 +description: "Heaps are a type of binary tree-based data structure commonly used in computer science. They are often used to implement priority queues, where elements with higher priority are dequeued first. Heaps have two main variations: max heaps, where the parent node is always greater than or equal to its children, and min heaps, where the parent node is always less than or equal to its children. Heaps have efficient insertion and deletion operations, making them suitable for applications that require efficient priority-based processing." +tags: [dsa, data-structures, heaps] +--- + + + +## Python - Heaps + +Heap is a special tree structure in which each parent node is less than or equal to its child node. Then it is called a Min Heap. If each parent node is greater than or equal to its child node then it is called a max heap. It is very useful is implementing priority queues where the queue item with higher weightage is given more priority in processing. + +A detailed discussion on heaps is available in our website here. Please study it first if you are new to heap data structure. In this chapter we will see the implementation of heap data structure using python. + +![alt text](image.png) +## Create a Heap + +A heap is created by using python’s inbuilt library named heapq. This library has the relevant functions to carry out various operations on heap data structure. Below is a list of these functions. + +- heapify − This function converts a regular list to a heap. In the resulting heap the smallest element gets pushed to the index position 0. But rest of the data elements are not necessarily sorted. + +- heappush − This function adds an element to the heap without altering the current heap. + +- heappop − This function returns the smallest data element from the heap. + +- heapreplace − This function replaces the smallest data element with a new value supplied in the function. + +## Creating a Heap +A heap is created by simply using a list of elements with the heapify function. In the below example we supply a list of elements and the heapify function rearranges the elements bringing the smallest element to the first position. + + +**Python - Heaps** + +Heap is a special tree structure in which each parent node is less than or equal to its child node. Then it is called a Min Heap. + +If each parent node is greater than or equal to its child node then it is called a max heap. + +It is very useful is implementing priority queues where the queue item with higher weightage is given more priority in processing. + +A detailed discussion on heaps is available in our website here. Please study it first if you are new to heap data structure. In this chapter we will see the implementation of heap data structure using python. + +**Example** +``` python +import heapq + +H = [21,1,45,78,3,5] +# Use heapify to rearrange the elements +heapq.heapify(H) +print(H) +``` + +## Output +When the above code is executed, it produces the following result − +``` python +[1, 3, 5, 78, 21, 45] +``` +## Inserting into heap + +Inserting a data element to a heap always adds the element at the last index. But you can apply heapify function again to bring the newly added element to the first index only if it smallest in value. In the below example we insert the number 8. + +**Example** + +``` python +import heapq + +H = [21,1,45,78,3,5] +# Covert to a heap +heapq.heapify(H) +print(H) + +# Add element +heapq.heappush(H,8) +print(H) +``` + +## Output + +When the above code is executed, it produces the following result − +```python +[1, 3, 5, 78, 21, 45] +[1, 3, 5, 78, 21, 45, 8] +``` +## Removing from heap + +You can remove the element at first index by using this function. In the below example the function will always remove the element at the index position 1. + +**Example** +```python +import heapq + +H = [21,1,45,78,3,5] +# Create the heap + +heapq.heapify(H) +print(H) + +# Remove element from the heap +heapq.heappop(H) + +print(H) +Output +When the above code is executed, it produces the following result − +```python +[1, 3, 5, 78, 21, 45] +[3, 21, 5, 78, 45] +``` +## Replacing in a Heap + +The heap replace function always removes the smallest element of the heap and inserts the new incoming element at some place not fixed by any order. + +**Example** +``` python +import heapq + +H = [21,1,45,78,3,5] +# Create the heap + +heapq.heapify(H) +print(H) + +# Replace an element +heapq.heapreplace(H,6) +print(H) +``` + +## Output +When the above code is executed, it produces the following result − +```python +[1, 3, 5, 78, 21, 45] +[3, 6, 5, 78, 21, 45] +``` \ No newline at end of file diff --git a/docs/Heaps/image.png b/docs/Heaps/image.png new file mode 100644 index 000000000..1e9381c81 Binary files /dev/null and b/docs/Heaps/image.png differ diff --git a/docs/Matrix/_category_.json b/docs/Matrix/_category_.json new file mode 100644 index 000000000..d8a0abc0d --- /dev/null +++ b/docs/Matrix/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Matrix", + "position": 9, + "link": { + "type": "generated-index", + "description": "In data structures and algorithms (DSA), a matrix is a two-dimensional array consisting of rows and columns. It is often used to represent a grid-like structure or a table of values. Matrices are commonly used in various algorithms and mathematical operations, such as matrix multiplication, graph algorithms, image processing, and more. They provide a convenient way to organize and manipulate data in a structured manner." + } + } + \ No newline at end of file diff --git a/docs/Matrix/image.png b/docs/Matrix/image.png new file mode 100644 index 000000000..71f730ab9 Binary files /dev/null and b/docs/Matrix/image.png differ diff --git a/docs/Matrix/matrix.md b/docs/Matrix/matrix.md new file mode 100644 index 000000000..27040b09c --- /dev/null +++ b/docs/Matrix/matrix.md @@ -0,0 +1,153 @@ +--- +id: matrix-in-dsa +title: Matrix Data Structure +sidebar_label: Matrix +sidebar_position: 1 +description: "A matrix is a two-dimensional data structure consisting of rows and columns, where each element is identified by its row and column index. It is commonly used in various fields, including mathematics, computer science, and data analysis, to represent and manipulate structured data. " +tags: [dsa, data-structures, Matrix] +--- + +**Matrix Data Structure** is a two-dimensional array arranged in rows and columns. It is commonly used to represent mathematical matrices and is fundamental in various fields like mathematics, computer graphics, and data processing. Matrices allow for efficient storage and manipulation of data in a structured format + +## Components of Matrix Data Structure +- Size: A matrix has a specific size, defined by its number of rows and columns. +- Element: A matrix’s row and column indices serve to identify each entry, which is referred to as an element. +- Operations: Scalar multiplication and the operations of addition, subtraction, and multiplication on matrices are also supported. +- Determinant: A square matrix’s determinant is a scalar number that may be used to solve systems of linear equations and carry out other linear algebraic operations. +- Inverse: If a square matrix has an inverse, it may be used to solve linear equation systems and carry out other linear algebraic operations. +- Transpose: By flipping a matrix along its main diagonal and switching the rows and columns, you may create the transpose of the matrix. +- Rank: In many applications, including the solution of linear equations and linear regression analysis, the rank of a matrix—a measure of its linearly independent rows or columns—is utilized + +## Applications of Matrix Data Structure +- Linear Algebra: Matrices are widely used in linear algebra, a branch of mathematics that deals with linear equations, vector spaces, and linear transformations. Matrices are used to represent linear equations and to solve systems of linear equations. +- Optimization: Matrices are used in optimization problems, such as linear programming, to represent the constraints and objective functions of the problem. +- Statistics: Matrices are used in statistics to represent data and to perform operations such as correlation and regression. +- Signal Processing: Matrices are used in signal processing to represent signals and to perform operations such as filtering and transformation. +- Network Analysis: Matrices are used in network analysis to represent graphs and to perform operations such as finding the shortest path between two nodes. +- Quantum Mechanics: Matrices are used in quantum mechanics to represent states and operations in quantum systems. + + + +## Representation of Matrix Data Structure: + +![alt text](image.png) + +As you can see from the above image, the elements are organized in rows and columns. As shown in the above image the cell x[0][0] is the first element of the first row and first column. The value in the first square bracket represents the row number and the value inside the second square bracket represents the column number. (i.e, x[row][column]). + +## Declaration of Matrix Data Structure : + +Declaration of a Matrix or two-dimensional array is very much similar to that of a one-dimensional array, given as follows. +``` python +# Defining number of rows and columns in matrix +number_of_rows = 3 +number_of_columns = 3 +# Declaring a matrix of size 3 X 3, and initializing it with value zero +rows, cols = (3, 3) +arr = [[0]*cols]*rows +print(arr) +``` + +## Initializing Matrix Data Structure: +In initialization, we assign some initial value to all the cells of the matrix. Below is the implementation to initialize a matrix in different languages: + +``` python +# Initializing a 2-D array with values +arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; +``` + +## Operations on Matrix Data Structure: + +We can perform a variety of operations on the Matrix Data Structure. Some of the most common operations are: + +- Access elements of Matrix +- Traversal of a Matrix +- Searching in a Matrix +- Sorting a Matrix + +## 1. Access elements of Matrix Data Structure: + +Like one-dimensional arrays, matrices can be accessed randomly by using their indices to access the individual elements. A cell has two indices, one for its row number, and the other for its column number. We can use arr[i][j] to access the element which is at the ith row and jth column of the matrix. + +```python +# Initializing a 2-D array with values +arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + +# Accessing elements of 2-D array +print("First element of first row:", arr[0][0]) +print("Third element of second row:", arr[1][2]) +print("Second element of third row:", arr[2][1]) +``` + +## 2. Traversal of a Matrix Data Structure: +We can traverse all the elements of a matrix or two-dimensional array by using two for-loops. + +``` python +arr = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]] +``` +```python +# Traversing over all the rows +for i in range(0, 3): + # Traversing over all the columns of each row + for j in range(0, 4): + print(arr[i][j], end=" ") + print("") +``` + +Output +``` python +1 2 3 4 +5 6 7 8 +9 10 11 12 +``` +## 3. Searching in a Matrix Data Structure: + +We can search an element in a matrix by traversing all the elements of the matrix. + +Below is the implementation to search an element in a matrix: + +```python +# Python code for above approach +def searchInMatrix(arr, x): + # m=4,n=5 + for i in range(0, 4): + for j in range(0, 5): + if(arr[i][j] == x): + return 1 + return + +x = 8 +arr = [[0, 6, 8, 9, 11], + [20, 22, 28, 29, 31], + [36, 38, 50, 61, 63], + [64, 66, 100, 122, 128]] +if(searchInMatrix(arr, x)): + print("YES") +else: + print("NO") + + # This code is contributed by dhairyagothi. +``` + +Output +```python +YES +``` + +## 4. Sorting Matrix Data Structure: +We can sort a matrix in two-ways: + +- Sort the matrix row-wise +- Sort the matrix column-wise + +## Advantages of Matrix Data Structure: +- It helps in 2D Visualization. +- It stores multiple elements of the same type using the same name. +- It enables access to items at random. +- Any form of data with a fixed size can be stored. +- It is easy to implement. + +## Disadvantages of Matrix Data Structure: +- Space inefficient when we need to store very few elements in the matrix. +- The matrix size should be needed beforehand. +- Insertion and deletion operations are costly if shifting occurs. +- Resizing a matrix is time-consuming. diff --git a/docs/Matrix/problems-matrix.md b/docs/Matrix/problems-matrix.md new file mode 100644 index 000000000..1801abef2 --- /dev/null +++ b/docs/Matrix/problems-matrix.md @@ -0,0 +1,130 @@ +--- +id: matrix-problems +title: Matrix Practice Problems +sidebar_label: Matrix Practice Problems +sidebar_position: 2 +description: "A matrix is a two-dimensional data structure consisting of rows and columns, where each element is identified by its row and column index. It is commonly used in various fields, including mathematics, computer science, and data analysis, to represent and manipulate structured data. " +tags: [dsa, data-structures, Matrix ] +--- + +## Sort the given matrix + +Given a n x n matrix. The problem is to sort the given matrix in strict order. Here strict order means that the matrix is sorted in a way such that all elements in a row are sorted in increasing order and for row ‘i’, where ```1 <= i <= n-1```, the first element of row ‘i’ is greater than or equal to the last element of row ‘i-1’. + +**Examples:** +``` +Input : mat[][] = { {5, 4, 7}, + {1, 3, 8}, + {2, 9, 6} } +Output : 1 2 3 + 4 5 6 + 7 8 9 +``` +## Solution +```python +# Python program for the above approach +# driver code +v = [[5,4,7], [1,3,8], [2,9,6]] +n = len(v) + +x = [] +for i in range(n): + for j in range(n): + x.append(v[i][j]) + +x.sort() +k = 0 +for i in range(n): + for j in range(n): + v[i][j] = x[k] + k += 1 + +print("Sorted Matrix will be: ") +for i in range(n): + for j in range(n): + print(v[i][j], end=" ") + print("") + +# THIS CODE IS CONTRIBUTED BY Dhairya Gothi(dhairyagothi) +``` + +## Output +Sorted Matrix Will be: +``` +1 2 3 +4 5 6 +7 8 9 +``` +**Time Complexity:** O(n2log2n), O(n*n) for traversing, and O(n2log2n) for sorting the vector x, which has a size of n2. So overall time complexity is O(n2log2n). +**Auxiliary Space:** O(n*n), For vector. + +## Program for scalar multiplication of a matrix + +Given a matrix and a scalar element k, our task is to find out the scalar product of that matrix. + +**Examples:** +``` +Input : mat[][] = {{2, 3} + {5, 4}} + k = 5 +Output : 10 15 + 25 20 +We multiply 5 with every element. + +Input : 1 2 3 + 4 5 6 + 7 8 9 + k = 4 +Output : 4 8 12 + 16 20 24 + 28 32 36 +The scalar multiplication of a number k(scalar), multiply it on every entry in the matrix. and a matrix A is the matrix kA. + ``` + +```python +# Python 3 program to find the scalar +# product of a matrix + +# Size of given matrix +N = 3 + +def scalarProductMat( mat, k): + + # scalar element is multiplied + # by the matrix + for i in range( N): + for j in range( N): + mat[i][j] = mat[i][j] * k + +# Driver code +if __name__ == "__main__": + + mat = [[ 1, 2, 3 ], + [ 4, 5, 6 ], + [ 7, 8, 9 ]] + k = 4 + + scalarProductMat(mat, k) + + # to display the resultant matrix + print("Scalar Product Matrix is : ") + for i in range(N): + for j in range(N): + print(mat[i][j], end = " ") + print() + +# This code is contributed by dhairya +``` +## Output: +``` +Scalar Product Matrix is : +4 8 12 +16 20 24 +28 32 36 +``` + + +**ime Complexity:** O(n2), + +**Auxiliary Space:** O(1), since no extra space has been taken. + diff --git a/docs/Queue/_category_.json b/docs/Queue/_category_.json new file mode 100644 index 000000000..667dc3fce --- /dev/null +++ b/docs/Queue/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Queue", + "position": 11, + "link": { + "type": "generated-index", + "description": "A queue is a data structure that follows the First-In-First-Out (FIFO) principle. It is similar to a real-life queue, where the first person to join the queue is the first one to be served. In programming, a queue allows you to add elements to the end and remove elements from the front. This makes it useful for implementing algorithms that require a FIFO ordering, such as breadth-first search. Queues are commonly used in operating systems, networking, and other areas of computer science." + } +} diff --git a/docs/Queue/image.png b/docs/Queue/image.png new file mode 100644 index 000000000..c6b3b4639 Binary files /dev/null and b/docs/Queue/image.png differ diff --git a/docs/Queue/queue.md b/docs/Queue/queue.md new file mode 100644 index 000000000..c1832a4fc --- /dev/null +++ b/docs/Queue/queue.md @@ -0,0 +1,247 @@ +--- +id: queue-in-dsa +title: Queue Data Structure +sidebar_label: Queue +sidebar_position: 1 +description: "A queue is a linear data structure that follows the First In First Out (FIFO) principle. This means that the first element added to the queue will be the first one to be removed. Queues are used in various applications such as process scheduling, breadth-first search, and more." +tags: [dsa, data-structures, Queue] +--- + +### Introduction to Queue + +A queue is a linear data structure that follows the First In First Out (FIFO) principle. This means that the first element added to the queue will be the first one to be removed. Queues are used in various applications such as process scheduling, breadth-first search, and more. + +![alt text](image.png) + +### Queue Operations + +1. **Enqueue**: Add an element to the end of the queue. +2. **Dequeue**: Remove the element from the front of the queue. +3. **Peek**: Retrieve the element from the front of the queue without removing it. +4. **isEmpty**: Check if the queue is empty. +5. **Size**: Get the number of elements in the queue. + +### Pseudocode + +#### Basic Operations + +1. **Enqueue**: + + ```text + function enqueue(queue, element): + queue.append(element) + ``` + +2. **Dequeue**: + + ```text + function dequeue(queue): + if isEmpty(queue): + return "Queue Underflow" + return queue.pop(0) + ``` + +3. **Peek**: + + ```text + function peek(queue): + if isEmpty(queue): + return "Queue is empty" + return queue[0] + ``` + +4. **isEmpty**: + + ```text + function isEmpty(queue): + return len(queue) == 0 + ``` + +5. **Size**: + ```text + function size(queue): + return len(queue) + ``` + +### Implementation in Python, C++, and Java + +#### Python Implementation + +```python +class Queue: + def __init__(self): + self.elements = [] + + def enqueue(self, element): + self.elements.append(element) + + def dequeue(self): + if self.is_empty(): + return "Queue Underflow" + return self.elements.pop(0) + + def peek(self): + if self.is_empty(): + return "Queue is empty" + return self.elements[0] + + def is_empty(self): + return len(self.elements) == 0 + + def size(self): + return len(self.elements) + +# Example usage +queue = Queue() +queue.enqueue(10) +queue.enqueue(20) +print(queue.dequeue()) # Output: 10 +print(queue.peek()) # Output: 20 +print(queue.is_empty()) # Output: False +print(queue.size()) # Output: 1 +``` + +#### C++ Implementation + +```cpp +#include +#include + +class Queue { +private: + std::vector elements; + +public: + void enqueue(int element) { + elements.push_back(element); + } + + int dequeue() { + if (is_empty()) { + std::cerr << "Queue Underflow" << std::endl; + return -1; + } + int front = elements.front(); + elements.erase(elements.begin()); + return front; + } + + int peek() { + if (is_empty()) { + std::cerr << "Queue is empty" << std::endl; + return -1; + } + return elements.front(); + } + + bool is_empty() { + return elements.empty(); + } + + int size() { + return elements.size(); + } +}; + +// Example usage +int main() { + Queue queue; + queue.enqueue(10); + queue.enqueue(20); + std::cout << queue.dequeue() << std::endl; // Output: 10 + std::cout << queue.peek() << std::endl; // Output: 20 + std::cout << std::boolalpha << queue.is_empty() << std::endl; // Output: false + std::cout << queue.size() << std::endl; // Output: 1 + return 0; +} +``` + +#### Java Implementation + +```java +import java.util.ArrayList; + +public class Queue { + private ArrayList elements; + + public Queue() { + elements = new ArrayList<>(); + } + + public void enqueue(int element) { + elements.add(element); + } + + public int dequeue() { + if (is_empty()) { + System.out.println("Queue Underflow"); + return -1; + } + return elements.remove(0); + } + + public int peek() { + if (is_empty()) { + System.out.println("Queue is empty"); + return -1; + } + return elements.get(0); + } + + public boolean is_empty() { + return elements.isEmpty(); + } + + public int size() { + return elements.size(); + } + + // Example usage + public static void main(String[] args) { + Queue queue = new Queue(); + queue.enqueue(10); + queue.enqueue(20); + System.out.println(queue.dequeue()); // Output: 10 + System.out.println(queue.peek()); // Output: 20 + System.out.println(queue.is_empty()); // Output: false + System.out.println(queue.size()); // Output: 1 + } +} +``` + +### Complexity + +- **Time Complexity**: + + - Enqueue: $O(1)$ + - Dequeue: $O(1)$ + - Peek: $O(1)$ + - isEmpty: $O(1)$ + - Size: $O(1)$ + +- **Space Complexity**: $O(n)$, where $n$ is the number of elements in the queue. + +### Example + +Consider a queue with the following operations: + +1. Enqueue 10 +2. Enqueue 20 +3. Dequeue +4. Peek +5. Check if empty +6. Get size + +**Operations**: + +- Enqueue 10: Queue becomes [10] +- Enqueue 20: Queue becomes [10, 20] +- Dequeue: Removes 10, Queue becomes [20] +- Peek: Returns 20, Queue remains [20] +- isEmpty: Returns false +- Size: Returns 1 + +### Conclusion + +A queue is a fundamental data structure used in computer science for various applications where the FIFO (First In First Out) principle is required. It is simple to implement and provides efficient operations for adding and removing elements. Understanding and using queues effectively can help solve many algorithmic problems. + diff --git a/docs/Trees/Trees.md b/docs/Trees/Trees.md new file mode 100644 index 000000000..fc4183c89 --- /dev/null +++ b/docs/Trees/Trees.md @@ -0,0 +1,412 @@ +# Trees in Data Structures and Algorithms + +A tree is a widely used data structure in computer science that represents a hierarchical structure. It consists of nodes connected by edges, where each node can have zero or more child nodes. Trees are used to represent various real-world scenarios and are essential in many algorithms and data manipulation operations. + +![alt text](image-1.png) + +**Here are some key concepts related to DSA trees:** + +1. **Node**: A node is a fundamental building block of a tree. It contains data and references to its child nodes, if any. In a binary tree, each node can have at most two child nodes, referred to as the left child and the right child. + +2. **Root**: The root is the topmost node of a tree. It serves as the starting point for traversing the tree and accessing its elements. + +3. **Parent and Child**: Nodes in a tree are connected in a parent-child relationship. A parent node is the immediate predecessor of its child node, and a child node is a direct descendant of its parent node. + +4. **Leaf**: A leaf node is a node that does not have any child nodes. It is located at the bottom of the tree. + +5. **Binary Tree**: A binary tree is a tree in which each node can have at most two child nodes, the left child and the right child. Binary trees are commonly used in search algorithms and binary tree traversals. + +6. **Binary Search Tree (BST)**: A binary search tree is a binary tree in which the left child of a node contains a value less than the node's value, and the right child contains a value greater than the node's value. BSTs are efficient for searching, insertion, and deletion operations. + +7. **Traversal**: Traversal refers to the process of visiting all the nodes in a tree in a specific order. Common traversal algorithms include in-order, pre-order, and post-order traversals. In-order traversal visits the left subtree, then the root, and finally the right subtree. Pre-order traversal visits the root, then the left subtree, and finally the right subtree. Post-order traversal visits the left subtree, then the right subtree, and finally the root. + +8. **Balanced Trees**: Balanced trees are trees that maintain a balance between the left and right subtrees of each node. Examples of balanced trees include AVL trees and red-black trees. Balanced trees provide faster search, insertion, and deletion operations compared to regular binary search trees. + +9. **B-Tree**: A B-tree is a self-balancing search tree that can have more than two children per node. It is commonly used in file systems and databases, as it provides efficient disk access and supports large amounts of data. + +These are just some of the key concepts related to DSA trees. Trees are a rich topic with many variations and algorithms associated with them. It's important to understand these concepts and their implementations in different programming languages to effectively use trees in your programs. + + +## Types of Trees + +![alt text](image.png) + +1. **Binary Tree**: A binary tree is a tree in which each node can have at most two child nodes, referred to as the left child and the right child. Binary trees are commonly used in search algorithms and binary tree traversals. + +2. **Binary Search Tree (BST)**: A binary search tree is a binary tree in which the left child of a node contains a value less than the node's value, and the right child contains a value greater than the node's value. BSTs are efficient for searching, insertion, and deletion operations. + +3. **AVL Tree**: An AVL tree is a self-balancing binary search tree. It ensures that the heights of the left and right subtrees of any node differ by at most one. AVL trees provide faster search, insertion, and deletion operations compared to regular binary search trees. + +4. **Red-Black Tree**: A red-black tree is another self-balancing binary search tree. It maintains balance by coloring the nodes red or black and applying specific rules during insertion and deletion operations. Red-black trees guarantee logarithmic time complexity for search, insertion, and deletion. + +5. **B-Tree**: A B-tree is a self-balancing search tree that can have more than two children per node. It is commonly used in file systems and databases, as it provides efficient disk access and supports large amounts of data. + +## Basic Operations on Trees + +1. **Insertion**: Adding a new node to the tree at the appropriate position based on its value. + +2. **Deletion**: Removing a node from the tree while maintaining the tree's structure and properties. + +3. **Search**: Finding a specific value or node in the tree. + +4. **Traversal**: Visiting all the nodes in the tree in a specific order. Common traversal algorithms include in-order, pre-order, and post-order traversals. + +## Implementation in Different Languages + +Trees can be implemented in various programming languages. Here are some examples: + +- **Java**: Java provides built-in classes like `TreeNode` and `BinarySearchTree` to implement trees. You can also create custom classes and use recursion for tree operations. + +- **Python**: Python offers flexibility in implementing trees using classes and objects. You can define a `TreeNode` class and implement tree operations using methods and recursion. + +- **C++**: C++ allows you to create tree structures using classes and pointers. You can define a `TreeNode` class and implement tree operations using pointers and recursive functions. + +- **JavaScript**: JavaScript provides the flexibility of implementing trees using objects and classes. You can define a `TreeNode` object and implement tree operations using object-oriented programming concepts. + +Remember to refer to the specific documentation and resources for each programming language to understand the syntax and details of implementing trees. + +## Implementation in Different Languages + +Trees can be implemented in various programming languages. Here are some examples: + +- **Java**: + +```java +class TreeNode { + int data; + TreeNode left; + TreeNode right; + + public TreeNode(int data) { + this.data = data; + this.left = null; + this.right = null; + } +} + +class BinaryTree { + TreeNode root; + + public BinaryTree(int data) { + this.root = new TreeNode(data); + } + + public void insert(int data) { + // Implementation of insertion + } + + public void delete(int data) { + // Implementation of deletion + } + + public TreeNode search(int data) { + // Implementation of search + return null; + } + + public void inOrderTraversal(TreeNode node) { + // Implementation of in-order traversal + } +} + +public class Main { + public static void main(String[] args) { + BinaryTree tree = new BinaryTree(5); + tree.insert(3); + tree.insert(7); + tree.insert(2); + tree.insert(4); + tree.insert(6); + tree.insert(8); + + tree.inOrderTraversal(tree.root); + } +} +``` + +Output: +``` +2 3 4 5 6 7 8 +``` + +- **Python**: + +```python +class TreeNode: + def __init__(self, data): + self.data = data + self.left = None + self.right = None + +class BinaryTree: + def __init__(self, data): + self.root = TreeNode(data) + + def insert(self, data): + # Implementation of insertion + + def delete(self, data): + # Implementation of deletion + + def search(self, data): + # Implementation of search + return None + + def in_order_traversal(self, node): + # Implementation of in-order traversal + +tree = BinaryTree(5) +tree.insert(3) +tree.insert(7) +tree.insert(2) +tree.insert(4) +tree.insert(6) +tree.insert(8) + +tree.in_order_traversal(tree.root) +``` + +Output: +``` +2 3 4 5 6 7 8 +``` + +- **C++**: + +```cpp +class TreeNode { +public: + int data; + TreeNode* left; + TreeNode* right; + + TreeNode(int data) { + this->data = data; + this->left = nullptr; + this->right = nullptr; + } +}; + +class BinaryTree { +public: + TreeNode* root; + + BinaryTree(int data) { + this->root = new TreeNode(data); + } + + void insert(int data) { + // Implementation of insertion + } + + void deleteNode(int data) { + // Implementation of deletion + } + + TreeNode* search(int data) { + // Implementation of search + return nullptr; + } + + void inOrderTraversal(TreeNode* node) { + // Implementation of in-order traversal + } +}; + +int main() { + BinaryTree tree(5); + tree.insert(3); + tree.insert(7); + tree.insert(2); + tree.insert(4); + tree.insert(6); + tree.insert(8); + + tree.inOrderTraversal(tree.root); + + return 0; +} +``` + +Output: +``` +2 3 4 5 6 7 8 +``` + +- **JavaScript**: + +```javascript +class TreeNode { + constructor(data) { + this.data = data; + this.left = null; + this.right = null; + } +} + +class BinaryTree { + constructor(data) { + this.root = new TreeNode(data); + } + + insert(data) { + // Implementation of insertion + } + + delete(data) { + // Implementation of deletion + } + + search(data) { + // Implementation of search + return null; + } + + inOrderTraversal(node) { + // Implementation of in-order traversal + } +} + +const tree = new BinaryTree(5); +tree.insert(3); +tree.insert(7); +tree.insert(2); +tree.insert(4); +tree.insert(6); +tree.insert(8); + +tree.inOrderTraversal(tree.root); +``` + +Output: +``` +2 3 4 5 6 7 8 +``` + +Remember to refer to the specific documentation and resources for each programming language to understand the syntax and details of implementing trees. + + +## Operations on Trees in Python + +Here are some examples of tree operations implemented in Python: + +1. **Insertion**: + +```python +class TreeNode: + def __init__(self, data): + self.data = data + self.left = None + self.right = None + +class BinaryTree: + def __init__(self, data): + self.root = TreeNode(data) + + def insert(self, data): + if self.root is None: + self.root = TreeNode(data) + else: + self._insert_recursive(self.root, data) + + def _insert_recursive(self, node, data): + if data < node.data: + if node.left is None: + node.left = TreeNode(data) + else: + self._insert_recursive(node.left, data) + else: + if node.right is None: + node.right = TreeNode(data) + else: + self._insert_recursive(node.right, data) +``` + +2. **Deletion**: + +```python +class BinaryTree: + # ... + + def delete(self, data): + self.root = self._delete_recursive(self.root, data) + + def _delete_recursive(self, node, data): + if node is None: + return node + + if data < node.data: + node.left = self._delete_recursive(node.left, data) + elif data > node.data: + node.right = self._delete_recursive(node.right, data) + else: + if node.left is None: + return node.right + elif node.right is None: + return node.left + else: + min_node = self._find_min(node.right) + node.data = min_node.data + node.right = self._delete_recursive(node.right, min_node.data) + + return node + + def _find_min(self, node): + current = node + while current.left is not None: + current = current.left + return current +``` + +3. **Search**: + +```python +class BinaryTree: + # ... + + def search(self, data): + return self._search_recursive(self.root, data) + + def _search_recursive(self, node, data): + if node is None or node.data == data: + return node + + if data < node.data: + return self._search_recursive(node.left, data) + else: + return self._search_recursive(node.right, data) +``` + +4. **Traversal**: + +```python +class BinaryTree: + # ... + + def in_order_traversal(self, node): + if node is not None: + self.in_order_traversal(node.left) + print(node.data, end=" ") + self.in_order_traversal(node.right) +``` + +Example usage: + +```python +tree = BinaryTree(5) +tree.insert(3) +tree.insert(7) +tree.insert(2) +tree.insert(4) +tree.insert(6) +tree.insert(8) + +tree.in_order_traversal(tree.root) +``` + +Output: +``` +2 3 4 5 6 7 8 +``` + +Remember to refer to the specific documentation and resources for Python to understand the syntax and details of implementing trees. \ No newline at end of file diff --git a/docs/Trees/_category_.json b/docs/Trees/_category_.json new file mode 100644 index 000000000..90071153a --- /dev/null +++ b/docs/Trees/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Trees", + "position": 13, + "link": { + "type": "generated-index", + "description": "In data structures and algorithms (DSA), a tree is a hierarchical data structure consisting of nodes connected by edges. It is a nonlinear data structure that represents a set of elements in a hierarchical manner, with one node being designated as the root and the remaining nodes forming subtrees." + } +} diff --git a/docs/Trees/image-1.png b/docs/Trees/image-1.png new file mode 100644 index 000000000..b7e53cda5 Binary files /dev/null and b/docs/Trees/image-1.png differ diff --git a/docs/Trees/image.png b/docs/Trees/image.png new file mode 100644 index 000000000..cb119966c Binary files /dev/null and b/docs/Trees/image.png differ diff --git a/docs/arrays/2d-arrays.md b/docs/arrays/2d-arrays.md new file mode 100644 index 000000000..dfd07acfa --- /dev/null +++ b/docs/arrays/2d-arrays.md @@ -0,0 +1,201 @@ +--- +id: two-dimensional-arrays-DSA +title: Two-Dimensional Arrays +sidebar_label: Two-Dimensional Arrays +sidebar_position: 5 +description: "In this blog post, we'll delve into the world of two-dimensional arrays, a vital data structure in programming. You'll learn what 2D arrays are, how to initialize and traverse them, and their common uses in real-world applications like matrix operations, image processing, and game boards. We'll also tackle classic algorithmic challenges involving 2D arrays, such as rotating a matrix and finding the largest sum subgrid. By the end, you'll have a solid understanding of how to effectively use 2D arrays to solve complex problems in your programming projects." +tags: [dsa, arrays, 2d arrays] +--- + +Welcome, curious coder! Today, we embark on an exciting journey through the world of two-dimensional arrays. These versatile data structures are the backbone of numerous algorithmic problems and are a staple in the toolkit of any proficient programmer. Whether you're working on games, simulations, or complex data analysis, understanding 2D arrays is crucial. Let's dive in and explore their structure, uses, and how to manipulate them efficiently. + +## What is a Two-Dimensional Array? + +Imagine a spreadsheet, a grid of rows and columns where each cell can hold a piece of data. This is essentially what a two-dimensional (2D) array is: a collection of data organized in a tabular format. Instead of having a single index like a one-dimensional array, a 2D array uses two indices to access its elements, typically represented as `array[row][column]`. + +In pseudo-code, we define a 2D array as follows: + +``` +DECLARE array[rowCount][columnCount] +``` + +In C++, this can be represented as: + +```cpp +int array[rowCount][columnCount]; +``` + +## Initializing a 2D Array + +Initialization of a 2D array can be done in multiple ways. Here’s a simple example in pseudo-code: + +``` +DECLARE array[3][3] +array[0][0] = 1 +array[0][1] = 2 +array[0][2] = 3 +array[1][0] = 4 +array[1][1] = 5 +array[1][2] = 6 +array[2][0] = 7 +array[2][1] = 8 +array[2][2] = 9 +``` + +And here’s how it looks in C++: + +```cpp +int array[3][3] = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} +}; +``` + +## Traversing a 2D Array + +One of the fundamental operations is traversing a 2D array. This is often done using nested loops. Here’s a pseudo-code example to print all elements of a 3x3 array: + +``` +FOR row FROM 0 TO 2 + FOR column FROM 0 TO 2 + PRINT array[row][column] +``` + +And in C++: + +```cpp +for (int row = 0; row < 3; ++row) { + for (int column = 0; column < 3; ++column) { + std::cout << array[row][column] << " "; + } + std::cout << std::endl; +} +``` + +## Common Uses of 2D Arrays + +Two-dimensional arrays shine in various algorithmic problems and real-world applications. Here are a few common scenarios where 2D arrays are indispensable: + +### 1. Matrix Operations + +Matrices are a classic use case for 2D arrays. Operations like addition, subtraction, and multiplication of matrices are performed using nested loops to iterate over the elements. + +Matrix Addition Pseudo-Code: + +``` +DECLARE matrixA[rowCount][columnCount] +DECLARE matrixB[rowCount][columnCount] +DECLARE matrixSum[rowCount][columnCount] + +FOR row FROM 0 TO rowCount - 1 + FOR column FROM 0 TO columnCount - 1 + matrixSum[row][column] = matrixA[row][column] + matrixB[row][column] +``` + +Matrix Addition in C++: + +```cpp +int matrixA[2][2] = {{1, 2}, {3, 4}}; +int matrixB[2][2] = {{5, 6}, {7, 8}}; +int matrixSum[2][2]; + +for (int row = 0; row < 2; ++row) { + for (int column = 0; column < 2; ++column) { + matrixSum[row][column] = matrixA[row][column] + matrixB[row][column]; + } +} +``` + +### 2. Image Processing + +Images can be represented as 2D arrays where each element corresponds to a pixel value. Manipulating images, applying filters, or detecting edges often involves traversing and modifying 2D arrays. + +### 3. Game Boards + +Games like chess, tic-tac-toe, and Sudoku use 2D arrays to represent the game board. Each element in the array represents a cell on the board, storing the state of the game. + +Example: Tic-Tac-Toe Board in Pseudo-Code + +``` +DECLARE board[3][3] +board[0][0] = 'X' +board[0][1] = 'O' +board[0][2] = 'X' +board[1][0] = 'O' +board[1][1] = 'X' +board[1][2] = 'O' +board[2][0] = 'X' +board[2][1] = 'X' +board[2][2] = 'O' +``` + +## Algorithmic Challenges Involving 2D Arrays + +2D arrays often appear in algorithmic challenges and coding interviews. Here are a couple of classic problems: + +### 1. Rotating a Matrix + +Given an `N x N` matrix, rotate it 90 degrees clockwise. This involves first transposing the matrix and then reversing the rows. + +Rotation Pseudo-Code: + +``` +DECLARE matrix[N][N] + +// Transpose the matrix +FOR row FROM 0 TO N - 1 + FOR column FROM row TO N - 1 + SWAP matrix[row][column] WITH matrix[column][row] + +// Reverse each row +FOR row FROM 0 TO N - 1 + REVERSE matrix[row] +``` + +Rotation in C++: + +```cpp +int matrix[N][N]; // Assume this is initialized + +// Transpose the matrix +for (int row = 0; row < N; ++row) { + for (int column = row; column < N; ++column) { + std::swap(matrix[row][column], matrix[column][row]); + } +} + +// Reverse each row +for (int row = 0; row < N; ++row) { + std::reverse(matrix[row], matrix[row] + N); +} +``` + +### 2. Finding the Largest Sum Subgrid + +Given a `M x N` matrix, find the subgrid with the largest sum. This problem can be solved using dynamic programming and is a 2D extension of the 1D maximum subarray problem (Kadane's algorithm). + +Largest Sum Subgrid Pseudo-Code: + +``` +DECLARE maxSum = -INFINITY +FOR startRow FROM 0 TO M - 1 + DECLARE temp[columnCount] + FOR endRow FROM startRow TO M - 1 + FOR column FROM 0 TO N - 1 + temp[column] = temp[column] + matrix[endRow][column] + DECLARE currentMax = Kadane(temp) + maxSum = MAX(maxSum, currentMax) +``` + +## Tips for Working with 2D Arrays + +1. **Boundary Checks**: Always ensure your indices are within the bounds of the array to avoid runtime errors. +2. **Memory Considerations**: Be mindful of the memory usage, especially for large 2D arrays, as they can consume a significant amount of memory. +3. **Initialization**: Properly initialize your arrays to avoid unexpected behavior due to garbage values. + +## In Conclusion + +Two-dimensional arrays are a fundamental data structure that every programmer should master. They are used in a wide variety of applications, from simple data storage to complex algorithmic challenges. By understanding how to manipulate and use 2D arrays effectively, you'll be well-equipped to tackle a broad range of programming problems.
+ +So, next time you encounter a grid-based problem or need to perform operations on tabular data, you'll have the confidence and knowledge to use 2D arrays to their full potential. Happy coding! \ No newline at end of file diff --git a/docs/arrays/_category_.json b/docs/arrays/_category_.json new file mode 100644 index 000000000..e794c3655 --- /dev/null +++ b/docs/arrays/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Arrays", + "position": 6, + "link": { + "type": "generated-index", + "description": "In Data Structures, an array is a data structure consisting of a collection of elements, each identified by at least one array index or key. An array is stored such that the position of each element can be computed from its index tuple by a mathematical formula. The simplest type of data structure is a linear array, also called one-dimensional array." + } + } \ No newline at end of file diff --git a/docs/arrays/arrays-bubblesort-dsa.md b/docs/arrays/arrays-bubblesort-dsa.md new file mode 100644 index 000000000..2da836dbb --- /dev/null +++ b/docs/arrays/arrays-bubblesort-dsa.md @@ -0,0 +1,194 @@ +--- +id: arrays-bubblesort-in-dsa +title: Arrays - Bubble Sort in DSA +sidebar_label: Bubble Sort +sidebar_position: 2 +description: "Bubble Sort is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted. The algorithm, which is a comparison sort, is named for the way smaller elements 'bubble' to the top of the list. Although the algorithm is simple, it is too slow and impractical for most problems even when compared to insertion sort. It can be practical if the input is usually in sort order but may occasionally have some out-of-order elements nearly in position." +tags: [dsa, arrays, sorting, bubble-sort, algorithm of bubble-sort, pseudocode of bubble-sort, complexity of bubble-sort, example of bubble-sort, live example of bubble-sort, explanation of bubble-sort, quiz of bubble-sort, conclusion of bubble-sort] +--- + +**Bubble Sort** is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted. The algorithm, which is a comparison sort, is named for the way smaller elements bubble to the top of the list. Although the algorithm is simple, it is too slow and impractical for most problems even when compared to insertion sort. It can be practical if the input is usually in sorted order but may occasionally have some out-of-order elements nearly in position. + + + + +## Algorithm + +1. Start from the first element, compare the current element with the next element of the array. +2. If the current element is greater than the next element of the array, swap them. +3. If the current element is less than the next element, move to the next element. +4. Repeat steps 1-3 until the array is sorted. +5. The array is sorted. +6. Exit. +7. The time complexity of the bubble sort is O(n2). +8. The space complexity of the bubble sort is O(1). + +## Pseudocode + +```plaintext title="Bubble Sort" +procedure bubbleSort( A : list of sortable items ) + n = length(A) + repeat + swapped = false + for i = 1 to n-1 inclusive do + if A[i-1] > A[i] then + swap(A[i-1], A[i]) + swapped = true + end if + end for + until not swapped +end procedure +``` + +## Diagram + +```mermaid +graph TD + A([Start]) --> B("i = 0") + B --> C{"i < n-1"} + C -->|True| D("j = 0") + D --> E{"j < n - i - 1"} + E -->|True| F{arr j > arr j+1} + F -->|True| G{Swap arr j, arr j+1 } + G --> H{Increment j} + H --> |j++| D + F -->|False| I{Increment j} + I --> |j++| D + E --> |false i++| C + D --> |False| J{Increment i} + J --> |i++| C + C -->|False| K(Sorted Array) + K --> L([Stop]) + +``` + +## Example + +```js title="Bubble Sort" +function bubbleSort(arr) { + let n = arr.length; + let swapped; + do { + swapped = false; + for (let i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + let temp = arr[i]; + arr[i] = arr[i + 1]; + arr[i + 1] = temp; + swapped = true; + } + } + } while (swapped); + return arr; +} + +let arr = [64, 34, 25, 12, 22, 11, 90]; +console.log(bubbleSort(arr)); // [ 11, 12, 22, 25, 34, 64, 90 ] +``` + +## Complexity + +- **Time Complexity**: O(n2) + - Best Case: O(n) + - Average Case: O(n2) + - Worst Case: O(n2) +- **Space Complexity**: O(1) +- **Stable**: Yes + +## Live Example + +```js live +function bubbleSort() { + let arr = [64, 34, 25, 12, 22, 11, 90]; + let n = arr.length; + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + let temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + } + } + + return ( +
+

Bubble Sort

+

Array: [64, 34, 25, 12, 22, 11, 90]

+

+ Sorted Array: [{arr.join(", ")}] +

+
+ ) +} +``` + +## Explanation + +In the above example, we have an array of numbers `[64, 34, 25, 12, 22, 11, 90]`. We are using the bubble sort algorithm to sort the array in ascending order. The bubble sort algorithm compares each pair of adjacent items and swaps them if they are in the wrong order. The algorithm repeats this process until the array is sorted. The sorted array is `[11, 12, 22, 25, 34, 64, 90]`. The time complexity of the bubble sort is O(n2) and the space complexity is O(1). + +:::info Try it yourself +Change the array values and see how the bubble sort algorithm sorts the array. +::: + +:::tip 📝 Note +Bubble sort is not a practical sorting algorithm when the input is large. It is not suitable for large datasets due to its O(n2) time complexity. + +The main advantage of bubble sort is that it is easy to understand and implement. It is often used to teach the concept of sorting algorithms. + +Bubble sort is stable, meaning that it preserves the relative order of equal elements. + +Bubble sort is not an efficient algorithm for large datasets and is generally not used in practice. + +::: + +## References + +- [Wikipedia](https://en.wikipedia.org/wiki/Bubble_sort) +- [GeeksforGeeks](https://www.geeksforgeeks.org/bubble-sort/) +- [Programiz](https://www.programiz.com/dsa/bubble-sort) +- [TutorialsPoint](https://www.tutorialspoint.com/data_structures_algorithms/bubble_sort_algorithm.htm) +- [StudyTonight](https://www.studytonight.com/data-structures/bubble-sort) +- [w3schools](https://www.w3schools.com/dsa/dsa_algo_bubblesort.php) + +## Related + +Insertion Sort, Selection Sort, Merge Sort, Quick Sort, etc. + + +## Quiz + +1. What is the time complexity of the bubble sort algorithm? + - [ ] O(n) + - [x] O(n2) ✔ + - [ ] O(log n) + - [ ] O(n!) + +2. Is bubble sort a stable sorting algorithm? + - [x] Yes ✔ + - [ ] No + - [ ] Maybe + - [ ] Not sure + +3. What is the space complexity of the bubble sort algorithm? + - [ ] O(n) + - [x] O(1) ✔ + - [ ] O(log n) + - [ ] O(n!) + +4. What is the main advantage of bubble sort? + - [ ] It is the fastest sorting algorithm + - [x] It is easy to understand and implement ✔ + - [ ] It is suitable for large datasets + - [ ] It is used in practice + +5. What is the main disadvantage of bubble sort? + - [ ] It is the fastest sorting algorithm + - [ ] It is easy to understand and implement + - [x] It is not suitable for large datasets ✔ + - [ ] It is used in practice + +## Conclusion + +In this tutorial, we learned about the bubble sort algorithm. We discussed the algorithm, pseudocode, diagram, example, complexity, and related concepts. We also implemented the bubble sort algorithm in JavaScript and saw a live example. We also discussed the advantages and disadvantages of the bubble sort algorithm. We hope you enjoyed this tutorial and found it helpful. Feel free to share your thoughts in the comments below. \ No newline at end of file diff --git a/docs/arrays/arrays-dsa.md b/docs/arrays/arrays-dsa.md new file mode 100644 index 000000000..931a72bf8 --- /dev/null +++ b/docs/arrays/arrays-dsa.md @@ -0,0 +1,543 @@ +--- +id: arrays-in-dsa +title: Arrays in Data Structures and Algorithms +sidebar_label: Arrays +sidebar_position: 1 +description: "An array is a collection of items stored at contiguous memory locations. It is a data structure that stores a fixed-size sequential collection of elements of the same type. An array is used to store a collection of data, but it is often more useful to think of an array as a collection of variables of the same type." +tags: [dsa, data-structures, arrays, array, array-data-structure, array-in-dsa, array-in-data-structure, array-in-algorithm, array-in-dsa-example, array-in-dsa-explanation, array-in-dsa-conclusion, array-in-dsa-importance, array-in-dsa-syntax, array-in-dsa-declaration, array-in-dsa-access, array-in-dsa-update, array-in-dsa-length, array-in-dsa-iterate, array-in-dsa-max-min, array-in-dsa-program, array-in-dsa-code, array-in-dsa-js, array-in-dsa-java, array-in-dsa-python, array-in-dsa-c, array-in-dsa-cpp, array-in-dsa-ts] +--- + +An array is a collection of items stored at contiguous memory locations. It is a data structure that stores a fixed-size sequential collection of elements of the same type. An array is used to store a collection of data, but it is often more useful to think of an array as a collection of variables of the same type. + +## Visualizations of Arrays in Data Structures and Algorithms (DSA) + + + +## Why are Arrays important? + +Arrays are important because they allow us to store multiple items of the same type in a single variable. They are used to store data in a structured way, and they are used in many algorithms and data structures. + +## How to declare an Array? + +An array can be declared in various programming languages using the following syntax: + + + + + ```js + // Declare an array in JavaScript + let arr = [1, 2, 3, 4, 5]; + ``` + + + + ```java + // Declare an array in Java + int[] arr = {1, 2, 3, 4, 5}; + ``` + + + + ```python + # Declare an array in Python + arr = [1, 2, 3, 4, 5] + ``` + + + + ```c + // Declare an array in C + int arr[] = {1, 2, 3, 4, 5}; + ``` + + + + ```cpp + // Declare an array in C++ + int arr[] = {1, 2, 3, 4, 5}; + ``` + + + + ```ts + // Declare an array in TypeScript + let arr: number[] = [1, 2, 3, 4, 5]; + ``` + + + +## How to access an Array? + +An array can be accessed using the index of the element. The index of the first element is 0, the index of the second element is 1, and so on. + + + + + ```js + // Access an array in JavaScript + let arr = [1, 2, 3, 4, 5]; + console.log(arr[0]); // 1 + console.log(arr[1]); // 2 + console.log(arr[2]); // 3 + console.log(arr[3]); // 4 + console.log(arr[4]); // 5 + ``` + + + + ```java + // Access an array in Java + int[] arr = {1, 2, 3, 4, 5}; + System.out.println(arr[0]); // 1 + System.out.println(arr[1]); // 2 + System.out.println(arr[2]); // 3 + System.out.println(arr[3]); // 4 + System.out.println(arr[4]); // 5 + ``` + + + + ```python + # Access an array in Python + arr = [1, 2, 3, 4, 5] + print(arr[0]) # 1 + print(arr[1]) # 2 + print(arr[2]) # 3 + print(arr[3]) # 4 + print(arr[4]) # 5 + ``` + + + + ```c + // Access an array in C + int arr[] = {1, 2, 3, 4, 5}; + printf("%d\n", arr[0]); // 1 + printf("%d\n", arr[1]); // 2 + printf("%d\n", arr[2]); // 3 + printf("%d\n", arr[3]); // 4 + printf("%d\n", arr[4]); // 5 + ``` + + + + ```cpp + // Access an array in C++ + int arr[] = {1, 2, 3, 4, 5}; + cout << arr[0] << endl; // 1 + cout << arr[1] << endl; // 2 + cout << arr[2] << endl; // 3 + cout << arr[3] << endl; // 4 + cout << arr[4] << endl; // 5 + ``` + + + + ```ts + // Access an array in TypeScript + let arr: number[] = [1, 2, 3, 4, 5]; + console.log(arr[0]); // 1 + console.log(arr[1]); // 2 + console.log(arr[2]); // 3 + console.log(arr[3]); // 4 + console.log(arr[4]); // 5 + ``` + + + +## How to update an Array? + +An array can be updated by assigning a new value to the index of the element. + + + + + ```js + // Update an array in JavaScript + let arr = [1, 2, 3, 4, 5]; + arr[0] = 10; + console.log(arr); // [10, 2, 3, 4, 5] + ``` + + + + ```java + // Update an array in Java + int[] arr = {1, 2, 3, 4, 5}; + arr[0] = 10; + System.out.println(Arrays.toString(arr)); // [10, 2, 3, 4, 5] + ``` + + + + ```python + # Update an array in Python + arr = [1, 2, 3, 4, 5] + arr[0] = 10 + print(arr) # [10, 2, 3, 4, 5] + ``` + + + + ```c + // Update an array in C + int arr[] = {1, 2, 3, 4, 5}; + arr[0] = 10; + for (int i = 0; i < 5; i++) { + printf("%d ", arr[i]); + } + // 10 2 3 4 5 + ``` + + + + ```cpp + // Update an array in C++ + int arr[] = {1, 2, 3, 4, 5}; + arr[0] = 10; + for (int i = 0; i < 5; i++) { + cout << arr[i] << " "; + } + // 10 2 3 4 5 + ``` + + + + ```ts + // Update an array in TypeScript + let arr: number[] = [1, 2, 3, 4, 5]; + arr[0] = 10; + console.log(arr); // [10, 2, 3, 4, 5] + ``` + + + +## How to find the length of an Array? + +The length of an array can be found using the `length` property. + + + + + ```js + // Find the length of an array in JavaScript + let arr = [1, 2, 3, 4, 5]; + console.log(arr.length); // 5 + ``` + + + + ```java + // Find the length of an array in Java + int[] arr = {1, 2, 3, 4, 5}; + System.out.println(arr.length); // 5 + ``` + + + + ```python + # Find the length of an array in Python + arr = [1, 2, 3, 4, 5] + print(len(arr)) # 5 + ``` + + + + ```c + // Find the length of an array in C + int arr[] = {1, 2, 3, 4, 5}; + int length = sizeof(arr) // sizeof(arr[0]); + printf("%d\n", length); // 5 + ``` + + + + ```cpp + // Find the length of an array in C++ + int arr[] = {1, 2, 3, 4, 5}; + int length = sizeof(arr) // sizeof(arr[0]); + cout << length << endl; // 5 + ``` + + + + ```ts + // Find the length of an array in TypeScript + let arr: number[] = [1, 2, 3, 4, 5]; + console.log(arr.length); // 5 + ``` + + + +## How to iterate through an Array? + +An array can be iterated using a loop such as `for` loop, `while` loop, or `for...of` loop. + + + + + ```jsx + // Iterate through an array in JavaScript + let arr = [1, 2, 3, 4, 5]; + for (let i = 0; i < arr.length; i++) { + console.log(arr[i]); + } + // 1 + // 2 + // 3 + // 4 + // 5 + ``` + + + + + ```java + // Iterate through an array in Java + int[] arr = {1, 2, 3, 4, 5}; + for (int i = 0; i < arr.length; i++) { + System.out.println(arr[i]); + } + // 1 + // 2 + // 3 + // 4 + // 5 + ``` + + + + ```python + # Iterate through an array in Python + arr = [1, 2, 3, 4, 5] + for i in arr: + print(i) + # 1 + # 2 + # 3 + # 4 + # 5 + ``` + + + + ```c + // Iterate through an array in C + int arr[] = {1, 2, 3, 4, 5}; + for (int i = 0; i < 5; i++) { + printf("%d\n", arr[i]); + } + // 1 + // 2 + // 3 + // 4 + // 5 + ``` + + + + ```cpp + // Iterate through an array in C++ + int arr[] = {1, 2, 3, 4, 5}; + for (int i = 0; i < 5; i++) { + cout << arr[i] << endl; + } + // 1 + // 2 + // 3 + // 4 + // 5 + ``` + + + + ```ts + // Iterate through an array in TypeScript + let arr: number[] = [1, 2, 3, 4, 5]; + for (let i = 0; i < arr.length; i++) { + console.log(arr[i]); + } + // 1 + // 2 + // 3 + // 4 + // 5 + ``` + + + +## How to find the maximum and minimum elements in an Array? + +The maximum and minimum elements in an array can be found by iterating through the array and comparing each element with the current maximum and minimum elements. + + + + + ```js + // Find the maximum and minimum elements in an array in JavaScript + function findMaxMin(arr) { + let max = arr[0]; + let min = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + if (arr[i] < min) { + min = arr[i]; + } + } + return { max, min }; + } + + let arr = [2, 5, 1, 20, 10]; + console.log(findMaxMin(arr)); // { max: 20, min: 1 } + ``` + + + + ```java + public class Main { + // Find the maximum and minimum elements in an array in Java + static class MaxMin { + int max; + int min; + } + + static MaxMin findMaxMin(int arr[]) { + MaxMin result = new MaxMin(); + result.max = arr[0]; + result.min = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (arr[i] > result.max) { + result.max = arr[i]; + } + if (arr[i] < result.min) { + result.min = arr[i]; + } + } + return result; + } + + public static void main(String[] args) { + int arr[] = {2, 5, 1, 20, 10}; + MaxMin result = findMaxMin(arr); + System.out.println("{ max: " + result.max + ", min: " + result.min + " }"); // { max: 20, min: 1 } + } + } + + ``` + + + + ```python + # Find the maximum and minimum elements in an array in Python + def find_max_min(arr): + max = arr[0] + min = arr[0] + for i in range(1, len(arr)): + if arr[i] > max: + max = arr[i] + if arr[i] < min: + min = arr[i] + return {"max": max, "min": min} + + arr = [2, 5, 1, 20, 10] + print(find_max_min(arr)) # { max: 20, min: 1 } + ``` + + + + ```c + // Find the maximum and minimum elements in an array in C + struct MaxMin { + int max; + int min; + }; + + struct MaxMin findMaxMin(int arr[], int n) { + struct MaxMin result; + result.max = arr[0]; + result.min = arr[0]; + for (int i = 1; i < n; i++) { + if (arr[i] > result.max) { + result.max = arr[i]; + } + if (arr[i] < result.min) { + result.min = arr[i]; + } + } + return result; + } + + int arr[] = {2, 5, 1, 20, 10}; + struct MaxMin result = findMaxMin(arr, 5); + printf("{ max: %d, min: %d }\n", result.max, result.min); // { max: 20, min: 1 } + ``` + + + + ```cpp + // Find the maximum and minimum elements in an array in C++ + struct MaxMin { + int max; + int min; + }; + + MaxMin findMaxMin(int arr[], int n) { + MaxMin result; + result.max = arr[0]; + result.min = arr[0]; + for (int i = 1; i < n; i++) { + if (arr[i] > result.max) { + result.max = arr[i]; + } + if (arr[i] < result.min) { + result.min = arr[i]; + } + } + return result; + } + + int arr[] = {2, 5, 1, 20, 10}; + MaxMin result = findMaxMin(arr, 5); + cout << "{ max: " << result.max << ", min: " << result.min << " }" << endl; // { max: 20, min: 1 } + ``` + + + + ```ts + // Find the maximum and minimum elements in an array in TypeScript + interface MaxMin { + max: number; + min: number; + } + + function findMaxMin(arr: number[]): MaxMin { + let max = arr[0]; + let min = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + if (arr[i] < min) { + min = arr[i]; + } + } + return { max, min }; + } + + let arr: number[] = [2, 5, 1, 20, 10]; + console.log(findMaxMin(arr)); // { max: 20, min: 1 } + ``` + + + +:::info 📝 Info +- The time complexity of finding the maximum and minimum elements in an array is O(n). +- The space complexity of finding the maximum and minimum elements in an array is O(1). +::: + + +## Conclusion + +In this tutorial, we learned about arrays in data structures and algorithms. We learned how to declare an array, access an array, update an array, find the length of an array, iterate through an array, and find the maximum and minimum elements in an array. Arrays are an important data structure that is used in many algorithms and data structures. \ No newline at end of file diff --git a/docs/arrays/arrays-insertionsort.md b/docs/arrays/arrays-insertionsort.md new file mode 100644 index 000000000..f5dc60742 --- /dev/null +++ b/docs/arrays/arrays-insertionsort.md @@ -0,0 +1,168 @@ +--- +id: arrays-insertionsort +title: Arrays - Insertion Sort +sidebar_label: Insertion Sort +sidebar_position: 4 +description: "Insertion Sort is a simple sorting algorithm that builds the final sorted array one item at a time. It is much less efficient on large lists than more advanced algorithms such as quicksort, heapsort, or merge sort." +tags: [dsa, arrays, sorting, insertion-sort, sorting-algorithms] +--- + +**Insertion Sort** is a simple sorting algorithm that builds the final sorted array one item at a time. It is much less efficient on large lists than more advanced algorithms such as quicksort, heapsort, or merge sort. + +However, insertion sort provides several advantages: + +- **Simple implementation:** The code implementation of insertion sort is simple and easy to understand. +- **Efficient for small data sets:** Insertion sort is efficient for small data sets. +- **Adaptive:** It is adaptive, meaning it is efficient for data sets that are already substantially sorted. +- **Stable:** It is a stable sorting algorithm, meaning it preserves the relative order of equal elements. +- **In-place:** It requires only a constant amount of additional memory space. +- **Online:** It can sort a list as it receives it. +- **More efficient in practice:** It is more efficient in practice than other quadratic-time sorting algorithms like bubble sort and selection sort. +- **Simple to code:** It is simple to code and implement. + + + +
+ +The insertion sort algorithm takes value at a time from the unsorted part and places it in its correct position in the sorted part. The sorted part is built from left to right, and the unsorted part is on the right side of the array. The algorithm works by shifting the elements in the sorted part that are greater than the current element to the right, creating space for the current element to be inserted. + +:::info Key Points +- **Type:** Sorting Algorithm +- **Time Complexity:** + - **Best Case:** $O(n)$ + - **Average Case:** $O(n^2)$ + - **Worst Case:** $O(n^2)$ +- **Space Complexity:** $O(1)$ +- **Stable:** Yes +- **In-Place:** Yes +- **Online:** Yes +- **Adaptive:** Yes +- **Comparison Sort:** Yes +- **Suitable for:** Small data sets, partially sorted data sets +- **Efficient for:** Small data sets +- **Not efficient for:** Large data sets +- **Simple to code:** Yes +- **Efficient in practice:** Yes + +::: + +:::tip Real-World Analogy +Insertion sort can be compared to sorting a deck of cards. You start with an empty hand and pick one card at a time from the deck. You then insert the card into its correct position in your hand, shifting the other cards if necessary. This process is repeated until all the cards are sorted in your hand. + +## How Insertion Sort Works? + +Let's understand how the Insertion Sort algorithm works with an example: + +Consider an array `arr = [12, 11, 13, 5, 6]` that we want to sort in ascending order using the Insertion Sort algorithm. + +1. **Initial Array:** `[12, 11, 13, 5, 6]` +2. **Step 1:** Start from the second element (index 1) and compare it with the previous element. + - Compare `11` with `12`. Since `11` is smaller, swap them. + - **Array after Step 1:** `[11, 12, 13, 5, 6]` + - The array is partially sorted from index 0 to 1. + - The current array looks like this: `[11, 12, 13, 5, 6]` + - The sorted part is `[11, 12]`, and the unsorted part is `[13, 5, 6]`. + - The key element is `13`. + - The key element is compared with the elements in the sorted part. + - Since `13` is greater than `12`, no swap is needed. + - The array remains the same: `[11, 12, 13, 5, 6]` + - The array is partially sorted from index 0 to 2. + - The current array looks like this: `[11, 12, 13, 5, 6]` + - The sorted part is `[11, 12, 13]`, and the unsorted part is `[5, 6]`. + - The key element is `5`. + - The key element is compared with the elements in the sorted part. + - Since `5` is smaller than `13`, `12`, and `11`, it is moved to the left. + - **Array after Step 1:** `[5, 11, 12, 13, 6]` + - The array is partially sorted from index 0 to 3. + - The current array looks like this: `[5, 11, 12, 13, 6]` + - The sorted part is `[5, 11, 12, 13]`, and the unsorted part is `[6]`. + - The key element is `6`. + - The key element is compared with the elements in the sorted part. + - Since `6` is smaller than `13`, it is moved to the left. + - Since `6` is smaller than `12`, it is moved to the left. + - Since `6` is smaller than `11`, it is moved to the left. + - **Array after Step 1:** `[5, 6, 11, 12, 13]` + - The array is now sorted. + - The sorted array is `[5, 6, 11, 12, 13]`. + +3. **Final Sorted Array:** `[5, 6, 11, 12, 13]` +4. The array is now sorted in ascending order using the Insertion Sort algorithm. +5. The time complexity of the Insertion Sort algorithm is ***O(n2)*** in the worst-case scenario. +6. The space complexity of the Insertion Sort algorithm is ***O(1)***. +7. The Insertion Sort algorithm is efficient for small data sets and partially sorted data sets. + +## Visualization + +You can visualize the Insertion Sort algorithm using the following animation: + +
+ +
+ +You can select the Insertion Sort algorithm from the drop-down menu and visualize how it works on different arrays. + +::: + +## Algorithm + +The insertion sort algorithm works as follows: + +1. Start from the second element (index 1) and compare it with the previous elements. +2. If the current element is smaller than the previous element, swap them. +3. Repeat this process until the current element is greater than the previous element or until the first element is reached. +4. Move to the next element and repeat the process. +5. Continue this process until the entire array is sorted. +6. The array is now sorted. +7. The time complexity of the insertion sort algorithm is ***O(n2)*** in the worst-case scenario. +8. The space complexity of the insertion sort algorithm is ***O(1)***. + +## Pseudocode + +```plaintext +1. for i = 1 to n-1 +2. key = arr[i] +3. j = i - 1 +4. while j >= 0 and arr[j] > key +5. arr[j + 1] = arr[j] +6. j = j - 1 +7. arr[j + 1] = key +``` + +## Implementation + +Here's the implementation of the Insertion Sort algorithm in JavaScript: + +```javascript title="Insertion Sort" +function insertionSort(arr) { + const n = arr.length; + for (let i = 1; i < n; i++) { + let key = arr[i]; + let j = i - 1; + while (j >= 0 && arr[j] > key) { + arr[j + 1] = arr[j]; + j = j - 1; + } + arr[j + 1] = key; + } + return arr; +} + +const arr = [12, 11, 13, 5, 6]; +console.log(insertionSort(arr)); // Output: [5, 6, 11, 12, 13] +``` + +## Complexity Analysis + +The time complexity of the Insertion Sort algorithm is ***O(n2)*** in the worst-case scenario when the array is sorted in reverse order. The best-case time complexity is ***O(n)*** when the array is already sorted. + +The space complexity of the Insertion Sort algorithm is ***O(1)*** since it requires only a constant amount of additional memory space. + +## References + +- [Wikipedia - Insertion Sort](https://en.wikipedia.org/wiki/Insertion_sort) +- [GeeksforGeeks - Insertion Sort](https://www.geeksforgeeks.org/insertion-sort/) +- [Programiz - Insertion Sort](https://www.programiz.com/dsa/insertion-sort) +- [Khan Academy - Insertion Sort](https://www.khanacademy.org/computing/computer-science/algorithms/insertion-sort/a/insertion-sort) +- [TutorialsPoint - Insertion Sort](https://www.tutorialspoint.com/data_structures_algorithms/insertion_sort_algorithm.htm) +- [StudyTonight - Insertion Sort](https://www.studytonight.com/data-structures/insertion-sorting) +- [Insertion Sort Visualization](https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html) \ No newline at end of file diff --git a/docs/arrays/arrays-selectionsort.md b/docs/arrays/arrays-selectionsort.md new file mode 100644 index 000000000..706040c34 --- /dev/null +++ b/docs/arrays/arrays-selectionsort.md @@ -0,0 +1,152 @@ +--- +id: arrays-selectionsort-in-dsa +title: Arrays - Selection Sort in DSA +sidebar_label: Selection Sort +sidebar_position: 3 +description: "Selection Sort is an in-place comparison sorting algorithm that divides the input list into two parts: the sublist of items already sorted and the sublist of items remaining to be sorted. It repeatedly finds the minimum element from the unsorted part and puts it at the beginning of the unsorted part. The algorithm maintains two subarrays in a given array. The subarray which is already sorted and the remaining subarray which is unsorted. In every iteration of selection sort, the minimum element from the unsorted subarray is picked and moved to the sorted subarray." +tags: [dsa, arrays, sorting, selection-sort, algorithm of selection-sort, pseudocode of selection-sort, complexity of selection-sort, example of selection-sort, live example of selection-sort, explanation of selection-sort, quiz of selection-sort, conclusion of selection-sort] +--- + +**Selection Sort** is an in-place comparison sorting algorithm that divides the input list into two parts: the sublist of items already sorted and the sublist of items remaining to be sorted. It repeatedly finds the minimum element from the unsorted part and puts it at the beginning of the unsorted part. The algorithm maintains two subarrays in a given array. The subarray which is already sorted and the remaining subarray which is unsorted. In every iteration of selection sort, the minimum element from the unsorted subarray is picked and moved to the sorted subarray. + + + +## Algorithm + +1. The selection sort algorithm divides the input list into two parts: the sublist of items already sorted and the sublist of items remaining to be sorted. +2. The algorithm repeatedly finds the minimum element from the unsorted part and puts it at the beginning of the unsorted part. +3. The algorithm maintains two subarrays in a given array. The subarray which is already sorted and the remaining subarray which is unsorted. +4. In every iteration of selection sort, the minimum element from the unsorted subarray is picked and moved to the sorted subarray. +5. The array is sorted. +6. Exit. +7. The time complexity of the selection sort is O(n^2). +8. The space complexity of the selection sort is O(1). + +## Pseudocode + +```plaintext title="Selection Sort" +procedure selectionSort( A : list of sortable items ) + n = length(A) + for i = 0 to n-1 inclusive do + min = i + for j = i+1 to n inclusive do + if A[j] < A[min] then + min = j + end if + end for + swap(A[i], A[min]) + end for +end procedure +``` + +## Diagram + + +## Complexity + +The time complexity of the selection sort is O(n^2). The space complexity of the selection sort is O(1). + +## Example + +```js title="Selection Sort" +function selectionSort(arr) { + let n = arr.length; + for (let i = 0; i < n - 1; i++) { + let min = i; + for (let j = i + 1; j < n; j++) { + if (arr[j] < arr[min]) { + min = j; + } + } + let temp = arr[i]; + arr[i] = arr[min]; + arr[min] = temp; + } + return arr; +} + +const arr = [64, 25, 12, 22, 11]; + +console.log(selectionSort(arr)); // [11, 12, 22, 25, 64] +``` + +## Practice Problems + +- [Leetcode - Sort an Array](https://leetcode.com/problems/sort-an-array/) +- [HackerRank - The Full Counting Sort](https://www.hackerrank.com/challenges/countingsort4/problem) +- [Codeforces - Sort the Array](https://codeforces.com/problemset/problem/451/B) +- [CodeChef - Turbo Sort](https://www.codechef.com/problems/TSORT) + +## Quiz + +1. What is the time complexity of the selection sort? + - A) O(n) + - B) O(n^2) + - C) O(n log n) + - D) O(1) + - Correct Answer: B + +2. What is the space complexity of the selection sort? + - A) O(n) + - B) O(n^2) + - C) O(n log n) + - D) O(1) + - Correct Answer: D +- Explanation: The space complexity of the selection sort is O(1). + +3. What is the best-case time complexity of the selection sort? + - A) O(n) + - B) O(n^2) + - C) O(n log n) + - D) O(1) + - Correct Answer: B + +4. What is the worst-case time complexity of the selection sort? + - A) O(n) + - B) O(n^2) + - C) O(n log n) + - D) O(1) + - Correct Answer: B + +5. Is the selection sort stable? + - A) Yes + - B) No + - Correct Answer: A + + +:::info Try it yourself + +```js live +function selectionSort() { + let arr = [64, 25, 12, 22, 11]; + let n = arr.length; + for (let i = 0; i < n - 1; i++) { + let min = i; + for (let j = i + 1; j < n; j++) { + if (arr[j] < arr[min]) { + min = j; + } + } + let temp = arr[i]; + arr[i] = arr[min]; + arr[min] = temp; + } + return ( +
+

Selection Sort

+

Array: [64, 25, 12, 22, 11]

+

+ Sorted Array: [{arr.join(", ")}] +

+
+ ) +} +``` + +In the above example, we have an array of numbers `[64, 25, 12, 22, 11]`. We are using the selection sort algorithm to sort the array in ascending order. The selection sort algorithm divides the input list into two parts: the sublist of items already sorted and the sublist of items remaining to be sorted. It repeatedly finds the minimum element from the unsorted part and puts it at the beginning of the unsorted part. The algorithm maintains two subarrays in a given array. The subarray which is already sorted and the remaining subarray which is unsorted. In every iteration of selection sort, the minimum element from the unsorted subarray is picked and moved to the sorted subarray. The sorted array is `[11, 12, 22, 25, 64]`. The time complexity of the selection sort is O(n^2) and the space complexity is O(1). + +::: + +## Conclusion + +In this article, we learned about the selection sort algorithm. Selection sort is an in-place comparison sorting algorithm that divides the input list into two parts: the sublist of items already sorted and the sublist of items remaining to be sorted. It repeatedly finds the minimum element from the unsorted part and puts it at the beginning of the unsorted part. The algorithm maintains two subarrays in a given array. The subarray which is already sorted and the remaining subarray which is unsorted. In every iteration of selection sort, the minimum element from the unsorted subarray is picked and moved to the sorted subarray. The time complexity of the selection sort is O(n^2) and the space complexity is O(1). Selection sort is a stable sorting algorithm. \ No newline at end of file diff --git a/docs/arrays/bucket-sort.md b/docs/arrays/bucket-sort.md new file mode 100644 index 000000000..8067ac6d2 --- /dev/null +++ b/docs/arrays/bucket-sort.md @@ -0,0 +1,204 @@ +--- +id: bucket-sort +title: Bucket sort +sidebar_label: Bucket sort +tags: + - DSA + - Python + - C++ + - Java + - Sorting + +description: "Thsi page containes Bucket Sort, with codes in python, java and c++ " +--- + +### Introduction to Bucket Sort + +Bucket sort is a comparison sorting algorithm that distributes elements into a number of "buckets." Each bucket is then sorted individually, either using another sorting algorithm or recursively applying the bucket sort. Finally, the sorted buckets are combined to form the final sorted array. Bucket sort is particularly useful for uniformly distributed data. + +### Steps of Bucket Sort + +1. **Create Buckets**: Initialize an empty array of buckets. +2. **Distribute Elements**: Distribute the elements of the input array into the appropriate buckets. +3. **Sort Buckets**: Sort each bucket individually. +4. **Concatenate Buckets**: Concatenate all sorted buckets to form the final sorted array. + +### Pseudocode + +```text +function bucketSort(array, bucketSize): + if length(array) == 0: + return array + + // Determine minimum and maximum values + minValue = min(array) + maxValue = max(array) + + // Initialize buckets + bucketCount = floor((maxValue - minValue) / bucketSize) + 1 + buckets = array of empty lists of size bucketCount + + // Distribute input array values into buckets + for i from 0 to length(array) - 1: + bucketIndex = floor((array[i] - minValue) / bucketSize) + append array[i] to buckets[bucketIndex] + + // Sort each bucket and concatenate them + sortedArray = [] + for i from 0 to bucketCount - 1: + sort(buckets[i]) // You can use any sorting algorithm + append buckets[i] to sortedArray + + return sortedArray +``` + +### Implementation in Python, C++, and Java + +#### Python Implementation + +```python +def bucket_sort(numbers, size=5): + if len(numbers) == 0: + return numbers + + # Determine minimum and maximum values + min_value = min(numbers) + max_value = max(numbers) + + # Initialize buckets + bucket_count = (max_value - min_value) // size + 1 + buckets = [[] for _ in range(bucket_count)] + + # Distribute input array values into buckets + for number in numbers: + bucket_index = (number - min_value) // size + buckets[bucket_index].append(number) + + # Sort each bucket and concatenate them + sorted_numbers = [] + for bucket in buckets: + sorted_numbers.extend(sorted(bucket)) + + return sorted_numbers + +# Example usage +data = [42, 32, 33, 52, 37, 47, 51] +sorted_data = bucket_sort(data) +print(sorted_data) # Output: [32, 33, 37, 42, 47, 51, 52] +``` + +#### C++ Implementation + +```cpp +#include +#include +#include +using namespace std; + +void bucketSort(vector& nums, int bucketSize) { + if (nums.empty()) + return; + + // Determine minimum and maximum values + int minValue = *min_element(nums.begin(), nums.end()); + int maxValue = *max_element(nums.begin(), nums.end()); + + // Initialize buckets + int numBuckets = (maxValue - minValue) / bucketSize + 1; + vector> buckets(numBuckets); + + // Distribute input array values into buckets + for (int num : nums) { + int bucketIndex = (num - minValue) / bucketSize; + buckets[bucketIndex].push_back(num); + } + + // Sort each bucket and concatenate them + nums.clear(); + for (auto& bucket : buckets) { + sort(bucket.begin(), bucket.end()); + nums.insert(nums.end(), bucket.begin(), bucket.end()); + } +} + +// Example usage +int main() { + vector data = {42, 32, 33, 52, 37, 47, 51}; + bucketSort(data, 5); + for (int num : data) { + cout << num << " "; + } + // Output: 32 33 37 42 47 51 52 + return 0; +} +``` + +#### Java Implementation + +```java +import java.util.ArrayList; +import java.util.Collections; + +public class BucketSort { + public static void bucketSort(int[] array, int bucketSize) { + if (array.length == 0) + return; + + // Determine minimum and maximum values + int minValue = array[0]; + int maxValue = array[0]; + for (int num : array) { + if (num < minValue) + minValue = num; + else if (num > maxValue) + maxValue = num; + } + + // Initialize buckets + int bucketCount = (maxValue - minValue) / bucketSize + 1; + ArrayList> buckets = new ArrayList<>(bucketCount); + for (int i = 0; i < bucketCount; i++) { + buckets.add(new ArrayList()); + } + + // Distribute input array values into buckets + for (int num : array) { + int bucketIndex = (num - minValue) / bucketSize; + buckets.get(bucketIndex).add(num); + } + + // Sort each bucket and concatenate them + int currentIndex = 0; + for (ArrayList bucket : buckets) { + Collections.sort(bucket); + for (int num : bucket) { + array[currentIndex++] = num; + } + } + } + + // Example usage + public static void main(String[] args) { + int[] data = {42, 32, 33, 52, 37, 47, 51}; + bucketSort(data, 5); + for (int num : data) { + System.out.print(num + " "); + } + // Output: 32 33 37 42 47 51 52 + } +} +``` + +### Complexity + +- **Time Complexity**: + + - Best Case: $O(n + k)$, where $n$ is the number of elements and $k$ is the number of buckets. + - Average Case: $O(n + k + n \log(\frac{n}{k}))$ + - Worst Case: $O(n^2)$, when all elements are placed in one bucket and a slow sorting algorithm (like bubble sort) is used within buckets. + +- **Space Complexity**: $O(n + k)$, for the input array and the buckets. + +### Conclusion + +Bucket sort is efficient for sorting uniformly distributed data and can achieve linear time complexity in the best case. However, it may degrade to quadratic time complexity in the worst case if elements are not uniformly distributed. It's essential to choose an appropriate bucket size and secondary sorting algorithm for optimal performance. By understanding its structure and implementation, you can effectively use bucket sort for various sorting tasks. diff --git a/docs/arrays/radix-sort.md b/docs/arrays/radix-sort.md new file mode 100644 index 000000000..50efd5fff --- /dev/null +++ b/docs/arrays/radix-sort.md @@ -0,0 +1,261 @@ +--- +id: radix-sort +title: Radix sort +sidebar_label: Radix Sort +tags: + - DSA + - Python + - C++ + - Java + - Sorting + +description: "This page explains Radix sort, with code implementations and resources for further learning." +--- + +### Introduction to Radix Sort + +Radix sort is a non-comparative integer sorting algorithm. It sorts integers by processing individual digits. Starting from the least significant digit (LSD) to the most significant digit (MSD), it uses a stable subroutine sort (like counting sort) to handle the individual digits. The algorithm is efficient for sorting numbers with a fixed number of digits and works well when the range of digits is not excessively large. + +### Steps of Radix Sort (Pseudocode Steps) + +1. **Find the maximum number** to determine the number of digits. +2. **Initialize**: Set up a loop to process each digit from the least significant to the most significant. +3. **Digit by digit sorting**: + - Use a stable sort (e.g., counting sort) to sort based on the current digit. +4. **Repeat** until all digits are processed. + +### Example + +To perform radix sort on the array [170, 45, 75, 90, 802, 24, 2, 66], we follow these steps: +![Example from GFG](https://media.geeksforgeeks.org/wp-content/uploads/20230609164537/Radix-Sort.png) + +Step 1: Find the largest element in the array, which is 802. It has three digits, so we will iterate three times, once for each significant place. + +Step 2: Sort the elements based on the unit place digits (X=0). We use a stable sorting technique, such as counting sort, to sort the digits at each significant place. + +Sorting based on the unit place: + +Perform counting sort on the array based on the unit place digits. +The sorted array based on the unit place is [170, 90, 802, 2, 24, 45, 75, 66]. + +![Example from GFG](https://media.geeksforgeeks.org/wp-content/uploads/20230609164536/Radix-Sort--1.png) + +Step 3: Sort the elements based on the tens place digits. + +Sorting based on the tens place: + +Perform counting sort on the array based on the tens place digits. +The sorted array based on the tens place is [802, 2, 24, 45, 66, 170, 75, 90]. + +![Example from GFG](https://media.geeksforgeeks.org/wp-content/uploads/20230609164535/Radix-Sort--2.png) + +Step 4: Sort the elements based on the hundreds place digits. + +Sorting based on the hundreds place: + +Perform counting sort on the array based on the hundreds place digits. +The sorted array based on the hundreds place is [2, 24, 45, 66, 75, 90, 170, 802]. + +![Example from GFG](https://media.geeksforgeeks.org/wp-content/uploads/20230609164535/Radix-Sort--3.png) + +Step 5: The array is now sorted in ascending order. + +The final sorted array using radix sort is [2, 24, 45, 66, 75, 90, 170, 802]. + +![Example from GFG](https://media.geeksforgeeks.org/wp-content/uploads/20230609164534/Radix-Sort--4.png) + +### Pseudocode for Radix Sort + +```text +function radixSort(array): + maxNumber = findMax(array) + numberOfDigits = countDigits(maxNumber) + + for digit in 1 to numberOfDigits: + countingSortByDigit(array, digit) + +function findMax(array): + maxNumber = array[0] + for number in array: + if number > maxNumber: + maxNumber = number + return maxNumber + +function countDigits(number): + count = 0 + while number != 0: + number = number // 10 + count += 1 + return count + +function countingSortByDigit(array, digit): + count = array of size 10 initialized to 0 + output = array of same size as input array + + # Count occurrences of each digit + for number in array: + digitValue = (number // 10^(digit - 1)) % 10 + count[digitValue] += 1 + + # Change count[i] so that count[i] contains the position of this digit in output + for i from 1 to 9: + count[i] += count[i - 1] + + # Build the output array + for i from length(array) - 1 to 0: + digitValue = (array[i] // 10^(digit - 1)) % 10 + output[count[digitValue] - 1] = array[i] + count[digitValue] -= 1 + + # Copy the output array to the input array + for i from 0 to length(array) - 1: + array[i] = output[i] +``` + +### Radix Sort Implementation in Python, C++, and Java + +#### Python + +```python +def radix_sort(array): + def counting_sort_by_digit(array, digit): + count = [0] * 10 + output = [0] * len(array) + + for number in array: + digit_value = (number // 10**(digit - 1)) % 10 + count[digit_value] += 1 + + for i in range(1, 10): + count[i] += count[i - 1] + + for i in range(len(array) - 1, -1, -1): + digit_value = (array[i] // 10**(digit - 1)) % 10 + output[count[digit_value] - 1] = array[i] + count[digit_value] -= 1 + + for i in range(len(array)): + array[i] = output[i] + + max_number = max(array) + number_of_digits = len(str(max_number)) + + for digit in range(1, number_of_digits + 1): + counting_sort_by_digit(array, digit) + +# Example usage +arr = [170, 45, 75, 90, 802, 24, 2, 66] +radix_sort(arr) +print(arr) +``` + +#### C++ + +```cpp +#include +#include +#include + +void countingSortByDigit(std::vector& array, int digit) { + int size = array.size(); + std::vector output(size); + int count[10] = {0}; + + for (int i = 0; i < size; i++) { + int digitValue = (array[i] / digit) % 10; + count[digitValue]++; + } + + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + for (int i = size - 1; i >= 0; i--) { + int digitValue = (array[i] / digit) % 10; + output[count[digitValue] - 1] = array[i]; + count[digitValue]--; + } + + for (int i = 0; i < size; i++) { + array[i] = output[i]; + } +} + +void radixSort(std::vector& array) { + int maxNumber = *max_element(array.begin(), array.end()); + for (int digit = 1; maxNumber / digit > 0; digit *= 10) { + countingSortByDigit(array, digit); + } +} + +// Example usage +int main() { + std::vector arr = {170, 45, 75, 90, 802, 24, 2, 66}; + radixSort(arr); + for (int num : arr) { + std::cout << num << " "; + } + return 0; +} +``` + +#### Java + +```java +import java.util.Arrays; + +public class RadixSort { + + public static void radixSort(int[] array) { + int maxNumber = Arrays.stream(array).max().getAsInt(); + for (int digit = 1; maxNumber / digit > 0; digit *= 10) { + countingSortByDigit(array, digit); + } + } + + private static void countingSortByDigit(int[] array, int digit) { + int size = array.length; + int[] output = new int[size]; + int[] count = new int[10]; + + for (int i = 0; i < size; i++) { + int digitValue = (array[i] / digit) % 10; + count[digitValue]++; + } + + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + for (int i = size - 1; i >= 0; i--) { + int digitValue = (array[i] / digit) % 10; + output[count[digitValue] - 1] = array[i]; + count[digitValue]--; + } + + for (int i = 0; i < size; i++) { + array[i] = output[i]; + } + } + + public static void main(String[] args) { + int[] arr = {170, 45, 75, 90, 802, 24, 2, 66}; + radixSort(arr); + System.out.println(Arrays.toString(arr)); + } +} +``` + +### Complexity of Radix Sort + +- **Time Complexity**: $O(d⋅(n+k))$ + - $d$ : Number of digits in the largest number. + - $n$ : Number of elements in the array. + - $k$ : Range of the digits for decimal system, $k = 10.$ +- **Space Complexity**: $O(n + k)$ + - $n$ : Number of elements in the array. + - $k$ : Range of the digits for decimal system, $k = 10.$ + +### Conclusion + +Radix sort is a powerful sorting algorithm for integers or fixed-length strings. Its efficiency and non-comparative nature make it a valuable tool for specific use cases, especially where the number of digits or characters is limited. Understanding and implementing radix sort can significantly enhance the performance of sorting operations in such scenarios. \ No newline at end of file diff --git a/docs/basic-concepts/_category_.json b/docs/basic-concepts/_category_.json new file mode 100644 index 000000000..b810bad9b --- /dev/null +++ b/docs/basic-concepts/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Basic Concepts", + "position": 5, + "link": { + "type": "generated-index", + "description": "Basic Concepts of Data Structures and Algorithms." + } + } \ No newline at end of file diff --git a/docs/basic-concepts/image-1.png b/docs/basic-concepts/image-1.png new file mode 100644 index 000000000..cd127aab3 Binary files /dev/null and b/docs/basic-concepts/image-1.png differ diff --git a/docs/basic-concepts/image.png b/docs/basic-concepts/image.png new file mode 100644 index 000000000..8a263334f Binary files /dev/null and b/docs/basic-concepts/image.png differ diff --git a/docs/basic-concepts/space-complexity.md b/docs/basic-concepts/space-complexity.md new file mode 100644 index 000000000..d78bae320 --- /dev/null +++ b/docs/basic-concepts/space-complexity.md @@ -0,0 +1,212 @@ +--- +id: space-complexity +title: Space Complexity +sidebar_label: Space Complexity +sidebar_position: 2 +description: "Space complexity is a measure of the amount of working storage an algorithm needs. It is a measure of the amount of memory space an algorithm needs to solve a problem as a function of the size of the input to the problem. It is the amount of memory space required by the algorithm to execute in its life cycle." +tags: [Space Complexity, Big O Notation, Memory, Algorithm, Complexity Analysis, Data Structure, DSA, JavaScript, Java, Python, C, C++, Space Complexity Example, Space Complexity Calculation, Space Complexity Analysis, Space Complexity Explanation, Space Complexity Conclusion, Space Complexity Importance, Space Complexity Formula, Space Complexity Constant Space, Space Complexity Auxiliary Space, Space Complexity Example, Space Complexity Program, Space Complexity Code] +--- + +Space complexity is a measure of the amount of working storage an algorithm needs. It is a measure of the amount of memory space an algorithm needs to solve a problem as a function of the size of the input to the problem. It is the amount of memory space required by the algorithm to execute in its life cycle. + +## Why is Space Complexity important? + +Space complexity is important because the memory that is allocated to the program is limited. If the program uses more memory than the available memory, the program will crash. Therefore, it is important to know the space complexity of the algorithm. + +## How to calculate Space Complexity? + +Space complexity is calculated by counting the amount of memory space used by the algorithm. It is calculated by counting the amount of memory space used by the algorithm as a function of the size of the input to the problem. + +## Example + +```js title="Space Complexity" +function sumOfN(n) { + let sum = 0; + for (let i = 1; i <= n; i++) { + sum += i; + } + return sum; +} +``` + +In the above example, the space complexity of the algorithm is O(1) because the algorithm uses a constant amount of memory space. + +## Example of Space Complexity + +1. Write a program to fine maximum and minimum element in an array. + + + + + ```js {6,9,12} + function findMaxMin(arr) { + let max = arr[0]; + let min = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + if (arr[i] < min) { + min = arr[i]; + } + } + return { max, min }; + } + + const arr = [2, 5, 1, 20, 10]; + console.log(findMaxMin(arr)); // { max: 20, min: 1 } + ``` + + + + + ```java + public class Main { + public static void main(String[] args) { + int[] arr = {2, 5, 1, 20, 10}; + System.out.println(findMaxMin(arr)); // { max: 20, min: 1 } + } + + public static Map findMaxMin(int[] arr) { + int max = arr[0]; + int min = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (arr[i] > max) { + max = arr[i]; + } + if (arr[i] < min) { + min = arr[i]; + } + } + Map result = new HashMap<>(); + result.put("max", max); + result.put("min", min); + return result; + } + } + ``` + + + + + ```python + def find_max_min(arr): + max = arr[0] + min = arr[0] + for i in range(1, len(arr)): + if arr[i] > max: + max = arr[i] + if arr[i] < min: + min = arr[i] + return {"max": max, "min": min} + + arr = [2, 5, 1, 20, 10] + print(find_max_min(arr)) # { max: 20, min: 1 } + ``` + + + + + ```c + #include + + struct MaxMin { + int max; + int min; + }; + + struct MaxMin findMaxMin(int arr[], int n) { + struct MaxMin result; + result.max = arr[0]; + result.min = arr[0]; + for (int i = 1; i < n; i++) { + if (arr[i] > result.max) { + result.max = arr[i]; + } + if (arr[i] < result.min) { + result.min = arr[i]; + } + } + return result; + } + + int main() { + int arr[] = {2, 5, 1, 20, 10}; + struct MaxMin result = findMaxMin(arr, 5); + printf("{ max: %d, min: %d }\n", result.max, result.min); // { max: 20, min: 1 } + return 0; + } + ``` + + + + + ```cpp + #include + #include + #include + + std::map findMaxMin(std::vector arr) { + int max = arr[0]; + int min = arr[0]; + for (int i = 1; i < arr.size(); i++) { + if (arr[i] > max) { + max = arr[i]; + } + if (arr[i] < min) { + min = arr[i]; + } + } + std::map result; + result["max"] = max; + result["min"] = min; + return result; + } + + int main() { + std::vector arr = {2, 5, 1, 20, 10}; + std::map result = findMaxMin(arr); + std::cout << "{ max: " << result["max"] << ", min: " << result["min"] << " }\n"; // { max: 20, min: 1 } + return 0; + } + ``` + + + + +In the above example, the space complexity of the algorithm is O(1) because the algorithm uses a constant amount of memory space. + +**Explanation:** In the above example, we are finding the maximum and minimum element in an array. We are using two variables `max` and `min` to store the maximum and minimum element in the array. We are using a constant amount of memory space to store the maximum and minimum element in the array. Therefore, the space complexity of the algorithm is O(1). + +:::info Complexity Analysis +**Farmula to calculate Space Complexity** + +Space Complexity = Constant Space + Auxiliary Space + +**Constant Space:** The amount of space used by the algorithm that is not dependent on the size of the input to the problem. It is a constant amount of memory space used by the algorithm. + +**Auxiliary Space:** The amount of space used by the algorithm that is dependent on the size of the input to the problem. It is a variable amount of memory space used by the algorithm. + +```plaintext title="Space Complexity" +Space Complexity = O(1) + O(n) = O(n) +``` + +**For Example:** + +```js title="Space Complexity" +function sumOfN(n) { + let sum = 0; // Constant Space (O(1)) + for (let i = 1; i <= n; i++) { + sum += i; // Auxiliary Space (O(n)) + } + return sum; +} +``` + +In the above example, the space complexity of the algorithm is `O(1) + O(n) = O(n)`. + +::: + +## Conclusion + +Space complexity is a measure of the amount of working storage an algorithm needs. It is a measure of the amount of memory space an algorithm needs to solve a problem as a function of the size of the input to the problem. It is the amount of memory space required by the algorithm to execute in its life cycle. \ No newline at end of file diff --git a/docs/basic-concepts/time-complexity.md b/docs/basic-concepts/time-complexity.md new file mode 100644 index 000000000..104eb97c4 --- /dev/null +++ b/docs/basic-concepts/time-complexity.md @@ -0,0 +1,306 @@ +--- +id: time-complexity +title: Time Complexity +sidebar_label: Time Complexity +sidebar_position: 1 +description: "Time Complexity is a measure of the amount of time an algorithm takes to solve a problem as a function of the size of the input to the problem. It is commonly estimated by counting the number of elementary operations performed by the algorithm, where an elementary operation takes a fixed amount of time to perform." +tags: [time complexity, big o notation, algorithm, complexity analysis, data structure, dsa, javascript, java, python, c, c++, time complexity example, time complexity calculation, time complexity analysis, time complexity explanation, time complexity conclusion, time complexity importance, time complexity formula, time complexity constant time, time complexity linear time, time complexity logarithmic time, time complexity quadratic time, time complexity exponential time, time complexity factorial time, time complexity polynomial time, time complexity sublinear time, time complexity linearithmic time, time complexity quasilinear time, time complexity superpolynomial time, time complexity subexponential time, time complexity log factorial time, time complexity exponential factorial time, time complexity exponential exponential time, time complexity exponential factorial exponential time, time complexity exponential exponential factorial time] +--- + +Time Complexity is a measure of the amount of time an algorithm takes to solve a problem as a function of the size of the input to the problem. It is commonly estimated by counting the number of elementary operations performed by the algorithm, where an elementary operation takes a fixed amount of time to perform. + +## Why is Time Complexity Important? + +Time Complexity is important because it helps us understand the efficiency of an algorithm. It allows us to compare different algorithms and choose the most efficient one for a given problem. It also helps us analyze the performance of an algorithm as the size of the input grows. + +## Common Notations for Time Complexity + +The most common notations used to represent the time complexity of an algorithm are: + +- **Big O Notation (O):** It represents the upper bound of the time complexity of an algorithm. It gives the worst-case time complexity of an algorithm. +- **Omega Notation (Ω):** It represents the lower bound of the time complexity of an algorithm. It gives the best-case time complexity of an algorithm. +- **Theta Notation (Θ):** It represents the average-case time complexity of an algorithm. It gives the tight bound of the time complexity of an algorithm. + +### Big O Notation (O) + +Big O Notation is used to represent the upper bound of the time complexity of an algorithm. It gives the worst-case time complexity of an algorithm. It is commonly used to analyze the performance of an algorithm as the size of the input grows. + +![Big O Notation](image.png) + +The Big O Notation is written as `O(f(n))`, where `f(n)` is a function that represents the time complexity of the algorithm. It is read as "O of f of n" or "order of f of n". + +### Omega Notation (Ω) + +Omega Notation is used to represent the lower bound of the time complexity of an algorithm. It gives the best-case time complexity of an algorithm. It is commonly used to analyze the performance of an algorithm as the size of the input grows. + +![Omega Notation](image-1.png) + +The Omega Notation is written as `Ω(f(n))`, where `f(n)` is a function that represents the time complexity of the algorithm. It is read as "Omega of f of n". + +### Theta Notation (Θ) + +Theta Notation is used to represent the average-case time complexity of an algorithm. It gives the tight bound of the time complexity of an algorithm. It is commonly used to analyze the performance of an algorithm as the size of the input grows. + +The Theta Notation is written as `Θ(f(n))`, where `f(n)` is a function that represents the time complexity of the algorithm. It is read as "Theta of f of n". + + +## Examples of Time Complexity + +Here are some examples of time complexity for different algorithms: + +- **Constant Time (O(1)):** An algorithm that takes the same amount of time to run, regardless of the size of the input. +- **Linear Time (O(n)):** An algorithm that takes time proportional to the size of the input. +- **Logarithmic Time (O(log n)):** An algorithm that takes time proportional to the logarithm of the size of the input. +- **Quadratic Time (O(n^2)):** An algorithm that takes time proportional to the square of the size of the input. +- **Exponential Time (O(2^n)):** An algorithm that takes time proportional to an exponential function of the size of the input. +- **Factorial Time (O(n!)):** An algorithm that takes time proportional to the factorial of the size of the input. +- **Polynomial Time (O(n^k)):** An algorithm that takes time proportional to a polynomial function of the size of the input. +- **Sublinear Time (O(log log n)):** An algorithm that takes time proportional to the logarithm of the logarithm of the size of the input. +- **Linearithmic Time (O(n log n)):** An algorithm that takes time proportional to the product of the size of the input and the logarithm of the size of the input. +- **Quasilinear Time (O(n log^k n)):** An algorithm that takes time proportional to the product of the size of the input and the logarithm of the size of the input raised to the power of k. +- **Superpolynomial Time (O(n^k)):** An algorithm that takes time proportional to a function that grows faster than any polynomial function of the size of the input. +- **Subexponential Time (O(2^poly(n))):** An algorithm that takes time proportional to a function that grows slower than any exponential function of the size of the input. +- **Log Factorial Time (O(log n!)):** An algorithm that takes time proportional to the logarithm of the factorial of the size of the input. +- **Exponential Factorial Time (O(2^n!)):** An algorithm that takes time proportional to an exponential function of the factorial of the size of the input. +- **Exponential Exponential Time (O(2^2^n)):** An algorithm that takes time proportional to an exponential function of an exponential function of the size of the input. +- **Exponential Factorial Exponential Time (O(2^n!^2^n)):** An algorithm that takes time proportional to an exponential function of the factorial of an exponential function of the size of the input. +- **Exponential Exponential Factorial Time (O(2^2^n!)):** An algorithm that takes time proportional to an exponential function of an exponential function of the factorial of the size of the input. + +## Analyzing Time Complexity + +To analyze the time complexity of an algorithm, we can follow these steps: + +1. **Count the Operations:** Count the number of elementary operations performed by the algorithm. +2. **Identify the Dominant Term:** Identify the term that grows the fastest as the size of the input grows. +3. **Express the Time Complexity:** Express the time complexity using the Big O notation. +4. **Compare with Other Algorithms:** Compare the time complexity with other algorithms to choose the most efficient one. +5. **Analyze the Best, Worst, and Average Cases:** Analyze the best-case, worst-case, and average-case time complexity of the algorithm. +6. **Optimize the Algorithm:** Optimize the algorithm to improve its time complexity if possible. +7. **Test the Algorithm:** Test the algorithm with different input sizes to verify its time complexity. +8. **Benchmark the Algorithm:** Benchmark the algorithm to measure its actual performance. +9. **Profile the Algorithm:** Profile the algorithm to identify performance bottlenecks and optimize them. +10. **Analyze the Real-World Performance:** Analyze the real-world performance of the algorithm in different environments and scenarios. +11. **Contribute:** If you have any tips to share, feel free to contribute to this section. + +## For Example + +1. Calculating sum of array elements using loop. + + + + + ```js + function sum(arr) { + let result = 0; + for (let i = 0; i < arr.length; i++) { + result += arr[i]; + } + return result; + } + const arr = [1, 2, 3, 4, 5]; + console.log(sum(arr)); // Output: 15 + ``` + + + ```py + def sum(arr): + result = 0 + for i in arr: + result += i + return result + arr = [1, 2, 3, 4, 5] + print(sum(arr)) # Output: 15 + ``` + + + ```java + class Sum { + public static void main(String args[]) { + int arr[] = {1, 2, 3, 4, 5}; + int result = 0; + for (int i = 0; i < arr.length; i++) { + result += arr[i]; + } + System.out.println(result); // Output: 15 + } + } + ``` + + + ```c + #include + int main() { + int arr[] = {1, 2, 3, 4, 5}; + int result = 0; + for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) { + result += arr[i]; + } + printf("%d\n", result); // Output: 15 + return 0; + } + ``` + + + ```go + package main + import "fmt" + func main() { + arr := []int{1, 2, 3, 4, 5} + result := 0 + for i := 0; i < len(arr); i++ { + result += arr[i] + } + fmt.Println(result) // Output: 15 + } + ``` + + + ```cpp + #include + using namespace std; + int main() { + int arr[] = {1, 2, 3, 4, 5}; + int result = 0; + for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) { + result += arr[i]; + } + cout << result << endl; // Output: 15 + return 0; + } + ``` + + + ```swift + import Foundation + let arr = [1, 2, 3, 4, 5] + var result = 0 + for i in arr { + result += i + } + print(result) // Output: 15 + ``` + + + ```kotlin + fun main() { + val arr = intArrayOf(1, 2, 3, 4, 5) + var result = 0 + for (i in arr) { + result += i + } + println(result) // Output: 15 + } + ``` + + + ```rs + fn main() { + let arr = [1, 2, 3, 4, 5]; + let mut result = 0; + for i in arr.iter() { + result += i; + } + println!("{}", result); // Output: 15 + } + ``` + + + ```php + + ``` + + + ```cs + using System; + class Sum { + static void Main() { + int[] arr = {1, 2, 3, 4, 5}; + int result = 0; + foreach (int i in arr) { + result += i; + } + Console.WriteLine(result); // Output: 15 + } + } + ``` + + + ```ts + let arr: number[] = [1, 2, 3, 4, 5]; + let result: number = 0; + for (let i of arr) { + result += i; + } + console.log(result); // Output: 15 + ``` + + + ```scala + object Sum { + def main(args: Array[String]): Unit = { + val arr = Array(1, 2, 3, 4, 5) + var result = 0 + for (i <- arr) { + result += i + } + println(result) // Output: 15 + } + } + ``` + + + ```haskell + main = do + let arr = [1, 2, 3, 4, 5] + let result = sum arr + print result -- Output: 15 + ``` + + + ```r + arr <- c(1, 2, 3, 4, 5) + result <- sum(arr) + print(result) # Output: 15 + ``` + + + ```perl + my @arr = (1, 2, 3, 4, 5); + my $result = 0; + foreach my $i (@arr) { + $result += $i; + } + print $result; # Output: 15 + ``` + + + ```lua + arr = {1, 2, 3, 4, 5} + result = 0 + for i = 1, #arr do + result = result + arr[i] + end + print(result) -- Output: 15 + ``` + + + +Each of the above code snippets has a time complexity of `O(n)` because the number of iterations in the loop is directly proportional to the size of the input array. + +**Exlpanation:** The time complexity of the given code is `O(n)` because the loop iterates through the array elements one by one, and the number of iterations is directly proportional to the size of the input array. Therefore, the time complexity is linear, i.e., `O(n)`. + + +:::info +**Note:** The time complexity of an algorithm can be different for different programming languages, but the underlying logic and analysis remain the same. +::: + +## Conclusion + +Time Complexity is an important concept in computer science and programming. It helps us understand the efficiency of algorithms and make informed decisions about choosing the most efficient algorithm for a given problem. By analyzing the time complexity of algorithms, we can optimize them for better performance and improve the overall efficiency of our programs. \ No newline at end of file diff --git a/docs/binary_search/Iterative_binary_search.md b/docs/binary_search/Iterative_binary_search.md new file mode 100644 index 000000000..7fab7b613 --- /dev/null +++ b/docs/binary_search/Iterative_binary_search.md @@ -0,0 +1,134 @@ +--- +id: iterative-binary-search-DSA +title: Iterative Binary Search +sidebar_label: Iterative Binary Search +sidebar_position: 7 +description: "In this blog post, we'll explore the iterative binary search algorithm, a fundamental technique in computer science for efficiently finding an element in a sorted array. You'll learn what iterative binary search is, how it works, and its time complexity. We'll also cover practical applications and common problems you can solve using this algorithm. By the end, you'll have a thorough understanding of iterative binary search and how to implement it in your programming projects." +tags: [dsa, algorithms, binary search, iterative] +--- + +Iterative Binary Search is powerful algorithm that is essential for efficiently finding elements in sorted arrays, making it a staple in the toolkit of any adept programmer. Whether you're optimizing search operations or solving complex algorithmic challenges, understanding iterative binary search is crucial. Let's delve into its mechanics, applications, and implementation. + +## What is Iterative Binary Search? + +Iterative binary search is a highly efficient algorithm used to find an element in a sorted array. It works by repeatedly dividing the search interval in half, using an iterative approach. If the value of the search key is less than the item in the middle of the interval, the algorithm narrows the interval to the lower half. Otherwise, it narrows it to the upper half. The process continues until the search key is found or the interval is empty. + +In pseudo-code, iterative binary search is defined as follows: + +```cpp +FUNCTION iterativeBinarySearch(array, key): +low = 0 +high = array.length - 1 +WHILE low <= high: +mid = (low + high) / 2 +IF array[mid] == key: +RETURN mid +ELSE IF array[mid] < key: +low = mid + 1 +ELSE: +high = mid - 1 +RETURN -1 +``` + +```cpp +int iterativeBinarySearch(int array[], int size, int key) { + int low = 0; + int high = size - 1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (array[mid] == key) { + return mid; + } else if (array[mid] < key) { + low = mid + 1; + } else { + high = mid - 1; + } + } + return -1; +} +``` + +## How Iterative Binary Search Works + +### Step-by-Step Explanation + +1. Initialize: Set two pointers, low at the beginning and high at the end of the array. +2. Middle Element: Calculate the middle element's index. +Comparison: +3. If the middle element is the target, return its index. +4. If the middle element is less than the target, discard the left half by setting low to mid + 1. +5. If the middle element is greater than the target, discard the right half by setting high to mid - 1. +6. Repeat: Repeat steps 2 and 3 until the target is found or the low pointer exceeds the high pointer. + +### Time Complexity + +The time complexity of iterative binary search is $O(logn)$, +where $𝑛$ is the number of elements in the array. This logarithmic time complexity makes iterative binary search significantly faster than linear search for large datasets. + +## Practical Applications + +Iterative binary search is widely used in various real-world applications and algorithmic problems: + +1. Searching in a Sorted Array + The primary use of iterative binary search is to find elements in a sorted array efficiently. It is the foundation for more complex search algorithms. + +2. Dictionary Lookups + Iterative binary search is used in dictionaries (like the one you're reading now) to quickly find words and their definitions. + +3. Binary Search Trees + Iterative binary search is the basis for searching in binary search trees (BSTs), a fundamental data structure in computer science. + +4. Finding Boundaries + Iterative binary search can be adapted to find the first or last occurrence of a target element, making it useful in problems requiring boundary searches. + +Common Problems Solved Using Iterative Binary Search +Iterative binary search can be adapted in various ways to solve different types of problems. Here are a couple of common problems: + +1. Lower Bound and Upper Bound + These variations of iterative binary search are used to find the first and last occurrence of a target element in a sorted array. + +Lower Bound Pseudo-Code: + +```cpp +FUNCTION lowerBound(array, key): + low = 0 + high = array.length + WHILE low < high: + mid = (low + high) / 2 + IF array[mid] < key: + low = mid + 1 + ELSE: + high = mid + RETURN low + +``` + +Upper Bound Pseudo-Code: + +```cpp +FUNCTION upperBound(array, key): + low = 0 + high = array.length + WHILE low < high: + mid = (low + high) / 2 + IF array[mid] <= key: + low = mid + 1 + ELSE: + high = mid + RETURN low + + +``` + +2. Rotated Sorted Array + Iterative binary search can be modified to handle rotated sorted arrays, where the array is sorted but then rotated at some pivot point. + +:::tip +Handle Edge Cases: Ensure your implementation correctly handles cases where the target element is not present or when the array is empty. +Prevent Overflow: When calculating the middle index, use $\text{mid} = \text{low} + \frac{\text{high} - \text{low}}{2}$ instead of $\text{mid} = \frac{\text{low} + \text{high}}{2}$ to prevent potential overflow. +Efficiency: The iterative approach often uses less memory than the recursive approach because it doesn't involve the overhead of multiple recursive function calls. +::: + +## Conclusion + +Iterative binary search is a fundamental algorithm that every programmer should master. Its efficiency and versatility make it a powerful tool for solving a wide range of problems. By understanding how iterative binary search works and how to implement its variations, you'll be well-equipped to tackle numerous challenges in your programming journey. diff --git a/docs/binary_search/_category_.json b/docs/binary_search/_category_.json new file mode 100644 index 000000000..34c97b96a --- /dev/null +++ b/docs/binary_search/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Binary Search", + "position": 7, + "link": { + "type": "generated-index", + "description": "Binary Search Algorithm is a searching algorithm used in a sorted array by repeatedly dividing the search interval in half. The idea of binary search is to use the information that the array is sorted and reduce the time complexity to O(log N). " + } +} diff --git a/docs/binary_search/binary-search.md b/docs/binary_search/binary-search.md new file mode 100644 index 000000000..fbc27804e --- /dev/null +++ b/docs/binary_search/binary-search.md @@ -0,0 +1,62 @@ +--- +id: binary-search-dsa +title: Binary Search +sidebar_label: Binary Search +description: "In this blog post, we'll dive into the binary search algorithm, a fundamental technique in computer science for efficiently finding an element in a sorted array." +tags: [dsa, algorithms, binary search] +--- + + +## Introduction +Binary search is a searching algorithm, used to search for an element in an array. It follows a unique approach which reduces the time complexity as compared to linear search. However, to use binary search, the array must be sorted. + +## Implementation + +Let us see how to implement binary search in Java: + +```java + //let element to be found=target + int low=0; + int high=n-1; //where n is the length of the sorted array + int mid; //represents the mid index of the array + + int flag=0; //element not yet found + + while(low<=high) { + + mid=(low + high)/2; + if(arr[mid]==target) { + flag=1; //element found + System.out.println("Target found!"); + break; + } + else if(arr[mid] +Binary search : O(log n) + +## Points to Remember: + +- Binary search requires a sorted array. +- Works for 1 dimensional arrays. +- Faster and complex than sequential search. +- Uses the divide and conquer approach. +- Best if arrays are too long (large datasets). diff --git a/docs/binary_search/recursive_binary_search.md b/docs/binary_search/recursive_binary_search.md new file mode 100644 index 000000000..4d3c50f27 --- /dev/null +++ b/docs/binary_search/recursive_binary_search.md @@ -0,0 +1,126 @@ +--- +id: recursive-binary-search-DSA +title: Recursive Binary Search +sidebar_label: Recursive Binary Search +sidebar_position: 8 +description: "In this blog post, we'll explore the recursive binary search algorithm, a fundamental technique in computer science for efficiently finding an element in a sorted array. You'll learn what recursive binary search is, how it works, and its time complexity. We'll also cover practical applications and common problems you can solve using this algorithm. By the end, you'll have a thorough understanding of recursive binary search and how to implement it in your programming projects." +tags: [dsa, algorithms, binary search, recursive] +--- + +Recursive Binary Search algorithm is essential for efficiently finding elements in sorted arrays, making it a staple in the toolkit of any adept programmer. Whether you're optimizing search operations or solving complex algorithmic challenges, understanding recursive binary search is crucial. Let's delve into its mechanics, applications, and implementation. + +## What is Recursive Binary Search? + +Recursive binary search is a highly efficient algorithm used to find an element in a sorted array. It works by repeatedly dividing the search interval in half, using a recursive approach. If the value of the search key is less than the item in the middle of the interval, the algorithm narrows the interval to the lower half. Otherwise, it narrows it to the upper half. The process continues until the search key is found or the interval is empty. + +In pseudo-code, recursive binary search is defined as follows: + +```cpp +FUNCTION recursiveBinarySearch(array, low, high, key): +IF low > high: +RETURN -1 +mid = (low + high) / 2 +IF array[mid] == key: +RETURN mid +ELSE IF array[mid] < key: +RETURN recursiveBinarySearch(array, mid + 1, high, key) +ELSE: +RETURN recursiveBinarySearch(array, low, mid - 1, key) +``` + +```cpp +int recursiveBinarySearch(int array[], int low, int high, int key) { + if (low > high) { + return -1; + } + int mid = low + (high - low) / 2; + if (array[mid] == key) { + return mid; + } else if (array[mid] < key) { + return recursiveBinarySearch(array, mid + 1, high, key); + } else { + return recursiveBinarySearch(array, low, mid - 1, key); + } +} +``` + +## How Recursive Binary Search Works + +### Step-by-Step Explanation + +1. Initialize: Set two pointers, low at the beginning and high at the end of the array. +2. Middle Element: Calculate the middle element's index. +Comparison: +3. If the middle element is the target, return its index. +4. If the middle element is less than the target, discard the left half by setting low to mid + 1. +5. If the middle element is greater than the target, discard the right half by setting high to mid - 1. +6. Repeat: Repeat steps 2 and 3 until the target is found or the low pointer exceeds the high pointer. + +### Time Complexity + +The time complexity of iterative binary search is $𝑂(log𝑛)$. + +where $n$ is the number of elements in the array. This logarithmic time complexity makes iterative binary search significantly faster than linear search for large datasets. + +## Practical Applications + +Iterative binary search is widely used in various real-world applications and algorithmic problems: + +1. Searching in a Sorted Array + The primary use of iterative binary search is to find elements in a sorted array efficiently. It is the foundation for more complex search algorithms. + +2. Dictionary Lookups + Iterative binary search is used in dictionaries (like the one you're reading now) to quickly find words and their definitions. + +3. Binary Search Trees + Iterative binary search is the basis for searching in binary search trees (BSTs), a fundamental data structure in computer science. + +4. Finding Boundaries + Iterative binary search can be adapted to find the first or last occurrence of a target element, making it useful in problems requiring boundary searches. + +Common Problems Solved Using Iterative Binary Search +Iterative binary search can be adapted in various ways to solve different types of problems. Here are a couple of common problems: + +1. Lower Bound and Upper Bound + These variations of iterative binary search are used to find the first and last occurrence of a target element in a sorted array. + +Lower Bound Pseudo-Code: + +```cpp +FUNCTION lowerBound(array, low, high, key): + IF low == high: + RETURN low + mid = (low + high) / 2 + IF array[mid] < key: + RETURN lowerBound(array, mid + 1, high, key) + ELSE: + RETURN lowerBound(array, low, mid, key) + + +``` + +Upper Bound Pseudo-Code: + +```cpp +FUNCTION upperBound(array, low, high, key): + if low == high: + return low + mid = (low + high) / 2 + if array[mid] <= key: + return upperBound(array, mid + 1, high, key) + else: + return upperBound(array, low, mid, key) + +``` + +2. Rotated Sorted Array + Recursive binary search can be modified to handle rotated sorted arrays, where the array is sorted but then rotated at some pivot point. + +:::tip +Handle Edge Cases: Ensure your implementation correctly handles cases where the target element is not present or when the array is empty. +Prevent Stack Overflow: Be mindful of the recursion depth, especially for large arrays, as deep recursion can lead to stack overflow. +Efficiency: The recursive approach can be more intuitive and elegant, but consider the iterative approach for environments with limited stack size. +::: + +## Conclusion +Recursive binary search is a fundamental algorithm that every programmer should master. Its efficiency and versatility make it a powerful tool for solving a wide range of problems. By understanding how recursive binary search works and how to implement its variations, you'll be well-equipped to tackle numerous challenges in your programming journey. diff --git a/docs/graphs/_category_.json b/docs/graphs/_category_.json new file mode 100644 index 000000000..440450f74 --- /dev/null +++ b/docs/graphs/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Graphs", + "position": 14, + "link": { + "type": "generated-index", + "description": "Graphs are a data structure that represents a collection of interconnected nodes or vertices. They are commonly used in Data Structures and Algorithms (DSA) to model relationships between objects or entities." + } +} diff --git a/docs/graphs/graphs.md b/docs/graphs/graphs.md new file mode 100644 index 000000000..a914f12e6 --- /dev/null +++ b/docs/graphs/graphs.md @@ -0,0 +1,301 @@ + + +# Graphs in Data Structures and Algorithms (DSA) + +Graphs in Data Structures and Algorithms (DSA) are a non-linear data structure that consists of a set of vertices (nodes) connected by edges. They are widely used to represent relationships between objects or entities. + +![alt text](image.png) + +## Implementing a Graph using an Adjacency Matrix + +To implement a graph, you can use various data structures such as an adjacency matrix or an adjacency list. + +An adjacency matrix is a 2D array where each cell represents the presence or absence of an edge between two vertices. It requires O(V^2) space, where V is the number of vertices. + +Here's an example of implementing a graph using an adjacency matrix in Python: + +```python +class Graph: + def __init__(self, num_vertices): + self.num_vertices = num_vertices + self.adj_matrix = [[0] * num_vertices for _ in range(num_vertices)] + + def add_edge(self, src, dest): + self.adj_matrix[src][dest] = 1 + self.adj_matrix[dest][src] = 1 + + def remove_edge(self, src, dest): + self.adj_matrix[src][dest] = 0 + self.adj_matrix[dest][src] = 0 + + def print_graph(self): + for row in self.adj_matrix: + print(row) + +# Example usage: +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.print_graph() +``` + +Output: +```plaintext +[0, 1, 0, 0] +[1, 0, 1, 0] +[0, 1, 0, 1] +[0, 0, 1, 0] +``` + +## Implementing a Graph using an Adjacency List + +Another way to implement a graph is using an adjacency list. It is a collection of linked lists, where each vertex has a list of its adjacent vertices. It requires O(V + E) space, where V is the number of vertices and E is the number of edges. + +Here's an example of implementing a graph using an adjacency list in Python: + +```python +class Graph: + def __init__(self, num_vertices): + self.num_vertices = num_vertices + self.adj_list = [[] for _ in range(num_vertices)] + + def add_edge(self, src, dest): + self.adj_list[src].append(dest) + self.adj_list[dest].append(src) + + def remove_edge(self, src, dest): + self.adj_list[src].remove(dest) + self.adj_list[dest].remove(src) + + def print_graph(self): + for vertex, adj_vertices in enumerate(self.adj_list): + print(f"Vertex {vertex}: {adj_vertices}") + +# Example usage: +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.print_graph() +``` + +Output: +```plaintext +Vertex 0: [1] +Vertex 1: [0, 2] +Vertex 2: [1, 3] +Vertex 3: [2] +``` + +## Additional Operations on Graphs + +These are just basic operations on graphs. Depending on your requirements, you can perform various other operations like finding a path between two vertices, checking for cycles, or finding the shortest path using algorithms like Depth-First Search (DFS) or Breadth-First Search (BFS). + + +Graphs in Data Structures and Algorithms (DSA) are a non-linear data structure that consists of a set of vertices (nodes) connected by edges. They are widely used to represent relationships between objects or entities. + +To implement a graph, you can use various data structures such as an adjacency matrix or an adjacency list. + +An adjacency matrix is a 2D array where each cell represents the presence or absence of an edge between two vertices. It requires O(V^2) space, where V is the number of vertices. + +Here's an example of implementing a graph using an adjacency matrix in Python: + +```python +class Graph: + def __init__(self, num_vertices): + self.num_vertices = num_vertices + self.adj_matrix = [[0] * num_vertices for _ in range(num_vertices)] + + def add_edge(self, src, dest): + self.adj_matrix[src][dest] = 1 + self.adj_matrix[dest][src] = 1 + + def remove_edge(self, src, dest): + self.adj_matrix[src][dest] = 0 + self.adj_matrix[dest][src] = 0 + + def print_graph(self): + for row in self.adj_matrix: + print(row) + +# Example usage: +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.print_graph() +``` + +Output: +``` +[0, 1, 0, 0] +[1, 0, 1, 0] +[0, 1, 0, 1] +[0, 0, 1, 0] +``` + +Another way to implement a graph is using an adjacency list. It is a collection of linked lists, where each vertex has a list of its adjacent vertices. It requires O(V + E) space, where V is the number of vertices and E is the number of edges. + +Here's an example of implementing a graph using an adjacency list in Python: + +```python +class Graph: + def __init__(self, num_vertices): + self.num_vertices = num_vertices + self.adj_list = [[] for _ in range(num_vertices)] + + def add_edge(self, src, dest): + self.adj_list[src].append(dest) + self.adj_list[dest].append(src) + + def remove_edge(self, src, dest): + self.adj_list[src].remove(dest) + self.adj_list[dest].remove(src) + + def print_graph(self): + for vertex, adj_vertices in enumerate(self.adj_list): + print(f"Vertex {vertex}: {adj_vertices}") + +# Example usage: +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.print_graph() +``` + +Output: +``` +Vertex 0: [1] +Vertex 1: [0, 2] +Vertex 2: [1, 3] +Vertex 3: [2] +``` + +These are just basic operations on graphs. Depending on your requirements, you can perform various other operations like finding a path between two vertices, checking for cycles, or finding the shortest path using algorithms like Depth-First Search (DFS) or Breadth-First Search (BFS). + +## Implementing a Graph in Java + +To implement a graph in Java, you can use similar approaches as in Python. Here's an example of implementing a graph using an adjacency matrix in Java: + +```java +public class Graph { + private int numVertices; + private int[][] adjMatrix; + + public Graph(int numVertices) { + this.numVertices = numVertices; + this.adjMatrix = new int[numVertices][numVertices]; + } + + public void addEdge(int src, int dest) { + adjMatrix[src][dest] = 1; + adjMatrix[dest][src] = 1; + } + + public void removeEdge(int src, int dest) { + adjMatrix[src][dest] = 0; + adjMatrix[dest][src] = 0; + } + + public void printGraph() { + for (int i = 0; i < numVertices; i++) { + for (int j = 0; j < numVertices; j++) { + System.out.print(adjMatrix[i][j] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + Graph g = new Graph(4); + g.addEdge(0, 1); + g.addEdge(1, 2); + g.addEdge(2, 3); + g.printGraph(); + } +} +``` + +Output: +``` +0 1 0 0 +1 0 1 0 +0 1 0 1 +0 0 1 0 +``` + +## Implementing a Graph in C++ + +Similarly, you can implement a graph in C++ using an adjacency matrix. Here's an example: + +```cpp +#include +#include + +using namespace std; + +class Graph { +private: + int numVertices; + vector> adjMatrix; + +public: + Graph(int numVertices) { + this->numVertices = numVertices; + this->adjMatrix.resize(numVertices, vector(numVertices, 0)); + } + + void addEdge(int src, int dest) { + adjMatrix[src][dest] = 1; + adjMatrix[dest][src] = 1; + } + + void removeEdge(int src, int dest) { + adjMatrix[src][dest] = 0; + adjMatrix[dest][src] = 0; + } + + void printGraph() { + for (int i = 0; i < numVertices; i++) { + for (int j = 0; j < numVertices; j++) { + cout << adjMatrix[i][j] << " "; + } + cout << endl; + } + } +}; + +int main() { + Graph g(4); + g.addEdge(0, 1); + g.addEdge(1, 2); + g.addEdge(2, 3); + g.printGraph(); + + return 0; +} +``` + +Output: +``` +0 1 0 0 +1 0 1 0 +0 1 0 1 +0 0 1 0 +``` + +Remember to adjust the number of vertices and edges according to your requirements. + +In conclusion, graphs are a fundamental data structure in Data Structures and Algorithms (DSA) that are used to represent relationships between objects or entities. They can be implemented using various data structures such as an adjacency matrix or an adjacency list. + +An adjacency matrix is a 2D array that represents the presence or absence of edges between vertices. It requires O(V^2) space, where V is the number of vertices. On the other hand, an adjacency list is a collection of linked lists where each vertex has a list of its adjacent vertices. It requires O(V + E) space, where V is the number of vertices and E is the number of edges. + +Both implementations have their own advantages and disadvantages. The choice of implementation depends on the specific requirements of the problem at hand. + +Additionally, there are various other operations that can be performed on graphs, such as finding a path between two vertices, checking for cycles, or finding the shortest path using algorithms like Depth-First Search (DFS) or Breadth-First Search (BFS). + +Graphs can also be implemented in other programming languages like Java and C++. The implementation follows similar approaches using either an adjacency matrix or an adjacency list. + +Overall, understanding graphs and their implementations is crucial for solving problems that involve relationships and connectivity between entities. diff --git a/docs/graphs/image.png b/docs/graphs/image.png new file mode 100644 index 000000000..6398c1d98 Binary files /dev/null and b/docs/graphs/image.png differ diff --git a/docs/basics/_category_.json b/docs/languages/_category_.json similarity index 84% rename from docs/basics/_category_.json rename to docs/languages/_category_.json index 1038a5ea0..4b7ca7825 100644 --- a/docs/basics/_category_.json +++ b/docs/languages/_category_.json @@ -1,5 +1,5 @@ { - "label": "Basics concepts", + "label": "Languages", "position": 3, "link": { "type": "generated-index", diff --git a/docs/basics/javascript/_category_.json b/docs/languages/javascript/_category_.json similarity index 100% rename from docs/basics/javascript/_category_.json rename to docs/languages/javascript/_category_.json diff --git a/docs/basics/javascript/ex-1.md b/docs/languages/javascript/ex-1.md similarity index 100% rename from docs/basics/javascript/ex-1.md rename to docs/languages/javascript/ex-1.md diff --git a/docs/basics/javascript/ex-2.md b/docs/languages/javascript/ex-2.md similarity index 100% rename from docs/basics/javascript/ex-2.md rename to docs/languages/javascript/ex-2.md diff --git a/docs/basics/javascript/ex-3.md b/docs/languages/javascript/ex-3.md similarity index 100% rename from docs/basics/javascript/ex-3.md rename to docs/languages/javascript/ex-3.md diff --git a/docs/linkedlist/LinkedList.md b/docs/linkedlist/LinkedList.md new file mode 100644 index 000000000..b2fd1f35b --- /dev/null +++ b/docs/linkedlist/LinkedList.md @@ -0,0 +1,695 @@ +--- +id: linkedlist-in-dsa +title: LinkedList in Data Structures and Algorithms +sidebar_label: LinkedList +sidebar_position: 2 +description: "A linked list is a linear data structure in which elements are not stored in contiguous memory locations. Instead, each element, called a node, contains a data part and a reference (or link) to the next node in the sequence. Linked lists are used in various applications such as dynamic memory allocation, implementation of data structures like stacks and queues, and more." +tags: + [ + dsa, + data-structures, + linkedlist, + linked-list, + linkedlist-data-structure, + linkedlist-in-dsa, + linkedlist-in-data-structure, + linkedlist-in-algorithm, + linkedlist-in-dsa-example, + linkedlist-in-dsa-explanation, + linkedlist-in-dsa-conclusion, + linkedlist-in-dsa-importance, + linkedlist-in-dsa-syntax, + linkedlist-in-dsa-declaration, + linkedlist-in-dsa-access, + linkedlist-in-dsa-update, + linkedlist-in-dsa-insertion, + linkedlist-in-dsa-deletion, + linkedlist-in-dsa-traversal, + linkedlist-in-dsa-program, + linkedlist-in-dsa-code, + linkedlist-in-dsa-js, + linkedlist-in-dsa-java, + linkedlist-in-dsa-python, + linkedlist-in-dsa-c, + linkedlist-in-dsa-cpp, + linkedlist-in-dsa-ts, + ] +--- + +A linked list is a linear data structure in which elements are not stored in contiguous memory locations. Instead, each element, called a node, contains a data part and a reference (or link) to the next node in the sequence. Linked lists are used in various applications such as dynamic memory allocation, implementation of data structures like stacks and queues, and more. + +## Why are Linked Lists important? + +Linked lists are important because they provide a flexible way to store and manipulate data. They allow for efficient insertion and deletion of elements, which can be particularly useful in applications where the size of the data set changes frequently. + +## How to declare a Linked List? + +A linked list can be declared in various programming languages using the following syntax: + +# LinkedList in Data Structures and Algorithms (DSA) + +## Introduction + +A linked list is a fundamental data structure in computer science that represents a sequence of nodes. Each node contains data and a reference to the next node in the sequence. + +## Types of Linked Lists + +- **Singly Linked List**: Each node points to the next node and the last node points to null. +- **Doubly Linked List**: Each node points to both the next and the previous nodes. +- **Circular Linked List**: The last node points to the first node, forming a circle. +- **Circular Doubly Linked List**: Combines properties of both circular and doubly linked lists. + +## Basic Operations + +- **Insertion**: Adding a node to the linked list. +- **Deletion**: Removing a node from the linked list. +- **Traversal**: Accessing each node of the linked list. +- **Search**: Finding a node with a specific value. +- **Update**: Modifying the value of an existing node. + +## Why are Linked Lists important? + +Linked lists are important because they provide a flexible way to store and manipulate data. They allow for efficient insertion and deletion of elements, which can be particularly useful in applications where the size of the data set changes frequently. + +## How to declare a Linked List? + +A linked list can be declared in various programming languages using the following syntax: + + + + + ```js + // Node class in JavaScript + class Node { + constructor(data) { + this.data = data; + this.next = null; + } + } + + // LinkedList class in JavaScript + class LinkedList { + constructor() { + this.head = null; + } + + // Add a node at the end + append(data) { + let newNode = new Node(data); + if (this.head === null) { + this.head = newNode; + return; + } + let current = this.head; + while (current.next) { + current = current.next; + } + current.next = newNode; + } + } + ``` + + + + ```java + // Node class in Java + class Node { + int data; + Node next; + Node(int d) { data = d; next = null; } + } + + // LinkedList class in Java + public class LinkedList { + Node head; + + // Add a node at the end + public void append(int data) { + Node newNode = new Node(data); + if (head == null) { + head = newNode; + return; + } + Node current = head; + while (current.next != null) { + current = current.next; + } + current.next = newNode; + } + } + ``` + + + + ```python + # Node class in Python + class Node: + def __init__(self, data): + self.data = data + self.next = None + + # LinkedList class in Python + class LinkedList: + def __init__(self): + self.head = None + + # Add a node at the end + def append(self, data): + new_node = Node(data) + if self.head is None: + self.head = new_node + return + last = self.head + while last.next: + last = last.next + last.next = new_node + ``` + + + + ```c + // Node structure in C + struct Node { + int data; + struct Node* next; + }; + + // LinkedList structure in C + struct LinkedList { + struct Node* head; + }; + + // Function to create a new node + struct Node* createNode(int data) { + struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); + newNode->data = data; + newNode->next = NULL; + return newNode; + } + + // Function to add a node at the end + void append(struct LinkedList* list, int data) { + struct Node* newNode = createNode(data); + if (list->head == NULL) { + list->head = newNode; + return; + } + struct Node* temp = list->head; + while (temp->next != NULL) { + temp = temp->next; + } + temp->next = newNode; + } + ``` + + + + ```cpp + // Node class in C++ + class Node { + public: + int data; + Node* next; + Node(int d) { data = d; next = nullptr; } + }; + + // LinkedList class in C++ + class LinkedList { + public: + Node* head; + LinkedList() { head = nullptr; } + + // Add a node at the end + void append(int data) { + Node* newNode = new Node(data); + if (head == nullptr) { + head = newNode; + return; + } + Node* current = head; + while (current->next != nullptr) { + current = current->next; + } + current->next = newNode; + } + }; + ``` + + + + ```ts + // Node class in TypeScript + class Node { + data: number; + next: Node | null; + + constructor(data: number) { + this.data = data; + this.next = null; + } + } + + // LinkedList class in TypeScript + class LinkedList { + head: Node | null; + + constructor() { + this.head = null; + } + + // Add a node at the end + append(data: number): void { + let newNode = new Node(data); + if (this.head === null) { + this.head = newNode; + return; + } + let current = this.head; + while (current.next !== null) { + current = current.next; + } + current.next = newNode; + } + } + ``` + + + +## How to access a Linked List? + +A linked list can be accessed by traversing the nodes starting from the head. + + + + + ```js + // Access elements in a LinkedList in JavaScript + let list = new LinkedList(); + list.append(10); + list.append(20); + list.append(30); + + let current = list.head; + while (current !== null) { + console.log(current.data); + current = current.next; + } + ``` + + + + ```java + // Access elements in a LinkedList in Java + LinkedList list = new LinkedList(); + list.append(10); + list.append(20); + list.append(30); + + Node current = list.head; + while (current != null) { + System.out.println(current.data); + current = current.next; + } + ``` + + + + ```python + # Access elements in a LinkedList in Python + list = LinkedList() + list.append(10) + list.append(20) + list.append(30) + + current = list.head + while current: + print(current.data) + current = current.next + ``` + + + + ```c + // Access elements in a LinkedList in C + struct LinkedList list; + list.head = NULL; + append(&list, 10); + append(&list, 20); + append(&list, 30); + + struct Node* current = list.head; + while (current != NULL) { + printf("%d\n", current->data); + current = current->next; + } + ``` + + + + ```cpp + // Access elements in a LinkedList in C++ + LinkedList list; + list.append(10); + list.append(20); + list.append(30); + + Node* current = list.head; + while (current != nullptr) { + std::cout << current->data << std::endl; + current = current->next; + } + ``` + + + + ```ts + // Access elements in a LinkedList in TypeScript + let list = new LinkedList(); + list.append(10); + list.append(20); + list.append(30); + + let current = list.head; + while (current !== null) { + console.log(current.data); + current = current.next; + } + ``` + + + +## How to update a Linked List? + +A linked list can be updated by changing the data of a node directly. + + + + + ```js + // Update elements in a LinkedList in JavaScript + let list = new LinkedList(); + list.append(10); + list.append(20); + list.append(30); + + let current = list.head; + while (current !== null) { + if (current.data === 20) { + current.data = 25; + } + current = current.next; + } + ``` + + + + ```java + // Update elements in a LinkedList in Java + LinkedList list = new LinkedList(); + list.append(10); + list.append(20); + list.append(30); + + Node current = list.head; + while (current != null) { + if (current.data == 20) { + current.data = 25; + } + current = current.next; + } + ``` + + + + ```python + # Update elements in a LinkedList in Python + list = LinkedList() + list.append(10) + list.append(20) + list.append(30) + + current = list.head + while current: + if current.data == 20: + current.data = 25 + current = current.next + ``` + + + + ```c + // Update elements in a LinkedList in C + struct LinkedList list; + list.head = NULL; + append(&list, 10); + append(&list, 20); + append(&list, 30); + + struct Node* current = list.head; + while (current != NULL) { + if (current->data == 20) { + current->data = 25; + } + current = current->next; + } + ``` + + + + ```cpp + // Update elements in a LinkedList in C++ + LinkedList list; + list.append(10); + list.append(20); + list.append(30); + + Node* current = list.head; + while (current != nullptr) { + if (current->data == 20) { + current->data = 25; + } + current = current->next; + } + ``` + + + + ```ts + // Update elements in a LinkedList in TypeScript + let list = new LinkedList(); + list.append(10); + list.append(20); + list.append(30); + + let current = list.head; + while (current !== null) { + if (current.data === 20) { + current.data = 25; + } + current = current.next; + } + ``` + + + +## How to delete a node from a Linked List? + +A node can be deleted from a linked list by adjusting the links of the adjacent nodes. + + + + + ```js + // Delete a node from a LinkedList in JavaScript + class LinkedList { + // ... + delete(data) { + if (this.head === null) return; + if (this.head.data === data) { + this.head = this.head.next; + return; + } + let current = this.head; + while (current.next !== null) { + if (current.next.data === data) { + current.next = current.next.next; + return; + } + current = current.next; + } + } + } + ``` + + + + ```java + // Delete a node from a LinkedList in Java + public class LinkedList { + // ... + public void delete(int data) { + if (head == null) return; + if (head.data == data) { + head = head.next; + return; + } + Node current = head; + while (current.next != null) { + if (current.next.data == data) { + current.next = current.next.next; + return; + } + current = current.next; + } + } + } + ``` + + + + ```python + # Delete a node from a LinkedList in Python + class LinkedList: + # ... + def delete(self, data): + if self.head is None: + return + if self.head.data == data: + self.head = self.head.next + return + current = self.head + while current.next: + if current.next.data == data: + current.next = current.next.next + return + current = current.next + ``` + + + + ```c + // Delete a node from a LinkedList in C + void deleteNode(struct LinkedList* list, int data) { + if (list->head == NULL) return; + struct Node* temp = list->head; + if (temp->data == data) { + list->head = temp->next; + free(temp); + return; + } + struct Node* prev = NULL; + while (temp != NULL && temp->data != data) { + prev = temp; + temp = temp->next; + } + if (temp == NULL) return; + prev->next = temp->next; + free(temp); + } + ``` + + + + ```cpp + // Delete a node from a LinkedList in C++ + class LinkedList { + // ... + void deleteNode(int data) { + if (head == nullptr) return; + Node* temp = head; + if (temp->data == data) { + head = temp->next; + delete temp; + return; + } + Node* prev = nullptr; + while (temp != nullptr && temp->data != data) { + prev = temp; + temp = temp->next; + } + if (temp == nullptr) return; + prev->next = temp->next; + delete temp; + } + }; + ``` + + + + ```ts + // Delete a node from a LinkedList in TypeScript + class LinkedList { + // ... + delete(data: number) { + if (this.head === null) return; + if (this.head.data === data) { + this.head = this.head.next; + return; + } + let current = this.head; + while (current.next !== null) { + if (current.next.data === data) { + current.next = current.next.next; + return; + } + current = current.next; + } + } + } + ``` + + + +## Applications of Linked Lists + +- **Dynamic memory allocation**: Linked lists can efficiently manage memory allocation and deallocation. +- **Implementation of stacks and queues**: Linked lists provide the foundation for stack and queue data structures. +- **Graph representation**: Adjacency lists use linked lists to represent graphs. +- **Handling sparse matrices**: Linked lists efficiently store and manipulate sparse matrices. + +## Common Linked List Problems + +- **Reverse a Linked List**: Reversing the nodes of a linked list. +- **Detect a cycle in a Linked List**: Checking if a linked list contains a cycle. +- **Merge two sorted Linked Lists**: Combining two sorted linked lists into one sorted linked list. +- **Find the middle of a Linked List**: Finding the middle node in a linked list. + +## Advanced Linked List Variants + +- **Doubly Linked List**: A linked list where each node has references to both the next and previous nodes. +- **Circular Linked List**: A linked list where the last node points back to the first node. +- **Skip List**: A linked list with additional pointers for faster search. + +## Resources and References + +- **Books**: + - "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein + - "Data Structures and Algorithm Analysis in C" by Mark Allen Weiss +- **Online Courses**: + - Coursera: Data Structures and Algorithms Specialization + - edX: Data Structures Fundamentals +- **Websites**: + - [GeeksforGeeks](https://www.geeksforgeeks.org) + - [LeetCode](https://leetcode.com) + - [HackerRank](https://www.hackerrank.com) + +--- + +By understanding and mastering these linked list concepts and algorithms, you will be well-equipped to tackle a wide range of problems in data structures and algorithms. + +## Conclusion + +Linked lists are a fundamental data structure in computer science and play a crucial role in various applications and algorithms. This guide covers the essential concepts and operations associated with linked lists, providing a comprehensive understanding of how they work and how to implement them in different programming languages. + +Understanding linked lists is crucial for solving numerous problems in computer science, from basic data manipulation to complex algorithms in dynamic memory allocation, graph representation, and more. The examples provided demonstrate how to work with linked lists efficiently, ensuring a robust foundation for tackling more advanced DSA concepts. Mastery of linked lists enhances your ability to handle data structures and perform operations crucial in both everyday programming and competitive coding. + +Problems for Practice +To further practice and test your understanding of linked lists, consider solving the following problems from LeetCode: + +1. Reverse Linked List +2. Linked List Cycle +3. Merge Two Sorted Lists +4. Remove Nth Node From End of List +5. Add Two Numbers + +Engaging with these problems will help reinforce the concepts learned and provide practical experience in using linked lists effectively. By practicing these problems, you will enhance your problem-solving skills and deepen your understanding of linked list manipulation in various contexts. diff --git a/docs/linkedlist/LinkedListProblems.md b/docs/linkedlist/LinkedListProblems.md new file mode 100644 index 000000000..5c727033b --- /dev/null +++ b/docs/linkedlist/LinkedListProblems.md @@ -0,0 +1,146 @@ +--- +id: leetcode-linkedlist-problems +title: LeetCode LinkedList Problems +sidebar_label: LinkedList Problems +sidebar_position: 1 +description: "A collection of easy, medium, and hard LinkedList problems from LeetCode to help you practice and master LinkedList concepts in Data Structures and Algorithms." +tags: + [ + dsa, + data-structures, + linkedlist, + leetcode, + linkedlist-problems, + linkedlist-in-dsa, + linkedlist-in-data-structure, + linkedlist-in-algorithm, + linkedlist-in-dsa-example, + linkedlist-in-dsa-explanation, + linkedlist-in-dsa-conclusion, + linkedlist-in-dsa-importance, + linkedlist-in-dsa-syntax, + linkedlist-in-dsa-declaration, + linkedlist-in-dsa-access, + linkedlist-in-dsa-update, + linkedlist-in-dsa-delete, + linkedlist-in-dsa-iterate, + linkedlist-in-dsa-max-min, + linkedlist-in-dsa-program, + linkedlist-in-dsa-code, + linkedlist-in-dsa-js, + linkedlist-in-dsa-java, + linkedlist-in-dsa-python, + linkedlist-in-dsa-c, + linkedlist-in-dsa-cpp, + linkedlist-in-dsa-ts, + ] +--- + +# LeetCode LinkedList Problems + +## Easy Problems + +1. [Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) + + - Problem: Reverse a singly linked list. + - LeetCode ID: 206 + +2. [Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) + + - Problem: Remove all elements from a linked list of integers that have value val. + - LeetCode ID: 203 + +3. [Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list/) + + - Problem: Check if a linked list is a palindrome. + - LeetCode ID: 234 + +4. [Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/) + + - Problem: Merge two sorted linked lists and return it as a new sorted list. + - LeetCode ID: 21 + +5. [Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/) + + - Problem: Determine if a linked list has a cycle. + - LeetCode ID: 141 + +6. [Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/) + + - Problem: Delete all duplicates such that each element appears only once. + - LeetCode ID: 83 + +7. [Intersection of Two Linked Lists](https://leetcode.com/problems/intersection-of-two-linked-lists/) + - Problem: Find the node at which the intersection of two singly linked lists begins. + - LeetCode ID: 160 + +## Medium Problems + +1. [Add Two Numbers](https://leetcode.com/problems/add-two-numbers/) + + - Problem: Add two numbers represented by linked lists. + - LeetCode ID: 2 + +2. [Odd Even Linked List](https://leetcode.com/problems/odd-even-linked-list/) + + - Problem: Group all odd nodes together followed by the even nodes. + - LeetCode ID: 328 + +3. [Remove Nth Node From End of List](https://leetcode.com/problems/remove-nth-node-from-end-of-list/) + + - Problem: Remove the nth node from the end of the list. + - LeetCode ID: 19 + +4. [Reorder List](https://leetcode.com/problems/reorder-list/) + + - Problem: Reorder the list to follow a specific order. + - LeetCode ID: 143 + +5. [Rotate List](https://leetcode.com/problems/rotate-list/) + + - Problem: Rotate the list to the right by k places. + - LeetCode ID: 61 + +6. [Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) + + - Problem: Swap every two adjacent nodes. + - LeetCode ID: 24 + +7. [Partition List](https://leetcode.com/problems/partition-list/) + - Problem: Partition the list so that nodes less than x come before nodes greater than or equal to x. + - LeetCode ID: 86 + +## Hard Problems + +1. [Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/) + + - Problem: Merge k sorted linked lists and return it as one sorted list. + - LeetCode ID: 23 + +2. [Reverse Nodes in k-Group](https://leetcode.com/problems/reverse-nodes-in-k-group/) + + - Problem: Reverse the nodes of a linked list k at a time. + - LeetCode ID: 25 + +3. [Copy List with Random Pointer](https://leetcode.com/problems/copy-list-with-random-pointer/) + + - Problem: A linked list with a random pointer, deep copy the list. + - LeetCode ID: 138 + +4. [LRU Cache](https://leetcode.com/problems/lru-cache/) + + - Problem: Design and implement a data structure for Least Recently Used (LRU) cache. + - LeetCode ID: 146 + +5. [Flatten a Multilevel Doubly Linked List](https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/) + + - Problem: Flatten a multilevel doubly linked list. + - LeetCode ID: 430 + +6. [Linked List Random Node](https://leetcode.com/problems/linked-list-random-node/) + - Problem: Return a random node's value from the linked list. + - LeetCode ID: 382 + +## Conclusion + +Practicing these problems will help reinforce the concepts learned and provide practical experience in using linked lists effectively. By solving these problems, you will enhance your problem-solving skills and deepen your understanding of linked list manipulation in various contexts. diff --git a/docs/linkedlist/_category_.json b/docs/linkedlist/_category_.json new file mode 100644 index 000000000..471f3ca54 --- /dev/null +++ b/docs/linkedlist/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "LinkedList", + "position": 9, + "link": { + "type": "generated-index", + "description": "In data structures, Linked List is basically chains of nodes where each node contains information such as data and a pointer to the next node in the chain. It is a popular data structure with a wide range of real-world applications. In this article, we will provide a complete introduction of Linked List, which will help you tackle any problem based on Linked List." + } +} diff --git a/docs/recursion/_category_.json b/docs/recursion/_category_.json new file mode 100644 index 000000000..1af68bcf8 --- /dev/null +++ b/docs/recursion/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Recursion", + "position": 10, + "link": { + "type": "generated-index", + "description": "In data structures, The process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. Using a recursive algorithm, certain problems can be solved quite easily. " + } +} diff --git a/docs/recursion/recursion.md b/docs/recursion/recursion.md new file mode 100644 index 000000000..3032a5c1e --- /dev/null +++ b/docs/recursion/recursion.md @@ -0,0 +1,249 @@ +--- +id: recursion-in-dsa +title: Recursion in Data Structures and Algorithms +sidebar_label: Recursion +sidebar_position: 3 +description: "Recursion is a programming technique where a function calls itself directly or indirectly to solve a problem. It is used in various applications such as solving mathematical problems, implementing data structures, and more." +tags: + [ + dsa, + algorithms, + recursion, + recursive-functions, + recursion-in-dsa, + recursion-in-algorithm, + recursion-in-dsa-example, + recursion-in-dsa-explanation, + recursion-in-dsa-conclusion, + recursion-in-dsa-importance, + recursion-in-dsa-syntax, + recursion-in-dsa-implementation, + recursion-in-dsa-applications, + recursion-in-dsa-problems, + recursion-in-dsa-solutions, + recursion-in-dsa-code, + recursion-in-dsa-js, + recursion-in-dsa-java, + recursion-in-dsa-python, + recursion-in-dsa-c, + recursion-in-dsa-cpp, + recursion-in-dsa-ts, + ] +--- + +Recursion is a programming technique where a function calls itself directly or indirectly to solve a problem. It is used in various applications such as solving mathematical problems, implementing data structures, and more. + +## Why is Recursion important? + +Recursion is important because it provides a simple and elegant way to solve complex problems. It allows problems to be broken down into smaller, more manageable subproblems, making it easier to understand and implement solutions. + +## How to implement Recursion? + +Recursion can be implemented in various programming languages using the following syntax: + +## Recursive Function Structure + +```python +def recursive_function(parameters): + if base_condition(parameters): + return base_case_value + else: + return recursive_function(modified_parameters) +``` + +## Examples of Recursive Functions + +### Factorial + + + + + ```js + function factorial(n) { + if (n === 0) { + return 1; + } + return n * factorial(n - 1); + } + ``` + + + + ```java + public class RecursionExample { + public static int factorial(int n) { + if (n == 0) { + return 1; + } + return n * factorial(n - 1); + } + } + ``` + + + + ```python + def factorial(n): + if n == 0: + return 1 + return n * factorial(n - 1) + ``` + + + + ```c + int factorial(int n) { + if (n == 0) { + return 1; + } + return n * factorial(n - 1); + } + ``` + + + + ```cpp + int factorial(int n) { + if (n == 0) { + return 1; + } + return n * factorial(n - 1); + } + ``` + + + + ```ts + function factorial(n: number): number { + if (n === 0) { + return 1; + } + return n * factorial(n - 1); + } + ``` + + +Fibonacci + + + + ```js + function fibonacci(n) { + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); + } + ``` + + + + ```java + public class RecursionExample { + public static int fibonacci(int n) { + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); + } + } + ``` + + + + ```python + def fibonacci(n): + if n <= 1: + return n + return fibonacci(n - 1) + fibonacci(n - 2) + ``` + + + + ```c + int fibonacci(int n) { + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); + } + ``` + + + + ```cpp + int fibonacci(int n) { + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); + } + ``` + + + + ```ts + function fibonacci(n: number): number { + if (n <= 1) { + return n; + } + return fibonacci(n - 1) + fibonacci(n - 2); + } + ``` + + + +## Applications of Recursion + +1. Mathematical computations: Factorial, Fibonacci series, power functions, etc. +2. Data structures: Tree and graph traversals. +3. Algorithm design: Divide and conquer algorithms like merge sort and quicksort. +4. Dynamic programming: Recursive definitions with memoization. +5. Common Recursive Problems +6. Tower of Hanoi: Moving disks between rods following specific rules. +7. Permutations and combinations: Generating all possible arrangements or selections. +8. Backtracking: Solving puzzles and constraint satisfaction problems. + +## Advanced Recursive Techniques + +1. Tail Recursion: A special form of recursion where the recursive call is the last statement in the function. Optimized by compilers to prevent stack overflow. +2. Memoization: Caching the results of expensive function calls to improve performance. + +## Resources and References + +### Books: + +1. "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein +2. "The Art of Computer Programming" by Donald Knuth + +### Online Courses: + +1. Coursera: Data Structures and Algorithms Specialization +2. edX: Algorithms and Data Structures + +### Websites: + +1. GeeksforGeeks +2. LeetCode +3. HackerRank + +By understanding and mastering these recursion concepts and techniques, you will be well-equipped to tackle a wide range of problems in data structures and algorithms. + +## Conclusion + +Recursion is a powerful technique in computer science that enables elegant and efficient solutions to complex problems. This guide covers the essential concepts and applications of recursion, providing a comprehensive understanding of how to implement and utilize recursive functions in different programming languages. + +Understanding recursion is crucial for solving numerous problems in computer science, from basic mathematical computations to complex algorithm design. The examples provided demonstrate how to work with recursion effectively, ensuring a robust foundation for tackling more advanced DSA concepts. Mastery of recursion enhances your ability to handle algorithmic challenges and perform operations crucial in both everyday programming and competitive coding. + +## Problems for Practice + +To further practice and test your understanding of recursion, consider solving the following problems from LeetCode: + +1. Fibonacci Number +2. Climbing Stairs +3. Permutations +4. Subsets +5. Combination Sum + + + Engaging with these problems will help reinforce the concepts learned and provide practical experience in using recursion effectively. By practicing these problems, you will enhance your problem-solving skills and deepen your understanding of recursive algorithms in various contexts. diff --git a/docs/recursion/recursionVsIteration.md b/docs/recursion/recursionVsIteration.md new file mode 100644 index 000000000..84d0b0067 --- /dev/null +++ b/docs/recursion/recursionVsIteration.md @@ -0,0 +1,157 @@ +--- +id: recursion-vs-iteration-in-dsa +title: Difference between Recursion and Iteration in Data Structures and Algorithms +sidebar_label: Recursion vs Iteration +sidebar_position: 3 +description: "Understanding the differences between recursion and iteration is crucial for selecting the appropriate approach for solving various problems in data structures and algorithms. This guide highlights the key differences, advantages, and use cases of both recursion and iteration." +tags: + [ + dsa, + data-structures, + algorithms, + recursion, + iteration, + recursion-vs-iteration, + recursion-in-dsa, + iteration-in-dsa, + recursion-vs-iteration-in-dsa, + recursion-vs-iteration-examples, + recursion-vs-iteration-differences, + recursion-vs-iteration-advantages, + recursion-vs-iteration-disadvantages, + recursion-vs-iteration-usage, + ] +--- + +Understanding the differences between recursion and iteration is crucial for selecting the appropriate approach for solving various problems in data structures and algorithms. This guide highlights the key differences, advantages, and use cases of both recursion and iteration. + +## Introduction + +### Recursion + +Recursion is a technique where a function calls itself in order to solve a smaller instance of the same problem until a base case is reached. Recursive solutions can be elegant and easy to understand but may come with performance overheads due to repeated function calls. + +### Iteration + +Iteration involves using loops (such as for, while, or do-while) to repeat a block of code until a certain condition is met. Iterative solutions are often more efficient in terms of memory usage and execution time compared to recursion, but they can be less intuitive for problems that have a naturally recursive structure. + +## Key Differences + +| Feature | Recursion | Iteration | +| ------------ | ------------------------------------------------------ | ------------------------------------------- | +| Definition | Function calls itself | Loop repeatedly executes block of code | +| Termination | Base case | Loop condition | +| Memory Usage | Uses call stack, can lead to stack overflow | Uses constant or linear memory | +| Readability | Often more readable for problems with recursive nature | Can be less readable for complex conditions | +| Performance | Higher overhead due to function calls | Generally more efficient | +| Use Cases | Divide and conquer, tree and graph traversal | Simple loops, repetitive tasks | + +## Examples + +### Factorial Calculation + +#### Recursion + +```python +def factorial(n): + if n == 0: + return 1 + else: + return n * factorial(n - 1) +``` + +#### Iteration + +```python +def factorial(n): + result = 1 + for i in range(1, n + 1): + result *= i + return result +``` + +### Fibonacci Series + +#### Recursion + +```python +def fibonacci(n): + if n <= 1: + return n + else: + return fibonacci(n - 1) + fibonacci(n - 2) +``` + +#### Iteration + +```python +def fibonacci(n): + a, b = 0, 1 + for _ in range(n): + a, b = b, a + b + return a +``` + +## Advantages and Disadvantages + +### Recursion + +#### Advantages: + + 1. Simplifies the code for problems that have a natural recursive structure. + 2. Makes the code easier to write and understand for problems like tree traversals. + +#### Disadvantages: + + 1. Can lead to stack overflow if the recursion depth is too large. + 2. Generally less efficient in terms of time and space due to function call overhead. + +### Iteration + +#### Advantages: + + 1. More efficient in terms of memory and execution time. + 2. Avoids the risk of stack overflow. + +#### Disadvantages: + + 1. Can result in more complex and less readable code for problems with recursive nature. + 2. Requires explicit management of loop conditions and state. + +## When to Use Recursion vs Iteration + +### Use Recursion When: + +The problem has a naturally recursive structure (e.g., tree and graph traversal). +Code readability and simplicity are more important than performance. +You are dealing with divide-and-conquer algorithms (e.g., quicksort, mergesort). + +### Use Iteration When: + +Performance and memory efficiency are critical. +The problem involves simple repetitive tasks or loops. +You want to avoid the risk of stack overflow for deep recursion. + +## Conclusion + +Both recursion and iteration are fundamental techniques in data structures and algorithms, each with its own strengths and weaknesses. The choice between recursion and iteration depends on the specific problem at hand, the importance of performance and memory usage, and the need for code readability and simplicity. By understanding the differences and appropriate use cases for each approach, you can make informed decisions in your algorithmic implementations. + +## Resources and References + +#### Books: + + 1. "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein + 2. "Data Structures and Algorithm Analysis in C" by Mark Allen Weiss + +#### Online Courses: + + 1. Coursera: Data Structures and Algorithms Specialization + 2. edX: Algorithms and Data Structures + +#### Websites: + + 1. GeeksforGeeks + 2. LeetCode + 3. HackerRank + + Understanding and mastering both recursion and iteration will significantly enhance your ability to solve a wide range of problems in data structures and algorithms. This guide provides a comprehensive overview of the differences, advantages, and use cases for each approach, enabling you to make the best choice for your specific needs. diff --git a/docs/recursion/recursion_leetcode_questions.md b/docs/recursion/recursion_leetcode_questions.md new file mode 100644 index 000000000..3010abf3c --- /dev/null +++ b/docs/recursion/recursion_leetcode_questions.md @@ -0,0 +1,75 @@ +--- +id: recursion-leetcode-questions +title: Recursion Leetcode Questions +sidebar_label: Recursion Leetcode Questions +sidebar_position: 4 +description: "A collection of 20 Leetcode questions focused on recursion, categorized into easy, medium, and hard levels." +tags: + [ + dsa, + data-structures, + algorithms, + recursion, + leetcode, + recursion-leetcode, + recursion-leetcode-easy, + recursion-leetcode-medium, + recursion-leetcode-hard, + recursion-leetcode-questions, + ] +--- + +A collection of 20 Leetcode questions focused on recursion, categorized into easy, medium, and hard levels. + +## Easy + +1. **[Factorial](https://leetcode.com/problems/factorial)** + - Description: Calculate the factorial of a given number using recursion. +2. **[Fibonacci Number](https://leetcode.com/problems/fibonacci-number)** + - Description: Compute the n-th Fibonacci number using recursion. +3. **[Reverse String](https://leetcode.com/problems/reverse-string)** + - Description: Reverse a string using recursion. +4. **[Sum of Digits of a Number](https://leetcode.com/problems/sum-of-digits-of-a-number)** + - Description: Find the sum of digits of a given number using recursion. +5. **[Power of Three](https://leetcode.com/problems/power-of-three)** + - Description: Determine if a number is a power of three using recursion. +6. **[Climbing Stairs](https://leetcode.com/problems/climbing-stairs)** + - Description: Find the number of ways to climb a staircase using recursion. +7. **[Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list)** + - Description: Check if a linked list is a palindrome using recursion. +8. **[Maximum Depth of Binary Tree](https://leetcode.com/problems/maximum-depth-of-binary-tree)** + - Description: Find the maximum depth of a binary tree using recursion. + +## Medium + +1. **[Subsets](https://leetcode.com/problems/subsets)** + - Description: Generate all possible subsets of a set using recursion. +2. **[Permutations](https://leetcode.com/problems/permutations)** + - Description: Generate all possible permutations of a sequence using recursion. +3. **[Unique Binary Search Trees II](https://leetcode.com/problems/unique-binary-search-trees-ii)** + - Description: Generate all unique BSTs that store values 1 to n using recursion. +4. **[Word Search](https://leetcode.com/problems/word-search)** + - Description: Search for a word in a 2D grid using recursion. +5. **[Combination Sum](https://leetcode.com/problems/combination-sum)** + - Description: Find all unique combinations of numbers that sum to a target using recursion. +6. **[Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number)** + - Description: Generate all possible letter combinations that a phone number could represent using recursion. +7. **[Generate Parentheses](https://leetcode.com/problems/generate-parentheses)** + - Description: Generate all combinations of well-formed parentheses using recursion. + +## Hard + +1. **[N-Queens](https://leetcode.com/problems/n-queens)** + - Description: Solve the N-Queens problem using recursion. +2. **[Sudoku Solver](https://leetcode.com/problems/sudoku-solver)** + - Description: Solve a given Sudoku puzzle using recursion. +3. **[Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching)** + - Description: Implement regular expression matching with support for '.' and '\*' using recursion. +4. **[Longest Valid Parentheses](https://leetcode.com/problems/longest-valid-parentheses)** + - Description: Find the length of the longest valid parentheses substring using recursion. +5. **[Interleaving String](https://leetcode.com/problems/interleaving-string)** + - Description: Determine if a string is an interleaving of two other strings using recursion. + +--- + +These questions provide a comprehensive overview of recursion techniques and are a great way to practice and improve your recursion skills in data structures and algorithms. diff --git a/docs/stack/_category_.json b/docs/stack/_category_.json new file mode 100644 index 000000000..9b32456bc --- /dev/null +++ b/docs/stack/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Stack", + "position": 12, + "link": { + "type": "generated-index", + "description": "Stack is a linear data structure that follows LIFO principle" + } +} diff --git a/docs/stack/stack-using-array.md b/docs/stack/stack-using-array.md new file mode 100644 index 000000000..3dd65eb53 --- /dev/null +++ b/docs/stack/stack-using-array.md @@ -0,0 +1,244 @@ +--- +id: stack-in-dsa +title: Stack Using Array +sidebar_label: Stack Using Array +sidebar_position: 1 +description: "Stack is a linear data structure that follows LIFO principle" +tags: [dsa, data-structures, stack, LIFO] +--- + +### Introduction to Stack + +A stack is a linear data structure that follows the Last In First Out (LIFO) principle. This means that the last element added to the stack will be the first one to be removed. Stacks are used in various applications such as expression evaluation, function call management in recursion, and more. + +### 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 of the stack without removing it. +4. **isEmpty**: Check if the stack is empty. +5. **Size**: Get the number of elements in the stack. + +### Pseudocode + +#### Basic Operations + +1. **Push**: + + ```text + function push(stack, element): + stack.append(element) + ``` + +2. **Pop**: + + ```text + function pop(stack): + if isEmpty(stack): + return "Stack Underflow" + return stack.pop() + ``` + +3. **Peek**: + + ```text + function peek(stack): + if isEmpty(stack): + return "Stack is empty" + return stack[-1] + ``` + +4. **isEmpty**: + + ```text + function isEmpty(stack): + return len(stack) == 0 + ``` + +5. **Size**: + ```text + function size(stack): + return len(stack) + ``` + +### Implementation in Python, C++, and Java + +#### Python Implementation + +```python +class Stack: + def __init__(self): + self.elements = [] + + def push(self, element): + self.elements.append(element) + + def pop(self): + if self.is_empty(): + return "Stack Underflow" + return self.elements.pop() + + def peek(self): + if self.is_empty(): + return "Stack is empty" + return self.elements[-1] + + def is_empty(self): + return len(self.elements) == 0 + + def size(self): + return len(self.elements) + +# Example usage +stack = Stack() +stack.push(10) +stack.push(20) +print(stack.pop()) # Output: 20 +print(stack.peek()) # Output: 10 +print(stack.is_empty()) # Output: False +print(stack.size()) # Output: 1 +``` + +#### C++ Implementation + +```cpp +#include +#include + +class Stack { +private: + std::vector.elements; + +public: + void push(int element) { + .elements.push_back(element); + } + + int pop() { + if (isEmpty()) { + std::cerr << "Stack Underflow" << std::endl; + return -1; + } + int top =.elements.back(); + .elements.pop_back(); + return top; + } + + int peek() { + if (isEmpty()) { + std::cerr << "Stack is empty" << std::endl; + return -1; + } + return.elements.back(); + } + + bool isEmpty() { + return.elements.empty(); + } + + int size() { + return.elements.size(); + } +}; + +// Example usage +int main() { + Stack stack; + stack.push(10); + stack.push(20); + std::cout << stack.pop() << std::endl; // Output: 20 + std::cout << stack.peek() << std::endl; // Output: 10 + std::cout << std::boolalpha << stack.isEmpty() << std::endl; // Output: false + std::cout << stack.size() << std::endl; // Output: 1 + return 0; +} +``` + +#### Java Implementation + +```java +import java.util.ArrayList; + +public class Stack { + private ArrayList.elements; + + public Stack() { + .elements = new ArrayList<>(); + } + + public void push(int element) { + .elements.add(element); + } + + public int pop() { + if (isEmpty()) { + System.out.println("Stack Underflow"); + return -1; + } + return.elements.remove.elements.size() - 1); + } + + public int peek() { + if (isEmpty()) { + System.out.println("Stack is empty"); + return -1; + } + return.elements.get.elements.size() - 1); + } + + public boolean isEmpty() { + return.elements.isEmpty(); + } + + public int size() { + return.elements.size(); + } + + // Example usage + public static void main(String[] args) { + Stack stack = new Stack(); + stack.push(10); + stack.push(20); + System.out.println(stack.pop()); // Output: 20 + System.out.println(stack.peek()); // Output: 10 + System.out.println(stack.isEmpty()); // Output: false + System.out.println(stack.size()); // Output: 1 + } +} +``` + +### Complexity + +- **Time Complexity**: + + - Push: $O(1)$ + - Pop: $O(1)$ + - Peek: $O(1)$ + - isEmpty: $O(1)$ + - Size: $O(1)$ + +- **Space Complexity**: $O(n)$, where $n$ is the number of elements in the stack. + +### Example + +Consider a stack with the following operations: + +1. Push 10 +2. Push 20 +3. Pop +4. Peek +5. Check if empty +6. Get size + +**Operations**: + +- Push 10: Stack becomes [10] +- Push 20: Stack becomes [10, 20] +- Pop: Removes 20, Stack becomes [10] +- Peek: Returns 10, Stack remains [10] +- isEmpty: Returns false +- Size: Returns 1 + +### Conclusion + +A stack is a fundamental data structure used in computer science for various applications where the LIFO (Last In First Out) principle is required. It is simple to implement and provides efficient operations for adding and removing elements. Understanding and using stacks effectively can help solve many algorithmic problems. diff --git a/docs/strings/_category_.json b/docs/strings/_category_.json new file mode 100644 index 000000000..5e94df7ac --- /dev/null +++ b/docs/strings/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "String", + "position": 8, + "link": { + "type": "generated-index", + "description": "In data structures, a string is a sequence of characters used to represent text. Strings are commonly used for storing and manipulating textual data in computer programs. They can be manipulated using various operations like concatenation, substring extraction, and comparison." + } +} diff --git a/docs/strings/leetcode_practice_problems_strings.md b/docs/strings/leetcode_practice_problems_strings.md new file mode 100644 index 000000000..a9ea2bd93 --- /dev/null +++ b/docs/strings/leetcode_practice_problems_strings.md @@ -0,0 +1,31 @@ +# Practice Problems for Strings + +To further practice and test your understanding of strings, consider solving the following problems from LeetCode: + +## Easy + +1. [Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) +2. [Longest Common Prefix](https://leetcode.com/problems/longest-common-prefix/) +3. [Implement strStr()](https://leetcode.com/problems/implement-strstr/) +4. [Count and Say](https://leetcode.com/problems/count-and-say/) +5. [Reverse String](https://leetcode.com/problems/reverse-string/) +6. [First Unique Character in a String](https://leetcode.com/problems/first-unique-character-in-a-string/) +7. [Valid Anagram](https://leetcode.com/problems/valid-anagram/) +8. [Reverse Vowels of a String](https://leetcode.com/problems/reverse-vowels-of-a-string/) +9. [Detect Capital](https://leetcode.com/problems/detect-capital/) +10. [Repeated Substring Pattern](https://leetcode.com/problems/repeated-substring-pattern/) + +## Medium + +11. [Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) +12. [Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/) +13. [Group Anagrams](https://leetcode.com/problems/group-anagrams/) +14. [Decode String](https://leetcode.com/problems/decode-string/) +15. [String to Integer (atoi)](https://leetcode.com/problems/string-to-integer-atoi/) +16. [Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) +17. [Simplify Path](https://leetcode.com/problems/simplify-path/) +18. [Add Bold Tag in String](https://leetcode.com/problems/add-bold-tag-in-string/) +19. [Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) +20. [Longest Palindromic Subsequence](https://leetcode.com/problems/longest-palindromic-subsequence/) + +Engaging with these problems will help reinforce the concepts learned and provide practical experience in using strings effectively. By practicing these problems, you will enhance your problem-solving skills and deepen your understanding of string manipulation in various contexts. diff --git a/docs/strings/strings-dsa.md b/docs/strings/strings-dsa.md new file mode 100644 index 000000000..03194ab49 --- /dev/null +++ b/docs/strings/strings-dsa.md @@ -0,0 +1,360 @@ +--- +id: strings-in-dsa +title: Strings in Data Structures and Algorithms +sidebar_label: Strings +sidebar_position: 1 +description: "A string is a sequence of characters. It is a data structure that represents a sequence of characters, either as a literal constant or as some kind of variable. In data structures and algorithms, strings are used in a wide range of applications such as text processing, pattern matching, and data serialization." +tags: + [ + dsa, + data-structures, + strings, + string, + string-data-structure, + string-in-dsa, + string-in-data-structure, + string-in-algorithm, + string-in-dsa-example, + string-in-dsa-explanation, + string-in-dsa-conclusion, + string-in-dsa-importance, + string-in-dsa-syntax, + string-in-dsa-declaration, + string-in-dsa-access, + string-in-dsa-update, + string-in-dsa-length, + string-in-dsa-iterate, + string-in-dsa-max-min, + string-in-dsa-program, + string-in-dsa-code, + string-in-dsa-js, + string-in-dsa-java, + string-in-dsa-python, + string-in-dsa-c, + string-in-dsa-cpp, + string-in-dsa-ts, + ] +--- + +A string is a sequence of characters. It is a data structure that represents a sequence of characters, either as a literal constant or as some kind of variable. In data structures and algorithms, strings are used in a wide range of applications such as text processing, pattern matching, and data serialization. + +## Why are Strings important? + +Strings are important because they are used to store and manipulate text. They are used in many applications such as text processing, pattern matching, and data serialization. + +## How to declare a String? + +A string can be declared in various programming languages using the following syntax: + +# Strings in Data Structures and Algorithms (DSA) + +## Table of Contents + +- [Strings in Data Structures and Algorithms (DSA)](#strings-in-data-structures-and-algorithms-dsa) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Basic String Operations](#basic-string-operations) + - [Why are Strings important?](#why-are-strings-important-1) + - [How to declare a String?](#how-to-declare-a-string-1) + - [How to access a String?](#how-to-access-a-string) + - [How to update a String?](#how-to-update-a-string) + - [How to find the length of a String?](#how-to-find-the-length-of-a-string) + - [Pattern Matching Algorithms](#pattern-matching-algorithms) + - [String Manipulation](#string-manipulation) + - [String Data Structures](#string-data-structures) + - [Common String Problems](#common-string-problems) + - [Advanced String Algorithms](#advanced-string-algorithms) + - [Resources and References](#resources-and-references) + - [Conclusion](#conclusion) + +## Introduction + +Strings are sequences of characters and are a fundamental data type in computer science. They are used extensively in algorithms and data structures. + +## Basic String Operations + +- **Length**: Determine the length of a string. +- **Concatenation**: Combine two or more strings. +- **Substring**: Extract a portion of a string. +- **Comparison**: Compare two strings lexicographically. +- **Search**: Find the occurrence of a substring. + +## Why are Strings important? + +Strings are important because they are used to store and manipulate text. They are used in many applications such as text processing, pattern matching, and data serialization. + +## How to declare a String? + +A string can be declared in various programming languages using the following syntax: + + + + + ```js + // Declare a string in JavaScript + let str = "Hello, world!"; + ``` + + + + ```java + // Declare a string in Java + String str = "Hello, world!"; + ``` + + + + ```python + # Declare a string in Python + str = "Hello, world!" + ``` + + + + ```c + // Declare a string in C + char str[] = "Hello, world!"; + ``` + + + + ```cpp + // Declare a string in C++ + std::string str = "Hello, world!"; + ``` + + + + ```ts + // Declare a string in TypeScript + let str: string = "Hello, world!"; + ``` + + + +## How to access a String? + +A string can be accessed using the index of the character. The index of the first character is 0, the index of the second character is 1, and so on. + + + + + ```js + // Access a string in JavaScript + let str = "Hello, world!"; + console.log(str[0]); // H + console.log(str[1]); // e + console.log(str[2]); // l + ``` + + + + ```java + // Access a string in Java + String str = "Hello, world!"; + System.out.println(str.charAt(0)); // H + System.out.println(str.charAt(1)); // e + System.out.println(str.charAt(2)); // l + ``` + + + + ```python + # Access a string in Python + str = "Hello, world!" + print(str[0]) # H + print(str[1]) # e + print(str[2]) # l + ``` + + + + ```c + // Access a string in C + char str[] = "Hello, world!"; + printf("%c\n", str[0]); // H + printf("%c\n", str[1]); // e + printf("%c\n", str[2]); // l + ``` + + + + ```cpp + // Access a string in C++ + std::string str = "Hello, world!"; + std::cout << str[0] << std::endl; // H + std::cout << str[1] << std::endl; // e + std::cout << str[2] << std::endl; // l + ``` + + + + ```ts + // Access a string in TypeScript + let str: string = "Hello, world!"; + console.log(str[0]); // H + console.log(str[1]); // e + console.log(str[2]); // l + ``` + + + +## How to update a String? + +A string can be updated by creating a new string with the desired changes, as strings are immutable in many programming languages. + + + + + ```js + // Update a string in JavaScript + let str = "Hello, world!"; + str = "Hello, JavaScript!"; + console.log(str); // Hello, JavaScript! + ``` + + + + ```java + // Update a string in Java + String str = "Hello, world!"; + str = "Hello, Java!"; + System.out.println(str); // Hello, Java! + ``` + + + + ```python + # Update a string in Python + str = "Hello, world!" + str = "Hello, Python!" + print(str) # Hello, Python! + ``` + + + + ```c + // Update a string in C + char str[] = "Hello, world!"; + strcpy(str, "Hello, C!"); + printf("%s\n", str); // Hello, C! + ``` + + + + ```cpp + // Update a string in C++ + std::string str = "Hello, world!"; + str = "Hello, C++!"; + std::cout << str << std::endl; // Hello, C++! + ``` + + + + ```ts + // Update a string in TypeScript + let str: string = "Hello, world!"; + str = "Hello, TypeScript!"; + console.log(str); // Hello, TypeScript! + ``` + + + +## How to find the length of a String? + +The length of a string can be found using the `length` property or method. + + + + + ```js + // Find the length of a string in JavaScript + let str = "Hello, world!"; + console.log(str.length); // 13 + ``` + + + + ```java + // Find the length of a string in Java + String str = "Hello, world!"; + System.out.println(str.length()); // 13 + ``` + + + + ```python + # Find the length of a string in Python + str = "Hello, world!" + print(len(str)) # 13 + ``` + + + +## Pattern Matching Algorithms + +- **Naive Pattern Matching**: A straightforward approach with a time complexity of O(m\*n). +- **Knuth-Morris-Pratt (KMP)**: An efficient pattern matching algorithm with a time complexity of O(m+n). +- **Rabin-Karp Algorithm**: Uses hashing for pattern matching with a time complexity of O(m+n) on average. +- **Boyer-Moore Algorithm**: A powerful algorithm with a worst-case time complexity of O(m\*n) but performs well in practice. + +## String Manipulation + +- **Reversal**: Reversing a string. +- **Palindromes**: Checking if a string reads the same forwards and backwards. +- **Anagrams**: Checking if two strings are permutations of each other. +- **Rotation**: Rotating a string by a given number of characters. + +## String Data Structures + +- **Trie (Prefix Tree)**: A tree-like data structure that stores a dynamic set of strings, typically used for search operations. +- **Suffix Tree**: A compressed trie of all suffixes of a given string, useful for pattern matching. +- **Suffix Array**: An array of all suffixes of a string, sorted in lexicographical order. +- **Aho-Corasick Algorithm**: A trie-based data structure for multiple pattern matching. + +## Common String Problems + +- **Longest Common Substring**: Finding the longest substring that appears in two or more strings. +- **Longest Common Subsequence**: Finding the longest sequence that can be derived from two strings without changing the order of characters. +- **Edit Distance (Levenshtein Distance)**: Measuring the minimum number of single-character edits required to change one string into another. +- **String Compression**: Reducing the size of a string using algorithms like Run-Length Encoding (RLE). + +## Advanced String Algorithms + +- **Burrows-Wheeler Transform (BWT)**: A data transformation algorithm useful for data compression. +- **Manacher’s Algorithm**: An efficient algorithm to find the longest palindromic substring in linear time. +- **Z-Algorithm**: Finds occurrences of a pattern in a string in linear time. + +## Resources and References + +- **Books**: + - "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein + - "Algorithms" by Robert Sedgewick and Kevin Wayne +- **Online Courses**: + - Coursera: Data Structures and Algorithm Specialization + - edX: Algorithms and Data Structures +- **Websites**: + - [GeeksforGeeks](https://www.geeksforgeeks.org) + - [LeetCode](https://leetcode.com) + - [HackerRank](https://www.hackerrank.com) + +--- + +By understanding and mastering these string concepts and algorithms, you will be well-equipped to tackle a wide range of problems in data structures and algorithms. + +## Conclusion + +Strings are a vital data structure in the study of data structures and algorithms (DSA). They are sequences of characters used to represent text and are fundamental to various programming tasks. In this tutorial, we explored the essential operations related to strings, including declaration, access, modification, length determination, iteration, and searching in different programming languages like JavaScript, Java, Python, C, C++, and TypeScript. + +Understanding strings is crucial for solving numerous problems in computer science, from simple text manipulation to complex algorithms in text processing, pattern matching, and more. The examples provided demonstrate how to work with strings efficiently, ensuring a robust foundation for tackling more advanced DSA concepts. Mastery of strings enhances your ability to handle data and perform operations crucial in both everyday programming and competitive coding. + +Problems for Practice +To further practice and test your understanding of strings, consider solving the following problems from LeetCode: + +1. Longest Substring Without Repeating Characters +2. Valid Anagram +3. Longest Palindromic Substring +4. Group Anagrams +5. Minimum Window Substring + +Engaging with these problems will help reinforce the concepts learned and provide practical experience in using strings effectively. By practicing these problems, you will enhance your problem-solving skills and deepen your understanding of string manipulation in various contexts. diff --git a/docusaurus.config.js b/docusaurus.config.js index 61d07478d..092f9f0d3 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -42,6 +42,16 @@ const config = { ], ], + stylesheets: [ + { + href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css', + type: 'text/css', + integrity: + 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', + crossorigin: 'anonymous', + }, + ], + themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({