Merge2BST LinkedList
#include <bits/stdc++.h> // Includes common standard libraries (iostream, vector, etc.)#include "BST.h" // Assumed to contain Node class, takeinput, levelOrderTraversal
using namespace std; // Use the standard namespace
// Note: The Node class, takeinput, and levelOrderTraversal// are assumed to be defined in "BST.h"./*class Node {public: int data; Node* left; Node* right; Node(int data) { // Constructor for Node this->data = data; this->left = NULL; this->right = NULL; }};
void takeinput(Node* &root) { ... }void levelOrderTraversal(Node* root) { ... }*/
// Function to flatten a BST into a sorted Doubly Linked List (DLL) in-place.// Uses reverse in-order traversal (Right -> Root -> Left).// 'head' will point to the current start of the DLL (the smallest element added so far).// Time Complexity: O(N)// Space Complexity: O(H) for recursion stack (H = height of BST)void flattenBST(Node* root, Node* &head) { // Base case: If current node is NULL, return. if(root == NULL) { return; }
// 1. Recursively flatten the right subtree. When this call returns, // 'head' points to the DLL formed from the right subtree. flattenBST(root->right, head);
// 2. Process the current 'root' node. // Link current 'root's right pointer to the head of the DLL formed so far (its successor). root->right = head;
// If there's a successor (head is not NULL), link its left pointer back to the current 'root'. if(head != NULL) { head->left = root; }
// Update 'head' to be the current 'root'. 'root' is now the new smallest element in our DLL. head = root;
// 3. Recursively flatten the left subtree. These elements will be prepended to 'head'. flattenBST(root->left, head);}
// Function to merge two sorted Doubly Linked Lists into a single sorted DLL.// Time Complexity: O(N1 + N2)// Space Complexity: O(1) (in-place merging)Node* mergeLinkedhead(Node* head1, Node* head2) { Node* head = NULL; // Head of the merged DLL Node* tail = NULL; // Tail of the merged DLL
// Debugging print statements - remove in production code // cout << "\nBEFORE FIRST WHILE\n";
// Merge until one of the lists is exhausted. while(head1 != NULL && head2 != NULL) { if(head1->data < head2->data) { // If merged list is empty, current node becomes head and tail. if(head == NULL) { head = tail = head1; } else { // Append to the tail of the merged list. tail->right = head1; head1->left = tail; tail = head1; } head1 = head1->right; // Advance head1 } else { // Similar logic for head2's smaller data. if(head == NULL) { head = tail = head2; } else { tail->right = head2; head2->left = tail; tail = head2; } head2 = head2->right; // Advance head2 } }
// Debugging print statements - remove in production code // cout << "\nAFTER FIRST WHILE\n";
// Append remaining nodes from head1 (if any). while(head1 != NULL) { tail->right = head1; head1->left = tail; tail = head1; head1 = head1->right; }
// Append remaining nodes from head2 (if any). while(head2 != NULL) { tail->right = head2; head2->left = tail; tail = head2; head2 = head2->right; }
return head; // Return the head of the merged DLL.}
// Function to count the number of nodes in a Doubly Linked List.// Time Complexity: O(N)// Space Complexity: O(1)int countNodes(Node* head) { int cnt = 0; Node* temp = head; while(temp != NULL) { cnt++; temp = temp->right; // Traverse using the 'right' (next) pointer. } return cnt;}
// Function to convert a sorted Doubly Linked List into a balanced BST.// The provided active implementation (using 'root = head; while(temp--)') is problematic.// The commented-out code below represents the standard correct and efficient approach.// Time Complexity: O(N)// Space Complexity: O(log N) for recursion stack (for balanced BST)Node* LL_2_BST(Node* &head, int n) { // Head must be passed by reference! // Base case: No nodes or invalid count. if(n <= 0 || head == NULL) { return NULL; }
// Recursively build the left subtree. // 'head' is advanced implicitly as nodes are consumed for the left subtree. Node* leftChild = LL_2_BST(head, n/2); // Corrected parameter name
// The current 'head' now points to the root of the current subtree. Node* root = head; root->left = leftChild; // Link the built left subtree.
// Advance 'head' past the current root node, for the right subtree. head = head->right;
// Recursively build the right subtree. // 'n - n/2 - 1' is the number of nodes remaining for the right subtree. root->right = LL_2_BST(head, n - n/2 - 1);
return root; // Return the root of the current balanced subtree.
// Original provided active code (potentially problematic for 'head' advancement): // int temp = n/2; // Node* root = head; // while(temp--) { // root = root->right; // } // root->left = LL_2_BST(head, n/2); // 'head' is not properly advanced for this call // root->right = LL_2_BST(root->right, n-n/2-1); // return root; // Note: The commented-out correct logic above requires 'head' to be updated recursively. // The active code's 'head' parameter is passed by value, not reference.}
// Main function to merge two BSTs into a single balanced BST using the DLL approach.// Time Complexity: O(N1 + N2)// Space Complexity: O(H_original) due to flattenBST, then O(log N) for LL_2_BST.Node* MergeBST(Node* root1, Node* root2) { // Step 1: Convert both BSTs into sorted Doubly Linked Lists. Node* head1 = NULL; // Head of DLL from BST1 Node* head2 = NULL; // Head of DLL from BST2
flattenBST(root1, head1); // Flatten BST1 into DLL head1 flattenBST(root2, head2); // Flatten BST2 into DLL head2
// Crucial step: Set 'left' pointer of the first node in each DLL to NULL, // as flattenBST might leave it pointing to an old parent from the BST structure. if (head1 != NULL) head1->left = NULL; if (head2 != NULL) head2->left = NULL;
// Step 2: Merge the two sorted DLLs into a single sorted DLL. Node* mergedListHead = mergeLinkedhead(head1, head2);
// Step 3: Convert the merged sorted DLL into a balanced BST. // First, count the total number of nodes in the merged DLL. int totalNodes = countNodes(mergedListHead); // Then, call LL_2_BST to build the balanced BST. Node* result = LL_2_BST(mergedListHead, totalNodes); // Use the correct DLL-to-BST conversion
return result; // Return the root of the newly merged and balanced BST.}
int main() { Node* root1 = NULL; // Initialize root of the first BST Node* root2 = NULL; // Initialize root of the second BST
cout << "Enter data to create BST 1 (e.g., 50 40 60 70 -1) : "; takeinput(root1); // Build BST 1
cout << "Enter data to create BST 2 (e.g., 55 45 35 65 47 -1) : "; takeinput(root2); // Build BST 2
cout << endl << "Level Order Traversal of BST 1 : " << endl; levelOrderTraversal(root1); // Print BST 1
cout << endl << "Level Order Traversal of BST 2 : " << endl; levelOrderTraversal(root2); // Print BST 2
// Call the MergeBST function to get the merged and balanced BST. Node* result = MergeBST(root1, root2);
// Print the level order traversal of the final merged BST from main. cout << endl << "Level Order Traversal of final merged BST (from main) : " << endl; levelOrderTraversal(result);
return 0;}
/*Example Inputs (from previous prompt, still valid for this method):BST 1: 50 40 60 70 -1BST 2: 55 45 35 65 47 -1
Expected Merged Sorted Array/DLL:35, 40, 45, 47, 50, 55, 60, 65, 70
Expected Merged and Balanced BST (Level Order Traversal):(Middle element of merged array/DLL is 50, so it's the root) 50 / \ 40 60 / \ / \ 35 45 55 70 / / 47 65
Output of Level Order Traversal:50 40 60 35 45 55 70 47 65*/
1. Binary Search Tree (BST) & Doubly Linked List (DLL) Recap
- BST: Left children are smaller, right children are larger. In-order traversal gives sorted elements.
- DLL: Each node has pointers to both the next (
right
) and previous (left
) nodes.
2. Core Idea for Merging Two BSTs (DLL Approach)
This method aims to be more space-efficient than converting to arrays by performing transformations in-place (or by reusing node memory), conceptually:
- Convert BST to Sorted Doubly Linked List (DLL): Transform each BST into a sorted DLL. The
right
pointer acts asnext
andleft
pointer acts asprev
. This is done in-place. - Merge Two Sorted DLLs: Combine the two sorted DLLs into a single, larger sorted DLL.
- Convert Sorted DLL to Balanced BST: Transform the merged sorted DLL back into a balanced BST.
3. Supporting Components (Briefly)
Node
class: Defines the structure of a BST node (data
,left
,right
). For DLL,left
acts asprev
,right
asnext
.takeinput
,levelOrderTraversal
: Functions (assumed inBST.h
) to build and visualize BSTs.
4. flattenBST(Node* root, Node* &head)
Function
- Purpose: Converts a BST into a sorted Doubly Linked List (DLL) in-place.
- Key Idea: Uses a Reverse In-order Traversal (
Right -> Root -> Left
). As we visit a node, we link it to thehead
of the DLL built so far, and then updatehead
to be the current node. This builds the DLL from right (largest) to left (smallest). - Parameters:
Node* root
: Current node in BST traversal.Node* &head
: A reference to the head of the DLL being built. It’s updated in each recursive call to point to the new smallest element added.
- Logic:
- Base Case: If
root == NULL
, return. - Recurse Right:
flattenBST(root->right, head);
First, flatten the right subtree. When this call returns,head
will point to the largest element processed from the right subtree. - Process Current Node (Root):
root->right = head;
: Link currentroot
’sright
pointer to thehead
of the DLL formed by elements larger thanroot
.if (head != NULL) { head->left = root; }
: Ifhead
isn’tNULL
(meaning there’s a successor), link itsleft
pointer back to the currentroot
.head = root;
: Update thehead
of the DLL to be the currentroot
.root
is now the new “smallest” element in the constructed part of the DLL.
- Recurse Left:
flattenBST(root->left, head);
Flatten the left subtree. These elements will be prepended to thehead
.
- Base Case: If
- Important Post-flattening: After
flattenBST
returns, theleft
pointer of the very first node in the DLL (the globally smallest element) might not beNULL
due to its original BST parent links. It must be set toNULL
explicitly (head1->left = NULL; head2->left = NULL;
) to form a proper DLL.
5. mergeLinkedhead(Node* head1, Node* head2)
Function
- Purpose: Merges two already sorted Doubly Linked Lists (
head1
,head2
) into a single sorted DLL. - Logic: A standard iterative merge process similar to the merge step in Merge Sort, adapted for DLLs.
- Initialize
head = NULL
andtail = NULL
for the merged list. - Iterate
while (head1 != NULL && head2 != NULL)
:- Compare
head1->data
andhead2->data
. - Select the node with the smaller data (let’s call it
chosen_node
). - If
head
isNULL
(first node of merged list), sethead = tail = chosen_node
. - Else, link
tail->right = chosen_node; chosen_node->left = tail; tail = chosen_node;
. - Advance the pointer of the list from which
chosen_node
was taken.
- Compare
- After the loop, one list might have remaining nodes. Append these remaining nodes to the
tail
of the merged list, updatingleft
andright
pointers accordingly. - Return the
head
of the fully merged DLL.
- Initialize
6. countNodes(Node* head)
Function
- Purpose: A simple utility function to count the number of nodes in a DLL.
- Logic: Traverses the DLL using
temp->right
untilNULL
is reached, incrementing a counter.
7. LL_2_BST(Node* head, int n)
Function
- Purpose: Converts a sorted Doubly Linked List into a balanced BST.
- The Provided Code’s Logic: The active code in your example directly calculates a middle node based on
n/2
steps fromhead
and then recursively tries to build left and right subtrees. This approach is prone to errors because thehead
pointer is not passed by reference and updated across recursive calls, meaning the left subtree might consume nodes that the right subtree also expects. - The Correct/Standard Efficient Logic (Commented in your code):
// Node* left = LL_2_BST(head, n/2); // Build left subtree, 'head' is updated// Node* root = head; // Current head is the root// root->left = left; // Link left child// head = head->right; // Advance head for the right subtree// root->right = LL_2_BST(head, n-n/2-1); // Build right subtree// return root;
- This standard approach passes
head
by reference (Node* &head
) to ensure it’s globally updated as nodes are consumed. - It recursively builds the left subtree, which advances the
head
pointer past those nodes. - Then, the current
head
(which is now the middle element) becomes the root. head
is advanced one more time for the root node itself.- Finally, the right subtree is built from the remaining list.
- For your provided code, the active
LL_2_BST
function is likely incorrect and will not produce a valid BST or will have issues with node consumption. The commented-out section shows the standard correct implementation.
- This standard approach passes
8. MergeBST(Node* root1, Node* root2)
Function
- Purpose: Orchestrates the entire process of merging two BSTs using the DLL-based approach.
- Logic Flow:
- Flatten BSTs: Call
flattenBST
forroot1
to gethead1
and forroot2
to gethead2
. - Correct DLL Heads: Set
head1->left = NULL;
andhead2->left = NULL;
to ensure proper DLL structure. - Merge DLLs: Call
mergeLinkedhead(head1, head2)
to getlist
, the single sorted DLL. - Count Nodes: Call
countNodes(list)
to get the total number of nodesn
in the merged DLL. - Convert DLL to BST: Call
LL_2_BST(list, n)
to transform thelist
into a balanced BST. (Using the correctLL_2_BST
logic is critical here). - Return the root of the final balanced BST.
- Flatten BSTs: Call
9. Complexity Analysis
-
Let
N1
be nodes inBST1
andN2
inBST2
.N = N1 + N2
. -
Time Complexity:
O(N)
flattenBST
:O(N1)
+O(N2)
=O(N)
(each node visited once).mergeLinkedhead
:O(N1 + N2)
=O(N)
(standard merge).countNodes
:O(N)
(traverses entire list).LL_2_BST
:O(N)
(each node visited and processed once to form BST).- Total:
O(N)
.
-
Space Complexity:
O(log N)
orO(H_original)
(dominated by recursion stack)flattenBST
:O(H_original)
for recursion stack (can beO(N)
in worst case for skewed BST).mergeLinkedhead
:O(1)
(in-place modification of existing nodes).LL_2_BST
:O(log N)
for recursion stack (for building a balanced BST from DLL, orO(N)
if building a skewed one, but here we aim for balanced).- Overall, the space complexity for extra memory used is O(log N) if
flattenBST
’s stack is considered for a balanced input tree, or O(N) in worst case for skewed input trees. The key advantage is avoiding an O(N) array storage like the previous method.