I was writing a function to delete a node in binary tree built on key value of each node and satisfies search property.
it turns out that when the node has both childeren non-empty i have to swap the node with his inorder successer.
for small data in nodes i can swap two nodes by swapping the data and key values. but my node contain huge data.is there any logic to do better becoz i dont want to swap all the data b/w nodes.thnx in advance.
If you are worried about the size of data to be swapped, then it is not an issue. When we swap nodes, we only swap the addresses and not the actual data. So this is a non-issue.
Related
Given an array of binary trees find whether any two trees share a node, not value wise, but "pointer" wise. At the bottom I provided an example.
My approach was to iterate through all the trees and store all the leaves (pointers) from each tree into a list, then check if list has any duplicates, but that's a rather slow approach. Is there perhaps a quicker way to solve this?
In the worst case you will have to traverse all nodes (all pointers) to find a shared node (pointer), as it might happen to be the last one visited. So the best time complexity we can expect to have is O(𝑚+𝑛) where 𝑚 and 𝑛 represent the number of nodes in either tree.
We can achieve this time complexity if we store the pointers from the first tree in a hash set and then traverse the pointers of the second tree to see if any of those is in the set. Assuming that get/set operations on a hash set have an amortized constant time complexity, the overal time complexity will be O(𝑚+𝑛).
If the same program is responsible for constructing the trees, then a reuse of the same node can be detected upon insertion. For instance, reuse of the same node in multiple trees can be completely avoided by having the insert method of your tree only take a value as argument, never a node instance. The method will then encapsulate the actual creation of the node, guaranteeing its uniqueness.
An idea for O(#nodes) time and O(1) space. It does more traversal work than simple traversals using a hash table, but it doesn't have the cost of using a hash table. I don't know what's better. Might depend on the language.
For two trees
Create one extra node. Do a Morris traversal of the first tree. It only modifies right child pointers, so we can use left child pointers for marking nodes as seen. For every tree node without left child, set our extra node as left child. Whenever checking a left child pointer, treat our extra node like a null pointer, i.e., don't visit it. After the traversal, the tree structure is restored, and all originally left-child-less tree nodes now point to our extra node as left child. That includes all leaf nodes.
Do a Morris traversal of the second tree. Again treat pointers to our extra node like null pointers. If we ever do encounter our extra node, we know the trees share a node. If not, then we know the trees don't share a node, since if they did share any, they'd also share a leaf node (just go down from any shared node to a leaf node, that's also shared), and all leafs nodes of the first tree are marked. After the traversal, the second tree is restored.
Do a Morris traversal of the first tree again, this time removing our extra node, restoring the original null pointers.
For an array of more than two trees
Mark the first tree as above. Check the second tree as above. Mark the second tree. Check the third. Mark the third. Check the fourth. Mark the fourth. Etc. When you found a shared node or there are no more trees, unmark the marked trees.
Every shared node must have two parents, or an ancestor with two parents.
LOOP over nodes
IF node has two parents
MARK node as shared
Mark all descendants as shared.
I'm aware of ways to keep binary search trees balanced/self-balancing using rotations.
I am not sure if my case needs to be that complicated. I don't need to maintain any sorted order property like with self-balancing BSTs. I just have an ordinary binary tree that I may need to delete nodes or insert nodes. I need try to maintain balance in the tree. For simplicity, my binary tree is similar to a segment tree, and every time a node is deleted, all the nodes along the path from the root to this node will be affected (in my case, it's just some subtraction of the nodal values). Similarly, every time a node is inserted, all the nodes from the root to the inserted node's final location will be affected (an addition to nodal values this time).
What would be the most straightforward way to keep a tree such as this balanced? It doesn't need to be strictly as height balanced as AVL trees, but something like RB trees or maybe slightly less balanced is acceptable as well.
If a new node does not have to be inserted at a particular spot -- possibly determined by its own value and the values in the tree -- but you are completely free to choose its location, then you could maintain the shape of the tree as a complete tree:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible.
An array is a very efficient data structure for a complete tree, as you can store the nodes in their order in a breadth-first traversal. Because the tree is given to be complete, the array has no gaps. This structure is commonly used for heaps:
Heaps are usually implemented with an array, as follows:
Each element in the array represents a node of the heap, and
The parent / child relationship is defined implicitly by the elements' indices in the array.
Example of a complete binary max-heap with node keys being integers from 1 to 100 and how it would be stored in an array.
In the array, the first index contains the root element. The next two indices of the array contain the root's children. The next four indices contain the four children of the root's two child nodes, and so on. Therefore, given a node at index i, its children are at indices 2i + 1 and 2i + 2, and its parent is at index floor((i-1)/2). This simple indexing scheme makes it efficient to move "up" or "down" the tree.
Operations
In your case, you would define the insert/delete operations as follows:
Insert: append the node to the end of the array. Then perform the mutation needed to its ancestors (as you described in your question)
Delete: replace the node to be deleted with the node that currently sits at the very end of the array, and shorten the array by 1. Make the updates needed that follow from the change at these two locations -- so two paths from root-to-node are impacted.
When balancing non-BSTs, the big question to ask is
Can your tree efficiently support rotations?
Some types of binary trees, like k-d trees, have a specific layer-by-layer structure that makes rotations infeasible. Others, like range trees, have auxiliary metadata in each node that's expensive to update after a rotation. But if you can handle rotations, then you can use just about any of the balancing strategies out there. The simplest option might be to model your tree on a treap: put a randomly-chosen weight field into each node, and then, during insertions, rotate your newly-added leaf up until its weight is less than its parent. To delete, repeatedly rotate the node with its lighter child until it's a leaf, then delete it.
If you cannot support rotations, you'll need a rebalancing strategy that does not require them. Perhaps the easiest option there is to model your tree after a scapegoat tree, which works by lazily detecting a node that's too deep for the tree to be balanced, then rebuilding the smallest imbalanced subtree possible into a perfectly-balanced tree to get everything back into order. Deletions are handled by rebuilding the whole tree once the number of nodes drops by some constant factor.
Suppose I am entering 3 elements into a top-down 2,3,4 tree. Would
all the three elements go into root?
For subsequent inserts would a 3rd element be inserted into a node
only if its a leaf node (or into a node when a key kicked up when
you encounter a 3 key node)
Yes, all the three elements would end up in root. Why? A node of 2-3-4 tree is broken only when it is full. When inserting three elements, the only node of the tree won't be full until the insertion of the third element.
For subsequent inserts, not just third, even the second and first elements would be inserted in the leaf node only. It's been nicely outlined in the insertion pseudocode of 2-3-4 trees on Wikipedia.
When a node in a B-tree is split, are keys from the original node duplicated in the new nodes? What's the purpose of doing this? Isn't this inefficient?
No. It's all done with pointers. Half of the pointers are moved to the new node.
Of course, there's no such thing as 'a B-tree'. There are a myriad of different implementations. I could imagine one in which the keys are actually stored in the nodes, such as a tree where the keys are ints. But they still wouldn't be 'duplicated', just the data copied.
If your beef is the storage left behind in the node that gets split, well, that's another optimization choice whether to free and reallocate smaller or not. Probably not, since more insertions could arrive that go into that node's 1/2 of the key space.
I think that you mean a B+ tree.
In a B+ tree that I wrote, I did duplicate the key values in the parent node during a split. key[pos] in the parent was set to the left node's lowest value and pointed to the left node. The right node's lowest value became key[pos+1] in the parent.
I have a data such that there are many parents each with 0-n children where each child can have 0-n nodes. Each node has a unique identifier (key) Ultimately, the parents are not connected to each other. It seems like this would be a list of trees, however that seems imprecise. I was thinking of joining them with a dummy root.
I need to be able to assembly a list of nodes that occur:
from any given node down (children)
from any given node down (children) then up to the root (up to the specific parent)
the top level parent of any given node (in an O(n) operation)
the level of the child in the tree (in an O(n) operation)
The structure will contain 300,000 nodes.
I was thinking perhaps I could implement a List of Trees and then also maintain a hash lookup structure that will reference a specific key value to provide me with a node as a starting point.
Is this a logical structure? Is there a better way to handle it? It seems crude to me.
If you are concerned in find a root node quickly you can think of create a tree where each node points to another tree.