From 3ca39d47f803dbf5ba2926cbb3e9750823ffc7ad Mon Sep 17 00:00:00 2001 From: mustafa ansari Date: Sun, 25 Aug 2024 12:56:04 +0530 Subject: [PATCH] some changes --- Array problems/Dequeue/deque.py | 108 +++++++ BST/.idea/material_theme_project_new.xml | 4 +- Greedy/greedy.ipynb | 374 ++++++++++++++++++++--- 3 files changed, 435 insertions(+), 51 deletions(-) diff --git a/Array problems/Dequeue/deque.py b/Array problems/Dequeue/deque.py index e69de29b..c0e6255b 100644 --- a/Array problems/Dequeue/deque.py +++ b/Array problems/Dequeue/deque.py @@ -0,0 +1,108 @@ +class Node: + def __init__(self, value): + self.value = value + self.next = None + self.prev = None + + +class Deque: + def __init__(self): + self.head = None + self.tail = None + self.size = 0 + + def is_empty(self): + return self.size == 0 + + def append(self, value): + new_node = Node(value) + if self.tail is None: # Deque is empty + self.head = self.tail = new_node + else: + self.tail.next = new_node + new_node.prev = self.tail + self.tail = new_node + self.size += 1 + + def appendleft(self, value): + new_node = Node(value) + if self.head is None: # Deque is empty + self.head = self.tail = new_node + else: + new_node.next = self.head + self.head.prev = new_node + self.head = new_node + self.size += 1 + + def pop(self): + if self.is_empty(): + raise IndexError("pop from an empty deque") + + value = self.tail.value + if self.head == self.tail: # Only one element in the deque + self.head = self.tail = None + else: + self.tail = self.tail.prev + self.tail.next = None + + self.size -= 1 + return value + + def popleft(self): + if self.is_empty(): + raise IndexError("pop from an empty deque") + + value = self.head.value + if self.head == self.tail: # Only one element in the deque + self.head = self.tail = None + else: + self.head = self.head.next + self.head.prev = None + + self.size -= 1 + return value + + def peek(self): + if self.is_empty(): + return None + return self.tail.value + + def peekleft(self): + if self.is_empty(): + return None + return self.head.value + + def __len__(self): + return self.size + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.value) + current = current.next + return "Deque: " + " <-> ".join(map(str, elements)) + + +# Example usage +if __name__ == "__main__": + deque = Deque() + + deque.append(10) + deque.append(20) + deque.appendleft(5) + deque.appendleft(1) + + print(deque) # Deque: 1 <-> 5 <-> 10 <-> 20 + + print(deque.pop()) # 20 + print(deque.popleft()) # 1 + + print(deque) # Deque: 5 <-> 10 + + deque.append(30) + deque.appendleft(2) + + print(deque) # Deque: 2 <-> 5 <-> 10 <-> 30 + print("Peek:", deque.peek()) # Peek: 30 + print("Peek Left:", deque.peekleft()) # Peek Left: 2 diff --git a/BST/.idea/material_theme_project_new.xml b/BST/.idea/material_theme_project_new.xml index e4a9d4ac..01b215fb 100644 --- a/BST/.idea/material_theme_project_new.xml +++ b/BST/.idea/material_theme_project_new.xml @@ -3,7 +3,9 @@ diff --git a/Greedy/greedy.ipynb b/Greedy/greedy.ipynb index 234a08a5..e5c1c033 100644 --- a/Greedy/greedy.ipynb +++ b/Greedy/greedy.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -21,7 +21,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -40,17 +40,9 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "240.0\n" - ] - } - ], + "outputs": [], "source": [ "# Driver Code\n", "if __name__ == \"__main__\":\n", @@ -64,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -88,17 +80,9 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "240.0\n" - ] - } - ], + "outputs": [], "source": [ "if __name__ == \"__main__\":\n", "\tW = 50\n", @@ -120,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -140,17 +124,9 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "74\n" - ] - } - ], + "outputs": [], "source": [ "if __name__==\"__main__\":\n", " job=[[\"j1\",3,1],\n", @@ -168,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -209,18 +185,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of jobs done: 3\n", - "Total profit: 142\n" - ] - } - ], + "outputs": [], "source": [ "# Example usage\n", "if __name__ == \"__main__\":\n", @@ -247,12 +214,236 @@ "#### A well-known Greedy algorithm is Huffman Coding. The size of code allocated to a character relies on the frequency of the character, which is why it is referred to be a greedy algorithm. The short-length variable code is assigned to the character with the highest frequency, and vice versa for characters with lower frequencies. It employs a variable-length encoding, which means that it gives each character in the provided data stream a different variable-length code." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Huffman Encoding\n", + "\n", + "Huffman encoding is a lossless data compression algorithm used to minimize the average length of codes assigned to symbols based on their frequencies. The idea is to use shorter codes for more frequent symbols and longer codes for less frequent symbols, thereby reducing the overall size of the encoded data.\n", + "\n", + "### Steps to Perform Huffman Encoding\n", + "\n", + "#### 1. **Frequency Analysis**\n", + " - Calculate the frequency of each character in the input data.\n", + " - Example: For the string `\"ABRACADABRA\"`, the frequency of characters would be:\n", + " - A: 5\n", + " - B: 2\n", + " - R: 2\n", + " - C: 1\n", + " - D: 1\n", + "\n", + "#### 2. **Build a Min-Heap**\n", + " - Create a min-heap (a priority queue) where each node is a character and its frequency.\n", + " - Each node is represented as a tuple `(frequency, character)`.\n", + "\n", + "#### 3. **Build the Huffman Tree**\n", + " - While there is more than one node in the heap:\n", + " 1. Extract the two nodes with the smallest frequencies from the heap.\n", + " 2. Create a new internal node with these two nodes as children. The frequency of the new node is the sum of the two nodes' frequencies.\n", + " 3. Insert the new node back into the heap.\n", + " - The remaining node in the heap is the root of the Huffman Tree.\n", + "\n", + "#### 4. **Generate Huffman Codes**\n", + " - Traverse the Huffman Tree to generate the codes for each character:\n", + " - Assign `0` for a left branch and `1` for a right branch.\n", + " - Continue this assignment until each leaf node (character) is reached.\n", + "\n", + "#### 5. **Encoding the Data**\n", + " - Replace each character in the input data with its corresponding Huffman code.\n", + "\n", + "#### Example of Huffman Encoding\n", + "\n", + "Let’s encode the string `\"ABRACADABRA\"` using Huffman encoding.\n", + "\n", + "1. **Frequency Analysis:**\n", + "\n", + " ```\n", + " A: 5\n", + " B: 2\n", + " R: 2\n", + " C: 1\n", + " D: 1\n", + " ```\n", + "\n", + "2. **Building the Min-Heap:**\n", + "\n", + " Initially, the min-heap contains the characters with their frequencies:\n", + "\n", + " ```\n", + " [(1, 'C'), (1, 'D'), (2, 'R'), (2, 'B'), (5, 'A')]\n", + " ```\n", + "\n", + "3. **Building the Huffman Tree:**\n", + "\n", + " - Extract `C` and `D`, and combine them into a new node: `CD` with frequency `2`.\n", + " - Heap now: `[(2, 'CD'), (2, 'R'), (5, 'A'), (2, 'B')]`\n", + " - Extract `CD` and `R`, and combine them into a new node: `CDR` with frequency `4`.\n", + " - Heap now: `[(2, 'B'), (5, 'A'), (4, 'CDR')]`\n", + " - Extract `B` and `CDR`, and combine them into a new node: `BCDR` with frequency `6`.\n", + " - Heap now: `[(5, 'A'), (6, 'BCDR')]`\n", + " - Extract `A` and `BCDR`, and combine them into a new node: `ABCDR` with frequency `11`.\n", + "\n", + " The final Huffman Tree looks like this:\n", + "\n", + " ```\n", + " (11)\n", + " / \\\n", + " A(5) (6)\n", + " / \\\n", + " B(2) (4)\n", + " / \\\n", + " R(2) (2)\n", + " / \\\n", + " C(1) D(1)\n", + " ```\n", + "\n", + "4. **Generating Huffman Codes:**\n", + "\n", + " - Traverse the tree:\n", + " - `A`: `0`\n", + " - `B`: `10`\n", + " - `R`: `110`\n", + " - `C`: `1110`\n", + " - `D`: `1111`\n", + "\n", + " The Huffman codes are:\n", + " ```\n", + " A: 0\n", + " B: 10\n", + " R: 110\n", + " C: 1110\n", + " D: 1111\n", + " ```\n", + "\n", + "5. **Encoding the Data:**\n", + "\n", + " Replace each character in `\"ABRACADABRA\"` with its Huffman code:\n", + "\n", + " ```\n", + " \"ABRACADABRA\" -> 0 10 110 0 1110 0 1111 0 10 0 110\n", + " ```\n", + "\n", + " So, the encoded data is:\n", + " ```\n", + " 01011001110011110100110\n", + " ```\n", + "\n", + "### Complexity Analysis\n", + "\n", + "- **Time Complexity:**\n", + " - Building the Huffman Tree: `O(n log n)`, where `n` is the number of unique characters (due to the heap operations).\n", + " - Generating the codes: `O(n)`.\n", + "\n", + "- **Space Complexity:**\n", + " - The space complexity is `O(n)`, where `n` is the number of unique characters.\n", + "\n", + "### Conclusion\n", + "\n", + "Huffman encoding is highly efficient for compressing data, especially when there are significant differences in the frequencies of characters. It is widely used in applications like ZIP compression and multimedia codecs. The key benefit of Huffman encoding is its ability to reduce the amount of data required to represent the input without losing any information." + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 91, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original text: ABRACADABRA\n", + "Encoded text: 01111100100010101111100\n", + "Huffman Codes: {'A': '0', 'C': '100', 'D': '101', 'R': '110', 'B': '111'}\n", + "Decoded text: ABRACADABRA\n" + ] + } + ], + "source": [ + "import heapq\n", + "from collections import defaultdict, Counter\n", + "\n", + "# Node class for the Huffman Tree\n", + "class Node:\n", + " def __init__(self, freq, char=None, left=None, right=None):\n", + " self.freq = freq\n", + " self.char = char\n", + " self.left = left\n", + " self.right = right\n", + " \n", + " # Define comparators for heapq to compare Node objects based on frequency\n", + " def __lt__(self, other):\n", + " return self.freq < other.freq\n", + "\n", + "# Function to build the Huffman Tree\n", + "def build_huffman_tree(frequency):\n", + " heap = []\n", + " for char, freq in frequency.items():\n", + " heapq.heappush(heap, Node(freq, char))\n", + " \n", + " while len(heap) > 1:\n", + " left = heapq.heappop(heap)\n", + " right = heapq.heappop(heap)\n", + " merged = Node(left.freq + right.freq, None, left, right)\n", + " heapq.heappush(heap, merged)\n", + " \n", + " return heapq.heappop(heap)\n", + "\n", + "# Function to generate the Huffman codes\n", + "def generate_codes(root, current_code=\"\", codes={}):\n", + " if root is None:\n", + " return\n", + " \n", + " if root.char is not None:\n", + " codes[root.char] = current_code\n", + " \n", + " generate_codes(root.left, current_code + \"0\", codes)\n", + " generate_codes(root.right, current_code + \"1\", codes)\n", + " \n", + " return codes\n", + "\n", + "# Function to encode the input text using the generated Huffman codes\n", + "def huffman_encode(text):\n", + " # Calculate frequency of each character\n", + " frequency = Counter(text)\n", + " \n", + " # Build Huffman Tree\n", + " root = build_huffman_tree(frequency)\n", + " \n", + " # Generate Huffman codes\n", + " codes = generate_codes(root)\n", + " \n", + " # Encode the text\n", + " encoded_text = \"\".join([codes[char] for char in text])\n", + " \n", + " return encoded_text, codes\n", + "\n", + "# Function to decode the encoded text using the Huffman Tree\n", + "def huffman_decode(encoded_text, codes):\n", + " reversed_codes = {v: k for k, v in codes.items()}\n", + " current_code = \"\"\n", + " decoded_text = []\n", + " \n", + " for bit in encoded_text:\n", + " current_code += bit\n", + " if current_code in reversed_codes:\n", + " decoded_text.append(reversed_codes[current_code])\n", + " current_code = \"\"\n", + " \n", + " return \"\".join(decoded_text)\n", + "\n", + "# Example usage\n", + "if __name__ == \"__main__\":\n", + " text = \"ABRACADABRA\"\n", + " print(\"Original text:\", text)\n", + " \n", + " encoded_text, codes = huffman_encode(text)\n", + " print(\"Encoded text:\", encoded_text)\n", + " print(\"Huffman Codes:\", codes)\n", + " \n", + " decoded_text = huffman_decode(encoded_text, codes)\n", + " print(\"Decoded text:\", decoded_text)\n" + ] }, { "cell_type": "markdown", @@ -260,6 +451,89 @@ "source": [ "### 5. Optimal Merge Patterng" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "The Optimal Merge Pattern is a classic problem in computer science, commonly encountered in file merging scenarios. The goal is to merge files with minimal computational cost, typically measured in terms of the number of comparisons needed.\n", + "\n", + "#### Problem Statement\n", + "Given a list of files with different sizes, the objective is to find the minimum cost required to merge all the files into a single file. The cost of merging two files is equal to the sum of their sizes.\n", + "\n", + "#### Steps to Solve the Problem\n", + "\n", + "1. **Understand the Input:**\n", + " - You have `n` files, each with a size represented as an integer.\n", + " - Example: `[5, 10, 15, 20]` where the numbers represent file sizes.\n", + "\n", + "2. **Min-Heap Approach:**\n", + " - **Step 1:** Insert all file sizes into a min-heap (a priority queue that supports extracting the minimum element efficiently).\n", + " - **Step 2:** Repeat until only one file remains in the heap:\n", + " 1. Extract the two smallest files from the heap.\n", + " 2. Merge them into a single file.\n", + " 3. Insert the resulting file back into the heap.\n", + " 4. Keep a running total of the cost incurred.\n", + "\n", + "#### Example\n", + "Let's consider an example with file sizes `[5, 10, 15, 20]`.\n", + "\n", + "1. Insert all sizes into the min-heap: `[5, 10, 15, 20]`.\n", + "2. Merge the two smallest files (5 and 10): Cost = 5 + 10 = 15. New heap: `[15, 15, 20]`.\n", + "3. Merge the next two smallest files (15 and 15): Cost = 15 + 15 = 30. New heap: `[20, 30]`.\n", + "4. Merge the remaining two files (20 and 30): Cost = 20 + 30 = 50. New heap: `[50]`.\n", + "\n", + "Total Cost = 15 + 30 + 50 = **95**.\n", + "\n", + "#### Complexity Analysis\n", + "\n", + "- **Time Complexity:**\n", + " - The time complexity of the Optimal Merge Pattern is `O(n log n)`, where `n` is the number of files.\n", + " - Inserting an element into a min-heap takes `O(log n)` time, and since we are performing this operation `n-1` times, the overall complexity is `O(n log n)`.\n", + "\n", + "- **Space Complexity:**\n", + " - The space complexity is `O(n)` due to the space needed to store the heap.\n", + "\n", + "#### Graphical Representation\n", + "\n", + "- The process can be represented as a tree, where each merge operation is a node, and the leaves are the original files.\n", + "\n", + "```plaintext\n", + " 95\n", + " / \\\n", + " 30 50\n", + " / \\ / \\\n", + " 15 15 20 30\n", + " / \\ / \\\n", + "5 10 15 15\n", + "```\n", + "\n", + "In this tree:\n", + "- The leaf nodes represent the original file sizes.\n", + "- The internal nodes represent the cost of merging the files.\n", + "- The root node gives the total cost of merging all files.\n", + "\n", + "#### Conclusion\n", + "The Optimal Merge Pattern is an efficient way to minimize the cost of merging multiple files. It utilizes a greedy approach by always merging the two smallest files first, ensuring that the total cost is as low as possible. The use of a min-heap makes this process computationally efficient.\n", + "\n", + "This pattern is not only relevant in file merging but also in other scenarios such as optimal binary search tree construction and Huffman coding." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Minimum Spaning Tree\n", + "**A Minimum Spanning Tree (MST) is a subset of the edges of a connected, undirected graph that connects all the vertices together, without any cycles, and with the minimum possible total edge weight. Two commonly used algorithms to find the MST are Kruskal's Algorithm and Prim's Algorithm.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -278,7 +552,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.12.3" } }, "nbformat": 4,