Given a BST and its root, print all sequences of nodes which give rise to the same bst - permutation

Given a BST, find all sequences of nodes starting from root that will essentially give the same binary search tree.
Given a bst, say
3
/ \
1 5
the answer should be 3,1,5 and 3,5,1.
another example
5
/ \
4 7
/ / \
1 6 10
the outputs will be
5,4,1,7,6,10
5,4,7,6,10,1
5,7,6,10,4,1
etc
The invariant here however is that the parent's index must always be lesser than its children. I am having difficulty implementing it.

I assume you want a list of all sequences which will generate the same BST.
In this answer, we will use Divide and Conquer. We will create a function findAllSequences(Node *ptr) which takes a node pointer as input and returns all the distinct sequences which will generate the subtree hanging from ptr. This function will return a Vector of Vector of int, i.e. vector<vector<int>> containing all the sequences.
The main idea for generating sequence is that root must come before all its children.
Algorithm:
Base Case 1:
If ptr is NULL, then return a vector with an empty sequence.
if (ptr == NULL) {
vector<int> seq;
vector<vector<int> > v;
v.push_back(seq);
return v;
}
Base Case 2:
If ptr is a leaf node, then return a vector with a single sequence. Its Trivial that this sequence will contain only a single element, i.e. value of that node.
if (ptr -> left == NULL && ptr -> right == NULL) {
vector<int> seq;
seq.push_back(ptr -> val);
vector<vector<int> > v;
v.push_back(seq);
return v;
}
Divide Part (this part is very simple.)
We assume that we have a function that can solve this problem, and thus we solve it for left sub tree and right sub tree.
vector<vector<int> > leftSeq = findAllSeq(ptr -> left);
vector<vector<int> > rightSeq = findAllSeq(ptr -> right);
Merging the two solutions.(The crux is in this step.)
Till now we have two set containg distinct sequences:
i. leftSeq - all sequences in this set will generate left subtree.
ii. rightSeq - all sequences in this set will generate right subtree.
Now each sequence in left subtree can be merged with each sequence of right subtree. While merging we should be careful that the relative order of elements is preserved. Also in each of the merged sequence we will add the value of current node in the beginning beacuse root must come before all children.
Pseudocode for Merge
vector<vector<int> > results
for all sequences L in leftSeq
for all sequences R in rightSeq
create a vector flags with l.size() 0's and R.size() 1's
for all permutations of flag
generate the corresponding merged sequence.
append the current node's value in beginning
add this sequence to the results.
return results.
Explanation: Let us take a sequence, say L(of size n) from the set leftSeq, and a sequence, say R(of size m) from set rightSeq.
Now these two sequences can be merged in m+nCn ways!
Proof: After merging, the new sequence will have m + n elements. As we have to maintain the relative order of elements, so firstly we will fill all n the elements from L in any of n places among total (m+n) places. After that remaining m places can be filled by elements of R. Thus we have to choose n places from (m+n) places.
To do this, lets create take a Boolean vector, say flags and fill it with n 0's and m 1's.A value of 0 represents a member from left sequence and a value of 1 represents member from right sequence. All what is left is to generate all permutations of this flags vector, which can be done with next_permutation. Now for each permutation of flags we will have a distinct merged sequence of L and R.
eg: Say L={1, 2, 3} R={4, 5}
so, n=3 and m=2
thus, we can have 3+2C3 merged sequences, i.e. 10.
1.now, Initially flags = {0 0 0 1 1}, filled with 3 0's and 2 1's
this will result into this merged sequence: 1 2 3 4 5
2.after calling nextPermutation we will have
flags = {0 0 1 0 1}
and this will generate sequence: 1 2 4 3 5
3.again after calling nextPermutation we will have
flags = {0 0 1 1 0}
ans this will generate sequence: 1 2 4 5 3and so on...
Code in C++
vector<vector<int> > findAllSeq(TreeNode *ptr)
{
if (ptr == NULL) {
vector<int> seq;
vector<vector<int> > v;
v.push_back(seq);
return v;
}
if (ptr -> left == NULL && ptr -> right == NULL) {
vector<int> seq;
seq.push_back(ptr -> val);
vector<vector<int> > v;
v.push_back(seq);
return v;
}
vector<vector<int> > results, left, right;
left = findAllSeq(ptr -> left);
right = findAllSeq(ptr -> right);
int size = left[0].size() + right[0].size() + 1;
vector<bool> flags(left[0].size(), 0);
for (int k = 0; k < right[0].size(); k++)
flags.push_back(1);
for (int i = 0; i < left.size(); i++) {
for (int j = 0; j < right.size(); j++) {
do {
vector<int> tmp(size);
tmp[0] = ptr -> val;
int l = 0, r = 0;
for (int k = 0; k < flags.size(); k++) {
tmp[k+1] = (flags[k]) ? right[j][r++] : left[i][l++];
}
results.push_back(tmp);
} while (next_permutation(flags.begin(), flags.end()));
}
}
return results;
}
Update 3rd March 2017: This solution wont work perfectly if original tree contains duplicates.

