如何合并2个二叉搜索树,使得结果树包含两棵树的所有元素,并保持BST属性。
我看到了提供的解决方案 How to merge two BST's efficiently?
然而,该解决方案涉及转换为双重链接列表。我想知道是否有一种更优雅的方式可以在没有转换的情况下完成。我提出了以下伪代码。它适用于所有情况吗?我也遇到了第三种情况的麻烦。
node* merge(node* head1, node* head2) {
if (!head1)
return head2;
if (!head2)
return head1;
// Case 1.
if (head1->info > head2->info) {
node* temp = head2->right;
head2->right = NULL;
head1->left = merge(head1->left, head2);
head1 = merge(head1, temp);
return head1;
} else if (head1->info < head2->info) { // Case 2
// Similar to case 1.
} else { // Case 3
// ...
}
}
答案 0 :(得分:20)
在递归遍历期间,不能直接合并两个二叉搜索树(BST)。 假设我们应该合并图中所示的Tree 1和Tree 2.
递归应该将合并减少到更简单的情况。我们不能减少 因为L2可以包含,所以仅合并到相应的左子树L1和L2 数字大于10,所以我们需要包含权利 子树R1进入过程。但随后我们将数字包括在内 超过10,可能超过20,所以我们需要包括 右子树R2也是如此。类似的推理表明了这一点 我们不能通过包含Tree 1和Tree 2中的子树来简化合并 在同一时间。
减少的唯一可能性是仅在相应的树内简化。 所以,我们可以改造 树木到右边刺有排序节点:
现在,我们可以轻松地将两个棘刺合并到一个脊柱中。这个 脊柱实际上是一个BST,所以我们可以在这里停下来。但是,这个BST 是完全不平衡的,所以我们将其转换为平衡的BST。
复杂性是:
Spine 1: time = O(n1), space = O(1)
Spine 2: time = O(n2), space = O(1)
Merge: time = O(n1+n2), space = O(1)
Balance: time = O(n1+n2), space = O(1)
Total: time = O(n1+n2), space = O(1)
完整的正在运行的代码位于http://ideone.com/RGBFQ。这是必不可少的部分。顶级代码如下:
Node* merge(Node* n1, Node* n2) {
Node *prev, *head1, *head2;
prev = head1 = 0; spine(n1, prev, head1);
prev = head2 = 0; spine(n2, prev, head2);
return balance(mergeSpines(head1, head2));
}
辅助功能用于转变为刺:
void spine(Node *p, Node *& prev, Node *& head) {
if (!p) return;
spine(p->left, prev, head);
if (prev) prev->right = p;
else head = p;
prev = p;
p->left = 0;
spine(p->right, prev, head);
}
刺的合并:
void advance(Node*& last, Node*& n) {
last->right = n;
last = n;
n = n->right;
}
Node* mergeSpines(Node* n1, Node* n2) {
Node head;
Node* last = &head;
while (n1 || n2) {
if (!n1) advance(last, n2);
else if (!n2) advance(last, n1);
else if (n1->info < n2->info) advance(last, n1);
else if (n1->info > n2->info) advance(last, n2);
else {
advance(last, n1);
printf("Duplicate key skipped %d \n", n2->info);
n2 = n2->right;
}
}
return head.right;
}
平衡:
Node* balance(Node *& list, int start, int end) {
if (start > end) return NULL;
int mid = start + (end - start) / 2;
Node *leftChild = balance(list, start, mid-1);
Node *parent = list;
parent->left = leftChild;
list = list->right;
parent->right = balance(list, mid+1, end);
return parent;
}
Node* balance(Node *head) {
int size = 0;
for (Node* n = head; n; n = n->right) ++size;
return balance(head, 0, size-1);
}
答案 1 :(得分:9)
假设我们有两棵树A和B,我们将树A的根插入到树B中并使用旋转移动插入的根以成为树B的新根。接下来,我们递归地合并树A和B的左右子树。该算法考虑了两种树结构,但插入仍取决于目标树的平衡程度。您可以使用此提示在O(n+m)
时间和O(1)
空间中合并两棵树。
以下实施归因于Dzmitry Huba:
// Converts tree to sorted singly linked list and appends it
// to the head of the existing list and returns new head.
// Left pointers are used as next pointer to form singly
// linked list thus basically forming degenerate tree of
// single left oriented branch. Head of the list points
// to the node with greatest element.
static TreeNode<T> ToSortedList<T>(TreeNode<T> tree, TreeNode<T> head)
{
if (tree == null)
// Nothing to convert and append
return head;
// Do conversion using in order traversal
// Convert first left sub-tree and append it to
// existing list
head = ToSortedList(tree.Left, head);
// Append root to the list and use it as new head
tree.Left = head;
// Convert right sub-tree and append it to list
// already containing left sub-tree and root
return ToSortedList(tree.Right, tree);
}
// Merges two sorted singly linked lists into one and
// calculates the size of merged list. Merged list uses
// right pointers to form singly linked list thus forming
// degenerate tree of single right oriented branch.
// Head points to the node with smallest element.
static TreeNode<T> MergeAsSortedLists<T>(TreeNode<T> left, TreeNode<T> right, IComparer<T> comparer, out int size)
{
TreeNode<T> head = null;
size = 0;
// See merge phase of merge sort for linked lists
// with the only difference in that this implementations
// reverts the list during merge
while (left != null || right != null)
{
TreeNode<T> next;
if (left == null)
next = DetachAndAdvance(ref right);
else if (right == null)
next = DetachAndAdvance(ref left);
else
next = comparer.Compare(left.Value, right.Value) > 0
? DetachAndAdvance(ref left)
: DetachAndAdvance(ref right);
next.Right = head;
head = next;
size++;
}
return head;
}
static TreeNode<T> DetachAndAdvance<T>(ref TreeNode<T> node)
{
var tmp = node;
node = node.Left;
tmp.Left = null;
return tmp;
}
// Converts singly linked list into binary search tree
// advancing list head to next unused list node and
// returning created tree root
static TreeNode<T> ToBinarySearchTree<T>(ref TreeNode<T> head, int size)
{
if (size == 0)
// Zero sized list converts to null
return null;
TreeNode<T> root;
if (size == 1)
{
// Unit sized list converts to a node with
// left and right pointers set to null
root = head;
// Advance head to next node in list
head = head.Right;
// Left pointers were so only right needs to
// be nullified
root.Right = null;
return root;
}
var leftSize = size / 2;
var rightSize = size - leftSize - 1;
// Create left substree out of half of list nodes
var leftRoot = ToBinarySearchTree(ref head, leftSize);
// List head now points to the root of the subtree
// being created
root = head;
// Advance list head and the rest of the list will
// be used to create right subtree
head = head.Right;
// Link left subtree to the root
root.Left = leftRoot;
// Create right subtree and link it to the root
root.Right = ToBinarySearchTree(ref head, rightSize);
return root;
}
public static TreeNode<T> Merge<T>(TreeNode<T> left, TreeNode<T> right, IComparer<T> comparer)
{
Contract.Requires(comparer != null);
if (left == null || right == null)
return left ?? right;
// Convert both trees to sorted lists using original tree nodes
var leftList = ToSortedList(left, null);
var rightList = ToSortedList(right, null);
int size;
// Merge sorted lists and calculate merged list size
var list = MergeAsSortedLists(leftList, rightList, comparer, out size);
// Convert sorted list into optimal binary search tree
return ToBinarySearchTree(ref list, size);
}
答案 2 :(得分:3)
我们可以合并树木的最佳方式是:
For each node n in first BST {
Go down the 2nd tree and find the appropriate place to insert n
Insert n there
}
for循环中的每次迭代都是O(log n),因为我们正在处理树,而for循环将被迭代n次,所以我们总共有O(n log n)。
答案 3 :(得分:2)
BST是有序或排序的二叉树。我的算法很简单:
遍历的 python 代码如下:
def traverse_binary_tree(node, callback):
if node is None:
return
traverse_binary_tree(node.leftChild, callback)
callback(node.value)
traverse_binary_tree(node.rightChild, callback)
遍历BST并建立新的合并BST的成本将保持 O(n)
答案 4 :(得分:2)
This blog post提供了O(logn)空间复杂度问题的解决方案。 (注意给定的方法不会修改输入树。)
答案 5 :(得分:1)
这就是我要做的。
此解决方案的时间复杂度为O(n1 + n2)。
STEPS:
这将需要O(n1 + n2)的时间和空间。
在实施时您可能会发现有用的链接
答案 6 :(得分:0)
以下算法来自Algorithms in C++。
这个想法与PengOne发布的算法几乎相同。该算法进行了合并,时间复杂度为O(n + m)。
link join(link a, link b) {
if (b == 0) return a;
if (a == 0) return b;
insert(b, a->item);
b->left = join(a->left, b->left);
b->right = join(a->right, b->right);
delete a;
return b;
}
insert
只需在树中的正确位置插入一个项目。
void insert(link &h, Item x) {
if (h == 0) {
h = new node(x);
return;
}
if (x.key() < h->item.key()) {
insert(h->left, x);
rotateRight(h);
}
else {
insert(h->right, x);
rotateLeft(h);
}
}
rotateRight
和rotateLeft
保持树的顺序正确。
void rotateRight(link &h) {
link x = h->left;
h->left = x->right;
x->right = h;
h = x;
}
void rotateLeft(link &h) {
link x = h->right;
h->right = x->left;
x->left = h;
h = x;
}
此处link
为node *
。
答案 7 :(得分:0)
假设问题只是打印从两个BST排序。然后更简单的方法是,
复杂性:o(m + n) 辅助空间:2个数组的o(m + n)
答案 8 :(得分:0)
<强> MergeTwoBST_to_BalancedBST.java 强>
public class MergeTwoBST_to_BalancedBST {
// arr1 and arr2 are the input arrays to be converted into a binary search
// structure and then merged and then balanced.
int[] arr1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
int[] arr2 = new int[] { 11, 12, 13, 14, 15, 16, 17, 18 };
BSTNode root1;
BSTNode root2;
// vector object to hold the nodes from the merged unbalanced binary search
// tree.
Vector<BSTNode> vNodes = new Vector<BSTNode>();
/**
* Constructor to initialize the Binary Search Tree root nodes to start
* processing. This constructor creates two trees from two given sorted
* array inputs. root1 tree from arr1 and root2 tree from arr2.
*
* Once we are done with creating the tree, we are traversing the tree in
* inorder format, to verify whether nodes are inserted properly or not. An
* inorder traversal should give us the nodes in a sorted order.
*/
public MergeTwoBST_to_BalancedBST() {
// passing 0 as the startIndex and arr1.length-1 as the endIndex.
root1 = getBSTFromSortedArray(arr1, 0, arr1.length - 1);
System.out.println("\nPrinting the first binary search tree");
inorder(root1); // traverse the tree in inorder format to verify whether
// nodes are inserted correctly or not.
// passing 0 as the startIndex and arr2.length-1 as the endIndex.
root2 = getBSTFromSortedArray(arr2, 0, arr2.length - 1);
System.out.println("\nPrinting the second binary search tree");
inorder(root2); // same here - checking whether the nodes are inserted
// properly or not.
}
/**
* Method to traverse the tree in inorder format. Where it traverses the
* left child first, then root and then right child.
*
* @param node
*/
public void inorder(BSTNode node) {
if (null != node) {
inorder(node.getLeft());
System.out.print(node.getData() + " ");
inorder(node.getRight());
}
}
/**
* Method to traverse the tree in preorder format. Where it traverses the
* root node first, then left child and then right child.
*
* @param node
*/
public void preorder(BSTNode node) {
if (null != node) {
System.out.print(node.getData() + " ");
preorder(node.getLeft());
preorder(node.getRight());
}
}
/**
* Creating a new Binary Search Tree object from a sorted array and
* returning the root of the newly created node for further processing.
*
* @param arr
* @param startIndex
* @param endIndex
* @return
*/
public BSTNode getBSTFromSortedArray(int[] arr, int startIndex, int endIndex) {
if (startIndex > endIndex) {
return null;
}
int middleIndex = startIndex + (endIndex - startIndex) / 2;
BSTNode node = new BSTNode(arr[middleIndex]);
node.setLeft(getBSTFromSortedArray(arr, startIndex, middleIndex - 1));
node.setRight(getBSTFromSortedArray(arr, middleIndex + 1, endIndex));
return node;
}
/**
* This method involves two operation. First - it adds the nodes from root1
* tree to root2 tree, and hence we get a merged root2 tree.Second - it
* balances the merged root2 tree with the help of a vector object which can
* contain objects only of BSTNode type.
*/
public void mergeTwoBinarySearchTree() {
// First operation - merging the trees. root1 with root2 merging should
// give us a new root2 tree.
addUtil(root1);
System.out.println("\nAfter the root1 tree nodes are added to root2");
System.out.println("Inorder Traversal of root2 nodes");
inorder(root2); // inorder traversal of the root2 tree should display
// the nodes in a sorted order.
System.out.println("\nPreorder traversal of root2 nodes");
preorder(root2);
// Second operation - this will take care of balancing the merged binary
// search trees.
balancedTheMergedBST();
}
/**
* Here we are doing two operations. First operation involves, adding nodes
* from root2 tree to the vector object. Second operation involves, creating
* the Balanced binary search tree from the vector objects.
*/
public void balancedTheMergedBST() {
// First operation : adding nodes to the vector object
addNodesToVector(root2, vNodes);
int vSize = vNodes.size();
// Second operation : getting a balanced binary search tree
BSTNode node = getBalancedBSTFromVector(vNodes, 0, vSize - 1);
System.out
.println("\n********************************************************");
System.out.println("After balancing the merged trees");
System.out.println("\nInorder Traversal of nodes");
inorder(node); // traversing the tree in inoder process should give us
// the output in sorted order ascending
System.out.println("\nPreorder traversal of root2 nodes");
preorder(node);
}
/**
* This method will provide us a Balanced Binary Search Tree. Elements of
* the root2 tree has been added to the vector object. It is parsed
* recursively to create a balanced tree.
*
* @param vNodes
* @param startIndex
* @param endIndex
* @return
*/
public BSTNode getBalancedBSTFromVector(Vector<BSTNode> vNodes,
int startIndex, int endIndex) {
if (startIndex > endIndex) {
return null;
}
int middleIndex = startIndex + (endIndex - startIndex) / 2;
BSTNode node = vNodes.get(middleIndex);
node.setLeft(getBalancedBSTFromVector(vNodes, startIndex,
middleIndex - 1));
node.setRight(getBalancedBSTFromVector(vNodes, middleIndex + 1,
endIndex));
return node;
}
/**
* This method traverse the tree in inorder process and adds each node from
* root2 to the vector object vNodes object only accepts objects of BSTNode
* type.
*
* @param node
* @param vNodes
*/
public void addNodesToVector(BSTNode node, Vector<BSTNode> vNodes) {
if (null != node) {
addNodesToVector(node.getLeft(), vNodes);
// here we are adding the node in the vector object.
vNodes.add(node);
addNodesToVector(node.getRight(), vNodes);
}
}
/**
* This method traverse the root1 tree in inorder process and add the nodes
* in the root2 tree based on their value
*
* @param node
*/
public void addUtil(BSTNode node) {
if (null != node) {
addUtil(node.getLeft());
mergeToSecondTree(root2, node.getData());
addUtil(node.getRight());
}
}
/**
* This method adds the nodes found from root1 tree as part it's inorder
* traversal and add it to the second tree.
*
* This method follows simple Binary Search Tree inserstion logic to insert
* a node considering the tree already exists.
*
* @param node
* @param data
* @return
*/
public BSTNode mergeToSecondTree(BSTNode node, int data) {
if (null == node) {
node = new BSTNode(data);
} else {
if (data < node.getData()) {
node.setLeft(mergeToSecondTree(node.getLeft(), data));
} else if (data > node.getData()) {
node.setRight(mergeToSecondTree(node.getRight(), data));
}
}
return node;
}
/**
*
* @param args
*/
public static void main(String[] args) {
MergeTwoBST_to_BalancedBST mergeTwoBST = new MergeTwoBST_to_BalancedBST();
mergeTwoBST.mergeTwoBinarySearchTree();
}
}
<强> BSTNode.java:强>
public class BSTNode {
BSTNode left, right;
int data;
/* Default constructor */
public BSTNode() {
left = null;
right = null;
data = 0;
}
/* Constructor */
public BSTNode(int data) {
left = null;
right = null;
this.data = data;
}
public BSTNode getLeft() {
return left;
}
public void setLeft(BSTNode left) {
this.left = left;
}
public BSTNode getRight() {
return right;
}
public void setRight(BSTNode right) {
this.right = right;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
答案 9 :(得分:0)
这可以通过3个步骤完成:
- 将BST覆盖到已排序的链表中(可以在O(m + n)时间完成此操作)
- 将这两个排序的链接列表合并到一个列表中(可以在O(m + n)时间就地完成)
- 将排序后的链表转换为平衡的BST(这可以在O(m + n)时间完成)