引言
本文来自于Google的一道题目:
how to merge two binary search tree into balanced binary search tree. how to merge two binary search tree into balanced binary search tree.. Let there be m elements in first tree and n elements in the other tree. Your merge function should take O(m+n) time and do it in place.
http://www.careercup.com/question?id=5261732222074880
要线性时间内完成,而且要求in place,不难想到把BST转换为Double linked list。
然后将两个dll merge。但是,存在线性时间复杂度的算法,把dll转化成BST吗?
下面就是本文要记述的内容,算法参考自
http://www.geeksforgeeks.org/in-place-conversion-of-sorted-dll-to-balanced-bst/
已排序的 Double linked list 转化为 BST
因为要求线性时间,所以必须保证DLL上的每个节点只被访问 consant 次,最好是一次。
既然要求每个节点只能被访问一次,那么从根节点构造肯定是不行的。这种算法让BST从叶节点开始被构造,通过灵活地运用递归,巧妙地实现了自底向上构造BST的过程,而且还能保证BST是平衡的。
#include<iostream> #include<stack> using namespace std; struct BSTNode{ int val; BSTNode *left; BSTNode *right; BSTNode(int v): val(v), left(NULL), right(NULL){} }; class BSTtoDLL{ public: BSTNode *func(BSTNode *root){ BSTtoDLLCore(root); return head; } private: BSTNode* pre = NULL; BSTNode* head = NULL; void BSTtoDLLCore(BSTNode *root){ if(!root) return; BSTtoDLLCore(root -> left); if(pre){ pre -> right = root; root -> left = pre; }else{ head = root; } pre = root; BSTtoDLLCore(root -> right); } }; class DLLtoBalancedBST{ public: BSTNode* func(BSTNode* head){ if(!head) return head; int n = 0; for(BSTNode *p = head; p; ++n, p = p -> right); return DLLtoBalancedBSTCore(&head, n); } private: //DLL to BST in place, Time O(N), Space O(LogN) for stack, N is the amount of nodes. //DLL needs to be sorted. BSTNode* DLLtoBalancedBSTCore(BSTNode** headref, int n){ if(n == 0) return NULL; BSTNode* left = DLLtoBalancedBSTCore(headref, n/2); BSTNode *root = *headref; root -> left = left; *headref = root -> right; root -> right = DLLtoBalancedBSTCore(headref, n-n/2-1); return root; } }; void InorderPrint(BSTNode* root){ if(!root) return; stack<BSTNode *> st; while(!st.empty() || root){ if(!root){ root = st.top(); st.pop(); cout << root -> val << ‘ ‘; root = root -> right; }else{ st.push(root); root = root -> left; } } cout << endl; } int main(){ //Construct oringal BST BSTNode *root = new BSTNode(5); BSTNode *left3 = new BSTNode(3); BSTNode *left1 = new BSTNode(1); BSTNode *left2 = new BSTNode(2); BSTNode *right6 = new BSTNode(6); BSTNode *right7 = new BSTNode(7); root -> left = left2; left2 -> left = left1; left2 -> right = left3; root -> right = right7; right7 -> left = right6; cout << "-------Inorder print BST-------\n"; InorderPrint(root); //Convert BST to DLL BSTtoDLL bstdll; BSTNode *head = bstdll.func(root); BSTNode *p = head; cout << "-------print converted double linked list----------\n"; for(; p->right != NULL; cout << p -> val << ‘ ‘, p = p -> right); cout << endl; for(; p != NULL; cout << p -> val << ‘ ‘, p = p -> left); cout << endl; //Convert DLL back to Balenced BST DLLtoBalancedBST dllbst; BSTNode *con_root = dllbst.func(head); cout << "-------Inorder print converted BST-------\n"; InorderPrint(con_root); return 0; }
高亮部分为转化过程。
每次递归,headaddr这个指向节点的指针向末尾移动一次。因此每个节点只被访问一次,时间复杂度是线性的。
我们可以发现,这种算法对单向链表也适用。当然单链表不能保证in place,必须新申明节点,但是时间复杂度依然是线性的。
再推而广之,对于给定一个只能向 next 移动的iterator,通过这个算法也能构造将 iterator 经过的节点构造为BST。不过我们需要存下iterator的起始位置,因为算法需要先遍历一边记下节点的数量。
下面给出单向链表上的实现。
已排序的单向Linked list 转化为BST
#include<iostream> #include<stack> using namespace std; struct ListNode{ int val; ListNode* next; ListNode(int v): val(v), next(NULL){} }; struct BSTnode{ int val; BSTnode* left; BSTnode* right; BSTnode(int v): val(v), left(NULL), right(NULL){} }; BSTnode *LLtoBSTCore(ListNode **headaddr, int n){ if(n <= 0) return NULL; BSTnode *left = LLtoBSTCore(headaddr, n/2); BSTnode *root = new BSTnode((*headaddr) -> val); root -> left = left; *headaddr = (*headaddr) -> next; root -> right = LLtoBSTCore(headaddr, n-n/2-1); return root; } BSTnode *LLtoBST(ListNode *head){ if(!head) return NULL; int n = 0; ListNode *p = head; for(; p; ++n, p = p -> next); return LLtoBSTCore(&head, n); } int main(){ ListNode *head = new ListNode(1); ListNode *end = head; for(int i = 2; i <= 9; end -> next = new ListNode(i++), end = end -> next); cout << "List: \n"; for(ListNode *p = head; p; cout << p -> val << ‘ ‘, p = p -> next); cout << endl; BSTnode *root = LLtoBST(head); cout << "BST inorder: " << endl; stack<BSTnode *> st; while(!st.empty() || root){ if(!root){ root = st.top(); st.pop(); cout << root -> val << " "; root = root -> right; }else{ st.push(root); root = root -> left; } } cout << endl; }
高亮部分为转化过程。