Here is a clear, concise and well-documented solution that I wrote for you in Python 3. I hope it helps you!
Code: bst_sequences.py
from binarytree import bst, Node
def weave_lists(first: list, second: list, results: list, prefix: list) -> None:
"""Recursively Weave the first list into the second list and append
it to the results list. The prefix list grows by an element with the
depth of the call stack. Ultimately, either the first or second list will
be exhausted and the base case will append a result."""
# base case
if not first or not second:
results.append(prefix + first + second)
return
# recursive case
first_head, first_tail = first[0], first[1:]
weave_lists(first_tail, second, results, prefix + [first_head])
second_head, second_tail = second[0], second[1:]
weave_lists(first, second_tail, results, prefix + [second_head])
def all_sequences(root: Node) -> list:
"""Splits the tree into three lists: prefix, left, and right."""
if root is None:
return []
answer = []
prefix = [root.value]
left = all_sequences(root.left) or [[]]
right = all_sequences(root.right) or [[]]
# At a minimum, left and right must be a list containing an empty list
# for the following nested loop
for i in range(len(left)):
for j in range(len(right)):
weaved = []
weave_lists(left[i], right[j], weaved, prefix)
answer.extend(weaved)
return answer
if __name__ == "__main__":
t = bst(2)
print(t)
solution = all_sequences(t)
for e, item in enumerate(solution):
print(f"{e:03}: {item}")
Sample Output
__4
/ \
1 5
/ \ \
0 2 6
000: [4, 1, 0, 2, 5, 6]
001: [4, 1, 0, 5, 2, 6]
002: [4, 1, 0, 5, 6, 2]
003: [4, 1, 5, 0, 2, 6]
004: [4, 1, 5, 0, 6, 2]
005: [4, 1, 5, 6, 0, 2]
006: [4, 5, 1, 0, 2, 6]
007: [4, 5, 1, 0, 6, 2]
008: [4, 5, 1, 6, 0, 2]
009: [4, 5, 6, 1, 0, 2]
010: [4, 1, 2, 0, 5, 6]
011: [4, 1, 2, 5, 0, 6]
012: [4, 1, 2, 5, 6, 0]
013: [4, 1, 5, 2, 0, 6]
014: [4, 1, 5, 2, 6, 0]
015: [4, 1, 5, 6, 2, 0]
016: [4, 5, 1, 2, 0, 6]
017: [4, 5, 1, 2, 6, 0]
018: [4, 5, 1, 6, 2, 0]
019: [4, 5, 6, 1, 2, 0]
Process finished with exit code 0

I have a much shorter solution. What do you think about it?
function printSequences(root){
let combinations = [];
function helper(node, comb, others){
comb.push(node.values);
if(node.left) others.push(node.left);
if(node.right) others.push(node.right);
if(others.length === 0){
combinations.push(comb);
return;
}else{
for(let i = 0; i<others.length; i++){
helper(others[i], comb.slice(0), others.slice(0, i).concat(others.slice(i+1, others.length)));
}
}
}
helper(root, [], []);
return combinations;
}

Note that the question is actually about topological sorting of a tree: find all the possible ways to perform topological sort. That is, we don't care about the specific way the tree was built, what's important is that elements are always added as leaves, never changing the structure of existing nodes. The constraint on the output is that nodes never precede their ancestors - treating the tree as a classic dependency graph.
But unlike topological sort for a general DAG, there's no need for reference counting here, since this is a tree - the number of references is always 1 or 0.
Here's a simple Python implementation:
def all_toposorts_tree(sources, history):
if not sources:
print(history)
return
for t in sources:
all_toposorts((sources - {t}) | {t.left, t.right} - {None}, history + [t.v])
all_toposorts_tree({root}, [])
This is question 4.9 in Cracking the Coding Interview, 6th Edition.

