how can you build binary tree without sorting it, I.E
if i have a input 5 4 9 8 1 2 7 how can you insert that into a reference based binary tree.
I know this can be easily implemented with Array, but is it possible with reference base?
Tree buildTree(int[] array, int index) {
if(index > array.length) { return null; }
return new Tree(
array[index],
buildTree(array, 2 * index + 1),
buildTree(array, 2 * index + 2));
}
Most of the work is in the recursion and in the indexing, but it's not too bad at all.
One simple rule is to always insert into the left subtree and then switch the subtrees. The right subtree will always be 0-1 elements larger than the left subtree, so you can always insert into the left subtree. Now, the left subtree is 0-1 elements larger than the right subtree, so you want to switch the subtrees to preserve the invariant. In pseudocode:
insert(t,v) {
if (t == null) {
return new TreeNode(v,null,null)
} else {
left = insert(t.left,v)
right = t.right
t.left = right
t.right = left
return t
}
}
Related
Problem description
I am trying to write a math test for my little son.
Such test must generate a list of random algebraic expressions according to certain rules and check the correctness of solution.
In particular, I want to generate expressions consisting strictly of a given number of operators that are selected from a certain list.
For example generate a list of expression consisting of 3 operators of addition and subtraction in random order like:
12 - (5 + 2) + 2
3 + 4 - 2 + 10
and so on
To represent and calculate the expression, I use the binary expression tree structure.
Each tree consists of either a Leaf or a Node that contains an Operator and two subtrees.
This is a simple recursive structure and I want to work with it only recursively.
No setters in the classes of the tree. I can only use constructors to create a tree.
Leaf class
public final class Leaf implements Expression {
private final int value;
public Leaf(int value) {
this.value = value;
}
// ...
}
Node Class
public final class Node implements Expression {
private final Operator operator;
private final Expression left;
private final Expression right;
public Node(#NotNull Operator operator,
#NotNull Expression left,
#NotNull Expression right) {
this.operator = operator;
this.left = left;
this.right = right;
}
// ...
}
And Operator is a simple Enum type. I simplify my classes for the purposes of this question.
My issue
I am trying to build an expression based on the following rules:
There should be at least one operator in the expression, so my tree always starts from the Node.
I choose a random operator from a given list and increase the number of operators used
While this number less than the given number of operators I construct the left and rights subtree for current Node.
The left subtree can be randomly either a Leaf or Node
The right subtree can also be either a Leaf or Node, but if the left subtree is a Leaf and there are still unused operators, then the right must be a Node.
I wrote such an expression builder:
public class SmartExpressionBuilder {
private final Random random = ThreadLocalRandom.current();
private final List<Operator> allowedOperators;
private final int numberOfOperators;
public SmartExpressionBuilder(List<Operator> allowedOperators, int numberOfOperators) {
this.allowedOperators = allowedOperators;
this.numberOfOperators = numberOfOperators;
}
private int operatorsUsed;
public Expression build() {
operatorsUsed = 0;
return helper();
}
private Expression helper() {
if (operatorsUsed == numberOfOperators) return randomLeaf();
Operator op = randomOperator();
Expression left = random.nextBoolean() ? helper() : randomLeaf();
Expression right = (left instanceof Leaf || random.nextBoolean()) ? helper() : randomLeaf();
return new Node(op, left, right);
}
private Operator randomOperator() {
operatorsUsed++;
return allowedOperators.get(random.nextInt(allowedOperators.size()));
}
private Leaf randomLeaf() {
return new Leaf(random.nextInt(1, 10));
}
public static void main(String[] args) {
final var builder = new SmartExpressionBuilder(List.of(Operator.ADD, Operator.SUB), 4);
IntStream.range(0, 10)
.mapToObj(ignored -> builder.build())
.forEach(exp -> {
System.out.printf("%s = %d%n", exp.infix(), exp.evaluate());
TreePrinter.print(exp);
});
}
}
This works in principle. In the sense that a tree really builds with a given number of operators.
But there's a problem.
I get nodes looks like this:
Node Node
/ \ or / \
Leaf Node Node Leaf
For example my actual expression and tree may looks like this:
4 + 4 - (1 + 3) - 2 = 2
+
4 -
- 2
4 +
1 3
but i never get tree like this:
Node +
/ \ or - +
Node Node 5 2 2 -
6 1
I understand what the essence of the problem is.
In my recursive function, I always go into the left tree first.
And every time my random generates an the Node is in the left subtree, and not the Leaf, recursion dive deeper and deeper int the left subtree until unused operators ends.
This means that if an Node appeared in the left subtree, then Node cannot appear in the right at the same depths of tree.
I broke my brain, but did not figure out how to solve this problem without abandoning the recursive construction of my tree.
I would be very grateful for any ideas how build nodes of this kind
Node
/ \
Node Node
It's going to be very difficult to get balanced trees this way - you have to tune it very carefully for the left tree to probably give you half the operators. I don't think it's worth it.
Instead, I would pick the target number of operators at the top level - that would be a minimum plus some random range to generate larger or smaller expressions - and then randomly assign some of them to each subtree. So you have a recursive call that takes a size parameter; if size==0, generate a leaf, otherwise make a node, and split size-1 into a leftSize and rightSize to pass to the recursive calls.
Here's some rough pseudocode (I don't write much Java these days, but hopefully it makes the algorithm clear)
private Expression build(int size){
if (size == 0) return buildLeaf()
else {
leftSize = randomInt(size-1)
rightSize = size - 1 - leftSize
leftTree = build(leftSize)
rightTree = build(rightSize)
return buildNode(leftTree, rightTree, getRandomOperator())
}
}
Does that make sense and work for you?
I rewritten my method, as Edward Peters suggested.
At each step of recursion, I randomly determine how many Node's will be in the left and right trees (the sum of these numbers at the first step should be equal to the required number of operators in the expression), and return the Leaf if the number of nodes turns out to zero.
It's work just fine.
public Expression build(int numberOfOperators) {
if (numberOfOperators == 0) return randomLeaf();
int leftNodes = random.nextInt(numberOfOperators);
int rightNodes = numberOfOperators - leftNodes - 1;
return new Node(randomOperator(), build(leftNodes), build(rightNodes));
}
One example of resulting expression tree:
5 + 5 - (4 + 7) = -1
- Node
+ + or Node Node
5 5 4 7 Leaf Leaf Leaf Leaf
I'm currently going over Robert Sedgewick's Algorithms book. In the book I'm trying to understand the implementation of the select method in a Binary Search Tree. The author uses a BST to implement a symbol table. The author describes the select method as follow:
Suppose that we seek the key of rank k (the key such that precisely k
other keys in the BST are smaller). If the number of keys t in the
left sub- tree is larger than k, we look (recursively) for the key of
rank k in the left subtree; if t is equal to k, we return the key at
the root; and if t is smaller than k, we look (recursively) for the
key of rank k - t - 1 in the right subtree. As usual, this de-
scription serves both as the basis for the recursive select() method
on the facing page and for a proof by induction that it works as
expected.
I want to understand specifically what is the purpose of the k - t - 1 pass to the recursive select method when the size of the left node is less than the number of keys smaller than k.
public Key select(int k)
{
return select(root, k).key;
}
private Node select(Node x, int k)
{ // Return Node containing key of rank k.
if (x == null) return null;
int t = size(x.left);
if (t > k) return select(x.left, k);
else if (t < k) return select(x.right, k-t-1);
else return x;
}
As you can see the above implementation of the select method of a Binary Search Tree. When the conditional t < k the author passes k-t-1 to the recursive select method call but I can't still figure out why that is.
k − t − 1 is equal to k − (t + 1). When t < k and we recurse into the right subtree, the t elements in the left subtree and the 1 root count toward the rank of the elements in the right subtree, so we need to adjust k to match.
Given a binary tree with TreeNode like:
class TreeNode {
int data;
TreeNode left;
TreeNode right;
int size;
}
Where size is the number of nodes in the (left subtree + right subtree + 1).
Print a random element from the tree in O(logn) running time.
Note: This post is different from this one, as it is clearly mentioned that we have a size associated with each node in this problem.
PS: Wrote this post inspired from this.
There is an easy approach which gives O(n) complexity.
Generate a random number in the range of 1 to root.size
Do a BFS or DFS traversal
Stop iterating at random numbered element and print it.
For improving the complexity, we need to create an ordering of our own where we branch at each iteration instead of going sequentially as in BFS and DFS. We can use the size property of each node to decide whether to traverse through the left sub-tree or right sub-tree. Following is the approach:
Generate a random number in the range of 1 to root.size (Say r)
Start traversing from the root node and decide whether to go to left sub-tree, right-subtree or print root:
if r <= root.left.size, traverse through the left sub-tree
if r == root.left.size + 1, print the root (we have found the random node to print)
if r > root.left.size + 1, traverse through the right sub-tree
Essentially, we have defined an order where current node is ordered at (size of left subtree of current) + 1.
Since we eliminate traversing through left or right sub-tree at each iteration, its running time is O(logn).
The pseudo-code would look something like this:
traverse(root, random)
if r == root.left.size + 1
print root
else if r > root.left.size + 1
traverse(root.right, random - root.left.size - 1)
else
traverse(root.left, random)
Following is an implementation in java:
public static void printRandom(TreeNode n, int randomNum) {
int leftSize = n.left == null ? 0 : n.left.size;
if (randomNum == leftSize + 1)
System.out.println(n.data);
else if (randomNum > leftSize + 1)
printRandom(n.right, randomNum - (leftSize + 1));
else
printRandom(n.left, randomNum);
}
Use size!
Pick a random number q between 0 and n.
Start from the root. If left->size == q return current node value. If the left->size < q the go right else you go left. If you go right subtract q -= left->size + 1. Repeat until you output a node.
This give you o(height of tree). If the tree is balanced you get O(LogN).
If the tree is not balanced but you still want to keep O(logN) you can do the same thing but cap the maximum number of iterations. Note that in this case not all nodes have the same probability of being returned.
Yes, use size!
As Sorin said, pick a random number i between 0 and n - 1 (not n)
Then perform the following instruction:
Treenode rand_node = select(root, i);
Where select could be as follows:
TreeNode select_rec(TreeNode r, int i) noexcept
{
if (i == r.left.size)
return r;
if (i < r.left.size)
return select_rec(r.left, i);
return select_rec(r.right, i - r.left.size - 1);
}
Now a very important trick: the null node must be a sentinel node with size set to 0, what has sense because the empty tree has zero nodes. You can avoid the use of sentinel, but then the select() operation is lightly more complex.
If the trees is balanced, then select() is O(log n)
i have implemented a function to find the depth of a node in a binary search tree but my implementation does not take care of duplicates. I have my code below and would like some suggestions on how to consider duplicates case in this function. WOuld really appreciate your help.
public int depth(Node n) {
int result=0;
if(n == null || n == getRoot())
return 0;
return (result = depth(getRoot(), n, result));
}
public int depth(Node temp, Node n, int result) {
int cmp = n.getData().compareTo(temp.getData());
if(cmp == 0) {
int x = result;
return x;
}
else if(cmp < 0) {
return depth(temp.getLeftChild(), n, ++result);
}
else {
return depth(temp.getRightChild(), n, ++result);
}
}
In the code you show, there is no way to prefer one node with same value over another. You need to have some criteria for differentiation.
You can retrieve the list of all duplicate nodes depths using the following approach, for example:
Find the depth of your node.
Find depth of the same node for the left subtree emerging from the found node - stop if not found.
Add depth of the previously found node (in 1) to the depth of the duplicate
Find depth of the same node for the right subtree emerging from the found node (in 1) - stop if not found.
Add depth of the previously found node (in 1) to the depth of the duplicate
Repeat for left and right subtrees.
Also see here: What's the case for duplications in BST?
Well, if there's duplicates, then the depth of a node with a given value doesn't make any sense on its own, because there may be multiple nodes with that value, hence multiple depths.
You have to decide what it means, which could be (not necessarily an exhaustive list):
the depth of the deepest node with that value.
the depth of the shallowest node with that value.
the depth of the first node found with that value.
the average depth of all nodes with that value.
the range (min/max) of depths of all nodes with that value.
a list of depths of all nodes with that value.
an error code indicating your query made little sense.
Any of those could make sense in specific circumstances.
Of course, if n is an actual pointer to a node, you shouldn't be comparing values of nodes at all, you should be comparing pointers. That way, you will only ever find one match and the depth of it makes sense.
Something like the following pseudo-code should do:
def getDepth (Node needle, Node haystack, int value):
// Gone beyond leaf, it's not in tree
if haystack == NULL: return -1
// Pointers equal, you've found it.
if needle == haystack: return value
// Data not equal search either left or right subtree.
if needle.data < haystack.data:
return getDepth (needle, haystack.left, value + 1)
if needle.data > haystack.data:
return getDepth (needle, haystack.right, value + 1)
// Data equal, need to search BOTH subtrees.
tryDepth = getDepth (needle, haystack.left, value + 1)
if trydepth == -1:
tryDepth = getDepth (needle, haystack.right, value + 1)
return trydepth
The reason why you have to search both subtrees when the values are equal is because the desired node may be in either subtree. Where the values are unequal, you know which subtree it's in. So, for the case where they're equal, you check one subtree and, if not found, you check the other.
I am trying to split a binary search tree at the root in Java. I have no idea how to go about this. The recursive call is what is mostly confusing me. Below is the pseudocode that I was given. When x is greater than T (the tree to be split), am I going to call split(R.key, R.right, L, R)? Am I on the right track? This is the only function in the project that is confusing me.
Thanks in advance.
void split( int x, bst T, bst L, bst R) /* assuming x is not in T */
{
if T is null, make L and R null
else if x < T.key
set R = T /* all keys at the root and in right subtree are greater than x */
recursively split T's left subtree into L and R’s left subtree
/* some keys in T's left subtree are greater than x, other may be less than x */
else /* x is greater than T.key */
set L = T
recursively split T's right subtree into L's right subtree and R
}
It is important that you must have a binary ordered tree, such that Left < Root < Right for every subtree. Also, there is no node in the left subtree such that node > Root, and no node in the right subtree such that node < Root. Balancing (that all subtrees are same depth +- 1) is not needed.
It is like a dicomotical search; if the value to split by is greater than your current root, you are sure that it is greater than all nodes in the left subtree (because of the previous restriction). So, in order to search which of the nodes of the tree are bigger, you only need to check the nodes to the right. Likewise, if the value to split by is less than the value of root, it is also less than the values of all the nodes of the right subtree, and you must check more finely in the left tree.
In order to see it clearly, I suggest you to draw this tree (no spacing here)
8
4 12
3 6 10 14
1 2 5 7 9 11 13 15
, set several sample split values, and mark which nodes would remain in the new tree.