diff --git a/dsa-solutions/Sorting-Algorithms/Cocktail-Sort.md b/dsa-solutions/Sorting-Algorithms/Cocktail-Sort.md new file mode 100644 index 000000000..28ebe7502 --- /dev/null +++ b/dsa-solutions/Sorting-Algorithms/Cocktail-Sort.md @@ -0,0 +1,146 @@ +-- +id: Cocktail-Sort +title: Cocktail Sort (Geeks for Geeks) +sidebar_label: Cocktail Sort +tags: + - Intermediate + - Sorting Algorithms + - Geeks for Geeks + - CPP + - Python + - Java + - JavaScript + - DSA +description: "This is a solution to the Cocktail Sort problem on Geeks for Geeks." +--- + +## 1. What is Cocktail Sort? + +Cocktail Sort is a variation of Bubble Sort. It traverses the list in both directions alternatively. This algorithm is also known as Bidirectional Bubble Sort or Shaker Sort. + +## 2. Algorithm for Cocktail Sort + +1. Start at the beginning of the list. +2. Traverse the list from left to right, swapping adjacent items if they are in the wrong order. +3. When the end of the list is reached, reverse the direction and traverse from right to left, again swapping adjacent items if they are in the wrong order. +4. Repeat steps 2 and 3 until the list is sorted. + +## 3. How does Cocktail Sort work? + +- It sorts in both directions in each pass through the list, which can help elements move into place faster. +- This bidirectional approach allows earlier elements to "bubble up" and later elements to "bubble down" in the same pass, potentially reducing the number of overall passes needed. + +## 4. Problem Description + +Given an array of integers, implement the Cocktail Sort algorithm to sort the array. + +## 5. Examples + +**Example 1:** + +``` +Input: [5, 1, 4, 2, 8, 0, 2] +Output: [0, 1, 2, 2, 4, 5, 8] +``` +**Example 2:** +``` +Input: [5, 1, 4, 2, 9, 8] +Output: [1, 2, 4, 5, 8, 9] +``` + +## 6. Constraints + +- The array should contain at least one element. + +## 7. Implementation + +**Python** +```python +def cocktail_sort(arr): + n = len(arr) + swapped = True + start = 0 + end = n - 1 + while swapped: + swapped = False + for i in range(start, end): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] + swapped = True + if not swapped: + break + swapped = False + end -= 1 + for i in range(end - 1, start - 1, -1): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] + swapped = True + start += 1 + return arr +``` +```java +import java.util.Arrays; + +public class CocktailSort { + public static void cocktailSort(int[] arr) { + boolean swapped = true; + int start = 0; + int end = arr.length - 1; + + while (swapped) { + swapped = false; + for (int i = start; i < end; i++) { + if (arr[i] > arr[i + 1]) { + int temp = arr[i]; + arr[i] = arr[i + 1]; + arr[i + 1] = temp; + swapped = true; + } + } + if (!swapped) break; + swapped = false; + end--; + for (int i = end - 1; i >= start; i--) { + if (arr[i] > arr[i + 1]) { + int temp = arr[i]; + arr[i] = arr[i + 1]; + arr[i + 1] = temp; + swapped = true; + } + } + start++; + } + } + + public static void main(String[] args) { + int[] arr = {5, 1, 4, 2, 8, 0, 2}; + cocktailSort(arr); + System.out.println(Arrays.toString(arr)); + } +} + +``` + +## 8. Complexity Analysis + +- **Time Complexity**: + - Best case: $O(n)$ (when the array is already sorted) + Average case: $O(n^2)$ + Worst case: $O(n^2)$ + +- **Space Complexity**: $O(1)$ (in-place sorting) + +## 9. Advantages and Disadvantages + +**Advantages:** +- Cocktail Sort can be more efficient than Bubble Sort for certain types of input because it can address issues where small elements are initially near the end of the list. +- Simple to understand and implement. + +**Disadvantages:** +- Still has a worst-case time complexity of $O(n^2)$, making it inefficient on large lists compared to more advanced algorithms like Quick Sort, Merge Sort, or Heap Sort. +- The bidirectional approach does not significantly improve performance for most input cases. + +## 10. References + +- **GFG Article on Cocktail Sort:** [Geeks for Geeks Cocktail Sort](https://www.geeksforgeeks.org/cocktail-sort/) +- **Wikipedia Article on Cocktail Sort:** [Cocktail Sort - Wikipedia](https://en.wikipedia.org/wiki/cocktail_sort) diff --git a/dsa-solutions/Sorting-Algorithms/Tree-Sort.md b/dsa-solutions/Sorting-Algorithms/Tree-Sort.md new file mode 100644 index 000000000..b5ebf2330 --- /dev/null +++ b/dsa-solutions/Sorting-Algorithms/Tree-Sort.md @@ -0,0 +1,177 @@ +-- +id: Tree-Sort +title: Tree Sort (Geeks for Geeks) +sidebar_label: Tree Sort +tags: + - Intermediate + - Sorting Algorithms + - Geeks for Geeks + - CPP + - Python + - Java + - JavaScript + - DSA +description: "This is a solution to the Tree Sort problem on Geeks for Geeks." +--- + +## 1. What is Tree Sort? + +Tree Sort is a sorting algorithm that uses a Binary Search Tree (BST) to sort elements. The elements are inserted into a BST and then an in-order traversal is performed to retrieve them in sorted order. + +## 2. Algorithm for Tree Sort + +1. Create an empty Binary Search Tree (BST). +2. Insert all elements from the array into the BST. +3. Perform an in-order traversal of the BST to retrieve the elements in sorted order. + +## 3. How does Tree Sort work? + +- Each element from the array is inserted into a BST. +- During the in-order traversal of the BST, elements are retrieved in ascending order because the left subtree is visited first, followed by the root, and then the right subtree. + +## 4. Problem Description + +Given an array of integers, implement the Tree Sort algorithm to sort the array. + +## 5. Examples + +**Example 1:** + +``` +Input: [10, 7, 8, 9, 1, 5] +Output: [1, 5, 7, 8, 9, 10] +``` + +**Example 2:** +``` +Input: [38, 27, 43, 3, 9, 82, 10] +Output: [3, 9, 10, 27, 38, 43, 82] + +``` + +## 6. Constraints + +- The array should contain at least one element. + +## 7. Implementation + +**Python** +```python +class TreeNode: + def __init__(self, key): + self.left = None + self.right = None + self.val = key + +def insert(root, key): + if root is None: + return TreeNode(key) + else: + if key < root.val: + root.left = insert(root.left, key) + else: + root.right = insert(root.right, key) + return root + +def inorder_traversal(root, res): + if root: + inorder_traversal(root.left, res) + res.append(root.val) + inorder_traversal(root.right, res) + +def tree_sort(arr): + if not arr: + return [] + root = TreeNode(arr[0]) + for key in arr[1:]: + insert(root, key) + sorted_array = [] + inorder_traversal(root, sorted_array) + return sorted_array + +``` +```java +import java.util.*; + +class TreeNode { + int val; + TreeNode left, right; + TreeNode(int item) { + val = item; + left = right = null; + } +} + +public class TreeSort { + TreeNode root; + + void insert(int key) { + root = insertRec(root, key); + } + + TreeNode insertRec(TreeNode root, int key) { + if (root == null) { + root = new TreeNode(key); + return root; + } + if (key < root.val) { + root.left = insertRec(root.left, key); + } else if (key > root.val) { + root.right = insertRec(root.right, key); + } + return root; + } + + void inorderRec(TreeNode root, List res) { + if (root != null) { + inorderRec(root.left, res); + res.add(root.val); + inorderRec(root.right, res); + } + } + + public static List treeSort(int[] arr) { + TreeSort tree = new TreeSort(); + for (int num : arr) { + tree.insert(num); + } + List sortedArray = new ArrayList<>(); + tree.inorderRec(tree.root, sortedArray); + return sortedArray; + } + + public static void main(String[] args) { + int[] arr = {5, 1, 4, 2, 8, 0, 2}; + List sortedArr = treeSort(arr); + for (int num : sortedArr) { + System.out.print(num + " "); + } + } +} + +``` + +## 8. Complexity Analysis + +- **Time Complexity**: + -Best case: $O(n \log n)$ (balanced BST) +Average case: $O(n \log n)$ (balanced BST) +Worst case: $O(n^2)$ (unbalanced BST) + +- **Space Complexity**: $O(n)$ (for the BST and recursion stack) + +## 9. Advantages and Disadvantages + +**Advantages:** +- Can achieve $O(n \log n)$ time complexity if the BST remains balanced. +- Simple to understand and implement. + +**Disadvantages:** +- In the worst case (unbalanced BST), the time complexity degrades to $O(n^2)$. +- Requires additional memory for the tree structure, which is $O(n)$. +- The bidirectional approach does not significantly improve performance for most input cases. + +## 10. References + +- **GFG Article on Tree Sort:** [Geeks for Geeks Counting Sort](https://www.geeksforgeeks.org/cartesian-tree-sorting/) +- **Wikipedia Article on Tree Sort:** [Counting Sort - Wikipedia](https://en.wikipedia.org/wiki/Tree_sort) diff --git a/dsa-solutions/lc-solutions/0000-0099/0023-merge-k-sorted-lists.md b/dsa-solutions/lc-solutions/0000-0099/0023-merge-k-sorted-lists.md index bb4977495..3c79fc976 100644 --- a/dsa-solutions/lc-solutions/0000-0099/0023-merge-k-sorted-lists.md +++ b/dsa-solutions/lc-solutions/0000-0099/0023-merge-k-sorted-lists.md @@ -1,43 +1,36 @@ --- id: merge-k-sorted-lists -title: Merge-K-Sorted-Lists (LeetCode) -sidebar_label: 0023-Merge-K-Sorted-Lists -tags: - - Linked List - - Divide and Conquer -description: "You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. -Merge all the linked-lists into one sorted linked-list and return it." +title: Merge k Sorted Lists (LeetCode) +sidebar_label: 0023-MergekSortedLists +description: Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. --- ## Problem Description -| Problem Statement | Solution Link | LeetCode Profile | -| :----------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------- | -| [Merge-K-Sorted-Lists](https://leetcode.com/problems/merge-k-sorted-lists/description/) | [Merge-K-Sorted-Lists](https://leetcode.com/problems/merge-k-sorted-lists/description/) | [DaminiChachane](https://leetcode.com/u/divcxl15/) | +| Problem Statement | Solution Link | LeetCode Profile | +| :---------------- | :------------ | :--------------- | +| [Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/) | [Merge k Sorted Lists Solution on LeetCode](https://leetcode.com/problems/merge-k-sorted-lists/solutions/) | [vaishu_1904](https://leetcode.com/u/vaishu_1904/) | -### Problem Description +## Problem Description -You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. +You are given an array of `k` linked-lists `lists`, each linked-list is sorted in ascending order. Merge all the linked-lists into one sorted linked-list and return it. - ### Examples #### Example 1 -- **Input:** ` lists = [[1,4,5],[1,3,4],[2,6]]` +- **Input:** `lists = [[1,4,5],[1,3,4],[2,6]]` - **Output:** `[1,1,2,3,4,4,5,6]` - **Explanation:** The linked-lists are: -``` -[ - 1->4->5, - 1->3->4, - 2->6 -] -``` -merging them into one sorted list: -`1->1->2->3->4->4->5->6` + `[ + 1->4->5, + 1->3->4, + 2->6 + ]` + merging them into one sorted list: + `1->1->2->3->4->4->5->6` #### Example 2 @@ -49,168 +42,149 @@ merging them into one sorted list: - **Input:** `lists = [[]]` - **Output:** `[]` - - ### Constraints -- $k == lists.length$ -- $0 \leq \text{k} \leq104$ -- $0 \leq \text{lists[i].length} \leq 500$ -- $(-10^4) \leq \text{lists[i][j]} \leq 104$ +- `k == lists.length` +- $0 <= k <= 10^4$ +- $0 <= lists[i].length <= 500$ +- $-10^4 <= lists[i][j] <= 10^4$ +- `lists[i]` is sorted in ascending order. +- The sum of `lists[i].length` will not exceed `10^4`. ### Approach -A Simple Solution is to initialize the result as the first list. Now traverse all lists starting from the second list. Insert every node of the currently traversed list into the result in a sorted way. +To merge `k` sorted linked lists, we can use a min-heap (priority queue) to efficiently get the smallest node among the current heads of the lists. This ensures that we always add the smallest element to the merged list. + +1. **Initialize the Min-Heap:** + - Add the first node of each linked list to the heap. + +2. **Build the Merged List:** + - Extract the smallest node from the heap and add it to the merged list. + - If the extracted node has a next node, add it to the heap. + - Repeat until the heap is empty. ### Solution Code #### Python -``` +```python +from heapq import heappop, heappush + class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next -class Solution: - def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: - n = len(lists) - - newList = ListNode() - new = newList - - while n: - # Find list with smallest value: - smallestNode = ListNode(float('inf')) - smallestIndex = -1 - for i in range(n): - listNode = lists[i] - if listNode and listNode.val < smallestNode.val: - smallestNode = listNode - smallestIndex = i - - if smallestIndex == -1: - break - - new.next = ListNode(smallestNode.val) - new = new.next - - if smallestNode.next: - lists[smallestIndex] = smallestNode.next - else: - lists.pop(smallestIndex) - n-=1 - - return newList.next -``` + def __lt__(self, other): + return self.val < other.val -#### Java +class Solution: + def mergeKLists(self, lists): + min_heap = [] + + # Initialize the heap + for l in lists: + if l: + heappush(min_heap, l) + + dummy = ListNode() + current = dummy + + # Extract the minimum node from the heap + while min_heap: + node = heappop(min_heap) + current.next = node + current = current.next + if node.next: + heappush(min_heap, node.next) + + return dummy.next ``` +#### Java + +```java +import java.util.PriorityQueue; + +class ListNode { + int val; + ListNode next; + ListNode(int x) { val = x; } +} + class Solution { - public ListNode merge(ListNode left, ListNode right) { - ListNode dummy = new ListNode(-1); - ListNode temp = dummy; - while (left != null && right != null) { - if (left.val < right.val) { - temp.next = left; - temp = temp.next; - left = left.next; - } else { - temp.next = right; - temp = temp.next; - right = right.next; + public ListNode mergeKLists(ListNode[] lists) { + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> a.val - b.val); + + for (ListNode list : lists) { + if (list != null) { + minHeap.offer(list); } } - while (left != null) { - temp.next = left; - temp = temp.next; - left = left.next; - } - while (right != null) { - temp.next = right; - temp = temp.next; - right = right.next; + + ListNode dummy = new ListNode(0); + ListNode current = dummy; + + while (!minHeap.isEmpty()) { + ListNode node = minHeap.poll(); + current.next = node; + current = current.next; + if (node.next != null) { + minHeap.offer(node.next); + } } + return dummy.next; } - - public ListNode mergeSort(List lists, int start, int end) { - if (start == end) { - return lists.get(start); - } - int mid = start + (end - start) / 2; - ListNode left = mergeSort(lists, start, mid); - ListNode right = mergeSort(lists, mid + 1, end); - return merge(left, right); - } - - public ListNode mergeKLists(List lists) { - if (lists.size() == 0) { - return null; - } - return mergeSort(lists, 0, lists.size() - 1); - } } + ``` #### C++ -``` -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode() : val(0), next(nullptr) {} - * ListNode(int x) : val(x), next(nullptr) {} - * ListNode(int x, ListNode *next) : val(x), next(next) {} - * }; - */ + +``` c++ +#include + +struct ListNode { + int val; + ListNode *next; + ListNode(int x) : val(x), next(nullptr) {} +}; + +struct compare { + bool operator()(ListNode* a, ListNode* b) { + return a->val > b->val; + } +}; + class Solution { public: - ListNode* merge(ListNode *left, ListNode *right) { - ListNode *dummy = new ListNode(-1); - ListNode *temp = dummy; - while (left != nullptr && right != nullptr) { - if (left -> val < right -> val) { - temp -> next = left; - temp = temp -> next; - left = left -> next; - } - else { - temp -> next = right; - temp = temp -> next; - right = right -> next; + ListNode* mergeKLists(vector& lists) { + priority_queue, compare> minHeap; + + for (ListNode* list : lists) { + if (list != nullptr) { + minHeap.push(list); } } - while (left != nullptr) { - temp -> next = left; - temp = temp -> next; - left = left -> next; - } - while (right != nullptr) { - temp -> next = right; - temp = temp -> next; - right = right -> next; + + ListNode dummy(0); + ListNode* current = &dummy; + + while (!minHeap.empty()) { + ListNode* node = minHeap.top(); + minHeap.pop(); + current->next = node; + current = current->next; + if (node->next != nullptr) { + minHeap.push(node->next); + } } - return dummy -> next; - } - ListNode* mergeSort(vector& lists, int start, int end) { - if (start == end) - return lists[start]; - int mid = start + (end - start) / 2; - ListNode *left = mergeSort(lists, start, mid); - ListNode *right = mergeSort(lists, mid + 1, end); - return merge(left, right); - } - ListNode* mergeKLists(vector& lists) { - if (lists.size() == 0) - return nullptr; - return mergeSort(lists, 0, lists.size() - 1); + + return dummy.next; } }; -``` -### Conclusion - -Merging k sorted lists efficiently requires leveraging data structures that help maintain order, such as a min-heap. The min-heap approach is particularly efficient with a time complexity of $𝑂(𝑁log𝑘)$. Understanding these methods not only helps in solving the problem optimally but also enhances the comprehension of advanced data structures and algorithms. +``` +#### Conclusion +The above solutions effectively merge k sorted linked lists into a single sorted list using a min-heap. This approach ensures that the smallest element is always added next to the merged list, maintaining sorted order. This solution efficiently handles edge cases and returns the correct merged list for various input configurations.