well here is my python code which does producing all sequences of elements/numbers for same BST.
for the logic i referred to the book cracking the coding interview by Gayle Laakmann Mcdowell
from binarytree import Node, bst, pprint
def wavelist_list(first, second, wave, prefix):
if first:
fl = len(first)
else:
fl = 0
if second:
sl = len(second)
else:
sl = 0
if fl == 0 or sl == 0:
tmp = list()
tmp.extend(prefix)
if first:
tmp.extend(first)
if second:
tmp.extend(second)
wave.append(tmp)
return
if fl:
fitem = first.pop(0)
prefix.append(fitem)
wavelist_list(first, second, wave, prefix)
prefix.pop()
first.insert(0, fitem)
if sl:
fitem = second.pop(0)
prefix.append(fitem)
wavelist_list(first, second, wave, prefix)
prefix.pop()
second.insert(0, fitem)
def allsequences(root):
result = list()
if root == None:
return result
prefix = list()
prefix.append(root.value)
leftseq = allsequences(root.left)
rightseq = allsequences(root.right)
lseq = len(leftseq)
rseq = len(rightseq)
if lseq and rseq:
for i in range(lseq):
for j in range(rseq):
wave = list()
wavelist_list(leftseq[i], rightseq[j], wave, prefix)
for k in range(len(wave)):
result.append(wave[k])
elif lseq:
for i in range(lseq):
wave = list()
wavelist_list(leftseq[i], None, wave, prefix)
for k in range(len(wave)):
result.append(wave[k])
elif rseq:
for j in range(rseq):
wave = list()
wavelist_list(None, rightseq[j], wave, prefix)
for k in range(len(wave)):
result.append(wave[k])
else:
result.append(prefix)
return result
if __name__=="__main__":
n = int(input("what is height of tree?"))
my_bst = bst(n)
pprint(my_bst)
seq = allsequences(my_bst)
print("All sequences")
for i in range(len(seq)):
print("set %d = " %(i+1), end="")
print(seq[i])
example output:
what is height of tree?3
___12
/ \
__ 6 13
/ \ \
0 11 14
\
2
All sequences
set 1 = [12, 6, 0, 2, 11, 13, 14]
set 2 = [12, 6, 0, 2, 13, 11, 14]
set 3 = [12, 6, 0, 2, 13, 14, 11]
set 4 = [12, 6, 0, 13, 2, 11, 14]
set 5 = [12, 6, 0, 13, 2, 14, 11]
set 6 = [12, 6, 0, 13, 14, 2, 11]
set 7 = [12, 6, 13, 0, 2, 11, 14]
set 8 = [12, 6, 13, 0, 2, 14, 11]
set 9 = [12, 6, 13, 0, 14, 2, 11]
set 10 = [12, 6, 13, 14, 0, 2, 11]
set 11 = [12, 13, 6, 0, 2, 11, 14]
set 12 = [12, 13, 6, 0, 2, 14, 11]
set 13 = [12, 13, 6, 0, 14, 2, 11]
set 14 = [12, 13, 6, 14, 0, 2, 11]
set 15 = [12, 13, 14, 6, 0, 2, 11]
set 16 = [12, 6, 0, 11, 2, 13, 14]
set 17 = [12, 6, 0, 11, 13, 2, 14]
set 18 = [12, 6, 0, 11, 13, 14, 2]
set 19 = [12, 6, 0, 13, 11, 2, 14]
set 20 = [12, 6, 0, 13, 11, 14, 2]
set 21 = [12, 6, 0, 13, 14, 11, 2]
set 22 = [12, 6, 13, 0, 11, 2, 14]
set 23 = [12, 6, 13, 0, 11, 14, 2]
set 24 = [12, 6, 13, 0, 14, 11, 2]
set 25 = [12, 6, 13, 14, 0, 11, 2]
set 26 = [12, 13, 6, 0, 11, 2, 14]
set 27 = [12, 13, 6, 0, 11, 14, 2]
set 28 = [12, 13, 6, 0, 14, 11, 2]
set 29 = [12, 13, 6, 14, 0, 11, 2]
set 30 = [12, 13, 14, 6, 0, 11, 2]
set 31 = [12, 6, 11, 0, 2, 13, 14]
set 32 = [12, 6, 11, 0, 13, 2, 14]
set 33 = [12, 6, 11, 0, 13, 14, 2]
set 34 = [12, 6, 11, 13, 0, 2, 14]
set 35 = [12, 6, 11, 13, 0, 14, 2]
set 36 = [12, 6, 11, 13, 14, 0, 2]
set 37 = [12, 6, 13, 11, 0, 2, 14]
set 38 = [12, 6, 13, 11, 0, 14, 2]
set 39 = [12, 6, 13, 11, 14, 0, 2]
set 40 = [12, 6, 13, 14, 11, 0, 2]
set 41 = [12, 13, 6, 11, 0, 2, 14]
set 42 = [12, 13, 6, 11, 0, 14, 2]
set 43 = [12, 13, 6, 11, 14, 0, 2]
set 44 = [12, 13, 6, 14, 11, 0, 2]
set 45 = [12, 13, 14, 6, 11, 0, 2]

here is another concise recursion based easy to understand solution:
from binarytree import Node, bst, pprint
def allsequences1(root):
if not root:
return None
lt = allsequences1(root.left)
rt = allsequences1(root.right)
ret = []
if not lt and not rt:
ret.append([root])
elif not rt:
for one in lt:
ret.append([root]+one)
elif not lt:
for two in rt:
ret.append([root]+two)
else:
for one in lt:
for two in rt:
ret.append([root]+one+two)
ret.append([root]+two+one)
return ret
if __name__=="__main__":
n = int(input("what is height of tree?"))
my_bst = bst(n)
pprint(my_bst)
seg = allsequences1(my_bst)
print("All sequences ..1")
for i in range(len(seq)):
print("set %d = " %(i+1), end="")
print(seq[i])

Let's first observe what must be be followed to create the same BST. The only sufficient rules here is insert parent before their left and right children. Because, if we can guarantee that for some node (that we are interested to insert) all parents (including grand parent) are inserted but none of it's children are inserted, than the node will find its appropriate place to be inserted.
Following this observation we can write backtrack to generate all sequence that will produce same BST.
active_list = {root}
current_order = {}
result ={{}}
backtrack():
if(len(current_order) == total_node):
result.push(current_order)
return;
for(node in active_list):
current_order.push(node.value)
if node.left :
active_list.push(node.left)
if node.right:
active_list.push(node.right)
active_list.remove(node)
backtrack()
active_list.push(node)
if node.left :
active_list.remove(node.left)
if node.right:
active_list.remove(node.right)
current_order.remove(node.val)
This is not working implementation. used just for illustration purpose.

public class Solution {
ArrayList<LinkedList<Long>> result;
/*Return the children of a node */
ArrayList<TreeNode> getChilden(TreeNode parent) {
ArrayList<TreeNode> child = new ArrayList<TreeNode>();
if(parent.left != null) child.add(parent.left);
if(parent.right != null) child.add(parent.right);
return child;
}
/*Gets all the possible Compinations*/
void getPermutations(ArrayList<TreeNode> permutations, LinkedList<Long> current) {
if(permutations.size() == 0) {
result.add(current);
return;
}
int length = permutations.size();
for(int i = 0; i < length; i++) {
TreeNode node = permutations.get(i);
permutations.remove(i);
ArrayList<TreeNode> newPossibilities = new ArrayList<TreeNode>();
newPossibilities.addAll(permutations);
newPossibilities.addAll(getChilden(node));
LinkedList<Long> newCur = new LinkedList<Long>();
newCur.addAll(current);
newCur.add(node.val);
getPermutations(newPossibilities, newCur);
permutations.add(i,node);
}
}
/*This method returns a array of arrays which will lead to a given BST*/
ArrayList<LinkedList<Long>> inputSequencesForBst(TreeNode node) {
result = new ArrayList<LinkedList<Long>>();
if(node == null)
return result;
ArrayList<TreeNode> permutations = getChilden(node);
LinkedList<Long> current = new LinkedList<Long>();
current.add(node.val);
getPermutations(permutations, current);
return result;
}
}
My solution. Works perfectly.

Here's my Python solution with plenty of explanation.
We build each array from left to right by choosing for every position one node out of a set of possible choices for that position. We add the node value to the path, and the children of the node (if any) to the list of possibilities, then recurse further. When there are no further choices we have one candidate array. To generate the rest of the the arrays, we backtrack until we can make a different choice and recurse again.
The catch is to use a suitable data structure for holding the possibilities. A list works, but the node has to be put back in the previous position while backtracking (order matters, since we have added the children of the node which must be visited AFTER the node). Insertion and deletion from a list takes linear time. A set doesn't work since it doesn't maintain order. A dict works best since Python dictionary remembers the insertion order and all operations run in constant time.
def bst_seq(root: TreeNode) -> list[list[int]]:
def _loop(choices: MutableMapping[TreeNode, bool], path: list[int], result: list[list[int]]) -> None:
if not choices:
result.append([*path])
else:
# Take a snapshot of the keys to avoid concurrent modification exception
for choice in list(choices.keys()):
del choices[choice]
children = list(filter(None, [choice.left, choice.right]))
for child in children:
choices[child] = False
path.append(choice.val)
_loop(choices, path, result)
path.pop()
choices[choice] = False
for child in children:
del choices[child]
result = []
_loop({root: False}, [], result)
return result

Related

on restoring the original order of row elements

Consider numpy array p shown below. Unique values 0 to 9 are used in each row. The distinguishing characteristic is that every row is composed of 5 (in this case) values PAIRED with 5 other values. Pairs are formed when p[k] = p[p[k]] for k = 0 to 9.
p = np.array([[1, 0, 3, 2, 5, 4, 7, 6, 9, 8],
...
[6, 5, 3, 2, 9, 1, 0, 8, 7, 4],
...
[9, 8, 5, 7, 6, 2, 4, 3, 1, 0]])
Examine, for example, the row:
[6, 5, 3, 2, 9, 1, 0, 8, 7, 4]
This row pairs values 6 and 0 because p[6] = 0 and p[0] = 6. Other pairs are values (5, 1), (3, 2), (9, 4), (8, 7). Different rows may have different arrangements of pairs.
Now, we are interested here in the 1st value of each pair (ie: 6, 5, 3, 9, 8) and the 2nd value of each pair (ie: 0, 1, 2, 4, 7)
I'm not sure this is the best way to proceed, but I've separated the 1st pair values from the 2nd pair values this way:
import numpy as np
p = np.array([6, 5, 3, 2, 9, 1, 0, 8, 7, 4])
p1 = np.where(p[p] < p) # indices of 1st member of pairs
p2 = (p[p1]) # indices of 2nd member of pairs
qi = np.hstack((p1, p2.reshape(1,5)))
qv = p[qi]
#output: qi = [0, 1, 2, 4, 7, 6, 5, 3, 9, 8] #indices of 1st of pair values, followed by 2nd of pair values
# qv = [6, 5, 3, 9, 8, 0, 1, 2, 4, 7] #corresponding values
Finally consider another 1D array: c = [1, 1, 1, 1, 1, -1, -1, -1, -1, -1].
I find c*qv, giving:
out1 = [6, 5, 3, 9, 8, 0, -1, -2, -4, -7]
QUESTION: out1 holds the correct values, but I need them to be in the original order (as found in p). How can this be achieved?
I need to get:
out2 = [6, 5, 3, -2, 9, -1, 0, 8, -7, -4]
You can reuse p1 and p2, which hold the original position information.
out2 = np.zeros_like(out1)
out2[p1] = out1[:5]
out2[p2] = out1[5:]
print(out2)
# [ 6 5 3 -2 9 -1 0 8 -7 -4]
Can also use qi to similar effect, but even neater.
out2 = np.zeros_like(out1)
out2[qi] = out1
Or using np.put in case you don't want to create out2:
np.put(out1, qi, out1)
print(out1)
# [ 6 5 3 -2 9 -1 0 8 -7 -4]
2D Case
For 2D version of the problem, we will use a similar idea, but some tricks while indexing.
p = np.array([[1, 0, 3, 2, 5, 4, 7, 6, 9, 8],
[6, 5, 3, 2, 9, 1, 0, 8, 7, 4],
[9, 8, 5, 7, 6, 2, 4, 3, 1, 0]])
c = np.array([1, 1, 1, 1, 1, -1, -1, -1, -1, -1])
p0 = np.arange(10) # this is equivalent to p[p] in 1D
p1_r, p1_c = np.where(p0 < p) # save both row and column indices
p2 = p[p1_r, p1_c]
# We will maintain row and column indices, not just qi
qi_r = np.hstack([p1_r.reshape(-1, 5), p1_r.reshape(-1, 5)]).ravel()
qi_c = np.hstack([p1_c.reshape(-1, 5), p2.reshape(-1, 5)]).ravel()
qv = p[qi_r, qi_c].reshape(-1, 10)
out1 = qv * c
# Use qi_r and qi_c to restore the position
out2 = np.zeros_like(out1)
out2[qi_r, qi_c] = out1.ravel()
print(out2)
# [[ 1 0 3 -2 5 -4 7 -6 9 -8]
# [ 6 5 3 -2 9 -1 0 8 -7 -4]
# [ 9 8 5 7 6 -2 -4 -3 -1 0]]
Feel free to print out each intermediate variable, will help you understand what's going on.

Eliminating array rows based on a property of consecutive pairs of elements

We are given an array sample a, shown below, and a constant c.
import numpy as np
a = np.array([[1, 3, 1, 11, 9, 14],
[2, 12, 1, 10, 7, 6],
[6, 7, 2, 14, 2, 15],
[14, 8, 1, 3, -7, 2],
[0, -3, 0, 3, -3, 0],
[2, 2, 3, 3, 12, 13],
[3, 14, 4, 12, 1, 4],
[0, 13, 13, 4, 0, 3]])
c = 2
It is convenient, in this problem, to think of each array row as being composed of three pairs, so the 1st row is [1,3, 1,11, 9,14].
DEFINITION: d_min is the minimum difference between the elements of two consecutive pairs.
The PROBLEM: I want to retain rows of array a, where all consecutive pairs have d_min <= c. Otherwise, the rows should be eliminated.
In the 1st array row, the 1st pair (1,3) and the 2nd pair (1,11) have d_min = 1-1=0.
The 2nd pair (1,11) and the 3rd pair(9,14) have d_min = 11-9=2. (in both cases, d_min<=c, so we keep this row in a)
In the 2nd array row, the 1st pair (2,12) and the 2nd pair (1,10) have d_min = 2-1=1.
But, the 2nd pair (1,10) and the 3rd pair(7,6) have d_min = 10-7=3. (3 > c, so this row should be eliminated from array a)
Current efforts: I currently handle this problem with nested for-loops (2 deep).
The outer loop runs through the rows of array a, determining d_min between the first two pairs using:
for r in a
d_min = np.amin(np.abs(np.subtract.outer(r[:2], r[2:4])))
The inner loop uses the same method to determine the d_min between the last two pairs.
Further processing only is done only when d_min<= c for both sets of consecutive pairs.
I'm really hoping there is a way to avoid the for-loops. I eventually need to deal with 8-column arrays, and my current approach would involve 3-deep looping.
In the example, there are 4 row eliminations. The final result should look like:
a = np.array([[1, 3, 1, 11, 9, 14],
[0, -3, 0, 3, -3, 0],
[3, 14, 4, 12, 1, 4],
[0, 13, 13, 4, 0, 3]])
Assume the number of elements in each row is always even:
import numpy as np
a = np.array([[1, 3, 1, 11, 9, 14],
[2, 12, 1, 10, 7, 6],
[6, 7, 2, 14, 2, 15],
[14, 8, 1, 3, -7, 2],
[0, -3, 0, 3, -3, 0],
[2, 2, 3, 3, 12, 13],
[3, 14, 4, 12, 1, 4],
[0, 13, 13, 4, 0, 3]])
c = 2
# separate the array as previous pairs and next pairs
sx, sy = a.shape
prev_shape = sx, (sy - 2) // 2, 1, 2
next_shape = sx, (sy - 2) // 2, 2, 1
prev_pairs = a[:, :-2].reshape(prev_shape)
next_pairs = a[:, 2:].reshape(next_shape)
# subtract which will effectively work as outer subtraction due to numpy broadcasting, and
# calculate the minimum difference for each pair
pair_diff_min = np.abs(prev_pairs - next_pairs).min(axis=(2, 3))
# calculate the filter condition as boolean array
to_keep = pair_diff_min.max(axis=1) <= c
print(a[to_keep])
#[[ 1 3 1 11 9 14]
# [ 0 -3 0 3 -3 0]
# [ 3 14 4 12 1 4]
# [ 0 13 13 4 0 3]]
Demo Link

Merge two ordered arrays into one ordered array

I am writing a method that takes two sorted arrays and I want it to return a merged array with all the values sorted. Given the two arrays below:
array_one = [3, 4, 8]
array_two = [1, 5, 7]
I want my merge_arrays method to return:
[1, 3, 4, 5, 7, 8]
My current algorithm is below:
def merge_arrays(array_one, array_two)
merged_array_size = array_one.length + array_two.length
merged_array = []
current_index_on_one = 0
current_index_on_two = 0
current_merged_index = 0
for i in (0..merged_array_size - 1)
if array_one[current_index_on_one] < array_two[current_index_on_two]
merged_array[current_merged_index] = array_one[current_index_on_one]
current_index_on_one += 1
current_merged_index += 1
else
merged_array[current_merged_index] = array_two[current_index_on_two]
current_index_on_two += 1
current_merged_index += 1
end
end
return merged_array
end
I am getting an error 'undefined method `<' for nil:NilClass'. I don't understand how the conditional is receiving this. I debugged the variables in the conditionals and they are giving true or false values. I'm not sure what is causing this error.
Maybe I am missing the point but you can do:
(array_one + array_two).sort
=> [1, 3, 4, 5, 7, 8]
I am getting an error 'undefined method `<' for nil:NilClass'. I don't understand how the conditional is receiving this.
You start by comparing index 0 to index 0:
[3, 4, 8] [1, 5, 7]
0-----------0 #=> 3 < 1
Then you increment the lower value's index by 1:
[3, 4, 8] [1, 5, 7]
0--------------1 #=> 3 < 5
And so on:
[3, 4, 8] [1, 5, 7]
1-----------1 #=> 4 < 5
[3, 4, 8] [1, 5, 7]
2--------1 #=> 8 < 5
[3, 4, 8] [1, 5, 7]
2-----------2 #=> 8 < 7
At that point you get:
[3, 4, 8] [1, 5, 7]
2--------------3 #=> 8 < nil
Index 3 is outside the array's bounds, so array_two[current_index_on_two] returns nil and:
if array_one[current_index_on_one] < array_two[current_index_on_two]
# ...
end
becomes
if 8 < nil
# ...
end
resulting in ArgumentError(comparison of Integer with nil failed). If nil is on the left hand side, you'd get NoMethodError (undefined method `<' for nil:NilClass).
Here's one way you can write merge using recursion. Note, as you specified, both inputs must already be sorted otherwise the output will be invalid. The inputs can vary in size.
def merge (xs, ys)
if xs.empty?
ys
elsif ys.empty?
xs
else
x, *_xs = xs
y, *_ys = ys
if x < y
[x] + (merge _xs, ys)
else
[y] + (merge xs, _ys)
end
end
end
merge [ 1, 3, 4, 6, 8, 9 ], [ 0, 2, 5, 7 ]
# => [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Assuming you have two sorted arrays. You need to create pipeline using recursion going to crunch through each array. checking at each iteration to see
which value at index 0 of either array is lower, removing that from the array and appending that value to the result array.
def merge_arrays(a, b)
# build a holder array that is the size of both input arrays O(n) space
result = []
# get lower head value
if a[0] < b[0]
result << a.shift
else
result << b.shift
end
# check to see if either array is empty
if a.length == 0
return result + b
elsif b.length == 0
return result + a
else
return result + merge_arrays(a, b)
end
end
> a = [3, 4, 6, 10, 11, 15]
> b = [1, 5, 8, 12, 14, 19]
> merge_arrays(a, b)
#=> [1, 3, 4, 5, 6, 8, 10, 11, 12, 14, 15, 19]
I made slight changes to your code in order to make it work. See the comments inside.
array_one = [2, 3, 4, 8, 10, 11, 12, 13, 15]
array_two = [1, 5, 6, 7, 9, 14]
def merge_arrays(array_one, array_two)
array_one, array_two = array_two, array_one if array_one.length > array_two.length # (1) swap arrays to make satement (3) work, need array_two always be the longest
merged_array_size = array_one.length + array_two.length
merged_array = []
current_index_on_one = 0
current_index_on_two = 0
current_merged_index = 0
for i in (0...merged_array_size-1) # (2) three points to avoid the error
if (!array_one[current_index_on_one].nil? && array_one[current_index_on_one] < array_two[current_index_on_two]) # (3) check also if array_one is nil
merged_array[current_merged_index] = array_one[current_index_on_one]
current_index_on_one += 1
current_merged_index += 1
else
merged_array[current_merged_index] = array_two[current_index_on_two]
current_index_on_two += 1
current_merged_index += 1
end
end
merged_array[current_merged_index] = array_one[current_index_on_one] || array_two[current_index_on_two] # (4) add the missing element at the end of the loop, looks what happen if you comment out this line
return merged_array
end
p merge_arrays(array_one, array_two)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
The error was coming because the loop was making one step over. The solution is to stop before and insert the missing element at the end of the loop.
It works also with:
# for i in (1...merged_array_size)
# and
# for i in (1..merged_array_size-1)
# and
# (merged_array_size-1).times do
arr1 = [3, 4, 8, 9, 12]
arr2 = [1, 5, 7, 8, 13]
arr = [arr1, arr2]
idx = [0, 0]
(arr1.size + arr2.size).times.with_object([]) do |_,a|
imin = [0, 1].min_by { |i| arr[i][idx[i]] || Float::INFINITY }
a << arr[imin][idx[imin]]
idx[imin] += 1
end
#=> [1, 3, 4, 5, 7, 8, 8, 9, 12, 13]

For each element in an array, if the element is less than its previous element, increase it till the previous element with one

Suppose I have an array: list1 = [8, 5, 3, 1, 1, 10, 15, 9]
Now if the element is less than its previous element, increase it till the previous element with one.
Here:
5 < 8 so 5 should become: 5 + 3 + 1 = 9 i.e (8+1)
3 < 5 so 3 should become: 3 + 2 + 1 = 6 i.e (5+1)
1 < 3 so 1 should become: 1 + 2 + 1 = 4 i.e (3+1)
Now I am able to get the difference between elements if its less than its previous element.
But, how to use it in a final list to get an output like this:
finallist = [8, 9, 6, 4, 1, 10, 15, 16]
Also how can I get a final list value of 'k' list in my code? Right now it shows:
[2]
[2, 4]
[2, 4, 3]
[2, 4, 3, 3]
[2, 4, 3, 3, 7]
Source code:
list1 = [8, 5, 3, 1, 1, 10, 15, 9]
k = []
def comput(x):
if i[x] < i[x-1]:
num = (i[x-1] - i[x]) + 1
k.append(num)
print(k)
return
for i in [list1]:
for j in range(len(list1)):
comput(j)
You can use a list comprehension for this. Basically, the following code will check if one is larger than the next. If it is, then it will convert it to the previous+1.
list1 = [8, 5, 3, 1, 1, 10, 15, 9]
k = [list1[0]] + [i if j<=i else j+1 for i,j in zip(list1[1:],list1[:-1])]
cost = [j-i for i,j in zip(list1,k)]
print(k)
print(cost)
Output:
[8, 9, 6, 4, 1, 10, 15, 16]
[0, 4, 3, 3, 0, 0, 0, 7]
The following code will create a new list with the required output
l1 = [8, 5, 3, 1, 1, 10, 15, 9]
l = [l1[0]]
c=[0] # cost / difference list
for i in range(len(l1)-1):
if l1[i+1] < l1[i]:
l.append(l1[i]+1)
c.append(l1[i]+1-l1[i+1])
else:
l.append(l1[i+1])
c.append(0)
print(l)
Output
[8, 9, 6, 4, 1, 10, 15, 16]
[0, 4, 3, 3, 0, 0, 0, 7]

Sort BST in O(n) using constant memory

This is not a homework. Just an interesting task :)
Given a complete binary search three represensted by array. Sort the array in O(n) using constant memory.
Example:
Tree:
8
/ \
4 12
/\ / \
2 6 10 14
/\ /\ /\ /\
1 3 5 7 9 11 13 15
Array: 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15
Output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
It is possible, people calling it homework probably haven't tried solving it yet.
We use the following as a sub-routine:
Given an array a1 a2 ... an b1 b2 .. bn, convert in O(n) time and O(1) space to
b1 a1 b2 a2 ... bn an
A solution for that can be found here: http://arxiv.org/abs/0805.1598
We use that as follows.
Do the above interleaving for the first 2^(k+1) - 2 elements, starting at k=1 repeating for k=2, 3 etc, till you go past the end of array.
For example in your array we get (interleaving sets identified by brackets)
8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15
[ ][ ]
4, 8, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 (k = 1, interleave 2)
[ ][ ]
2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15 (k = 2, interleave 6)
[ ][ ]
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 (k = 3, interleave 14)
So the total time is n + n/2 + n/4 + ... = O(n).
Space used is O(1).
That this works can be proved by induction.
Thinking about the O(1) in-place variant, but for now here's the O(N) solution
An O(N) space solution
If you can use an O(N) output array, then you can simply perform an inorder traversal. Every time you visit a node, add it to the output array.
Here's an implementation in Java:
import java.util.*;
public class Main {
static void inorder(int[] bst, List<Integer> sorted, int node) {
if (node < bst.length) {
inorder(bst, sorted, node * 2 + 1);
sorted.add(bst[node]);
inorder(bst, sorted, node * 2 + 2);
}
}
public static void main(String[] args) {
int[] bst = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 };
final int N = bst.length;
List<Integer> sorted = new ArrayList<Integer>();
inorder(bst, sorted, 0);
System.out.println(sorted);
// prints "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]"
}
}
Attachment
Source and output on ideone.com

Resources