Identify swapped subtrees in a binary search trees - algorithm

I am looking at this challenge:
Suppose two subtrees in a binary search tree have been swapped, and that the BST property is broken. Devise an algorithm that identifies the two swapped subtrees in O(n) time.
My Thoughts
When an inorder traversal of a BST is done, the elements are sorted.
Now when two subtrees are swapped, the inorder traversal will
not be sorted. So if you compare the inorder traversal of the original
tree and the swapped one, it would be like you have taken two subsets
of a sorted array in the original one and swapped them.
But now the challenge comes to identify the corresponding subtrees, and I have no idea how to derive that from the inorder traversal.

First of all, if the tree has duplicate values and they are not always stored at the same side of their parent (that has the same value) then it is not always possible to detect a swap. Imagine a tree with the same value in many different places:
______ 8 _____
/ \
_ 8_ ___ 8 __
/ \ / \
2 8* 8* 14
/ \ / \ / \ / \
1 3 8 8 8 8 13 15
This is a valid BST. And if we were to swap the two subtrees marked with an asterisk, we would end up with a valid BST, and so there is no way to detect which subtrees were swapped. It could well have been the children of the first *-node that had been swapped, or the children of the second *-node that had been swapped. There is no way to know this.
So, it is only possible to detect a swap, if the result of the swap inserts the two involved subtrees at invalid positions. One way to ensure this, is to dictate that duplicate values should always be stored at the same side (for example at the right) of the parent node that has the same value.
Algorithm
An in-order traversal is a good idea, but the idea to then verify that the visited nodes come out in their right order is less useful.
Instead, during the traversal, keep track of the "window" (a min-max range) between which values are allowed in the currently visited subtree. As soon as you find a child that has a value outside that window, report that child node as being misplaced, and don't continue in that child's subtree (as we may assume that the subtree itself is a consistent BST).
If there was indeed a single swap, you will find two such anomalies.
Code
Here is some code (actually JavaScript), assuming you have a Node class with the usual value, left and right properties, and duplicate values can only be stored at the right of a parent node having the same value. The function takes as argument the root node of the BST:
function findSwap(root) {
let results = []; // This array (stack) will be filled with 2 nodes
// Recursive function, which will populate the array:
function recur(node, min, max) {
if (node.value < min || node.value >= max) { // Out of range!
results.push(node); // log this node, and don't bother recurring deeper
} else {
if (node.left != null) {
recur(node.left, min, node.value); // Narrow the window
}
if (node.right != null) {
recur(node.right, node.value, max); // Narrow the window
}
}
}
// Start the search with an infinite window
recur(root, -Infinity, Infinity);
return results; // Return the two nodes found as an array of nodes
}
Note that the out-of-range condition needs exactly those inequalities:
node.value < min || node.value >= max
The min value represents an allowable value, but the max does not. So the valid value range of a node is [min, max) (including min, excluding max). This follows from the extra requirement that duplicate values should always be stored at the right side. If you would decide to always store them on the left side, then the equality should be allowed on the min value and not the max value.
Implementation
Below is a runnable snippet which first creates this binary search tree:
______ 8 _____
/ \
_ 4_ __ 12 __
/ \ / \
2 6 10 14
/ \ / \ / \ / \
1 3 5 7 9 11 13 15
It then swaps the subtree at 6 with the subtree at 10. And finally it calls the above function and reports the result:
function findSwap(root) {
let results = [];
function recur(node, min, max) {
if (node.value < min || node.value >= max) {
results.push(node);
} else {
if (node.left) {
recur(node.left, min, node.value);
}
if (node.right) {
recur(node.right, node.value, max);
}
}
}
recur(root, -Infinity, Infinity);
return results;
}
// Define the Node class
class Node {
constructor(value) {
this.value = value;
this.left = this.right = null;
}
add(...values) { // Allow adding more than one value with one call
for (let value of values) {
if (value < this.value) {
if (this.left) this.left.add(value);
else this.left = new Node(value);
} else {
if (this.right) this.right.add(value);
else this.right = new Node(value);
}
}
}
}
// Demo:
// Create a complete binary tree with values 1 through 15
let root = new Node(8); // root
root.add( 4, 12, // level 1
2, 6, 10, 14, // level 2
1, 3, 5, 7, 9, 11, 13, 15); // level 3
// Perform a swap of the subtree rooted in 6 and in 10:
[root.left.right, root.right.left] = [root.right.left, root.left.right];
// Call the function:
let result = findSwap(root);
// Report which subtrees were swapped
console.log(result[0].value, result[1].value); // 10, 6
Of course, if the tree did not have a swap of exactly two distinct subtrees, then the returned array will not always give reliable information, since it assumes that a wrongly attached subtree is itself still consistent.
But if the returned array is empty, you may conclude that the BST is fine.
Detecting move of one subtree
In comments you gave an example of a subtree that was moved (not swapped with another):
In that case the above code will return just the misplaced subtree, but it will not give information about where this subtree came from.
If also this case should be covered, then we need to change the output, because it doesn't really help to list the other (degenerate) "subtree" as null. So then I propose to have the output state which is the parent and the side of the edge where the subtree was cut away.
The above algorithm could be adapted so that it will do some post processing in case there is only one anomaly found: in that case a simple binary search will find the insertion spot for that misplaced subtree. This post processing represents O(logn) time complexity, so it does not impact the overall linear time complexity.
Here is the adapted code, together with the example you had posted:
function findSwap(root) {
let results = [];
function recur(node, parent, side, min, max) {
if (node.value < min || node.value >= max) {
results.push({parent, side, node});
return;
}
if (node.left != null) {
recur(node.left, node, "left", min, node.value);
}
if (node.right != null) {
recur(node.right, node, "right", node.value, max);
}
}
recur(root, null, "root", -Infinity, Infinity);
// Post processing:
if (results.length === 1) {
// It was not a swap, but a move
let value = results[0].node.value;
// Look up the insertion point for the misplaced value (and its subtree)
let parent = root;
while (results.length < 2) {
if (value < parent.value) {
if (parent.left == null) {
result.push({parent, side: "left", node: null });
} else {
parent = parent.left;
}
} else {
if (parent.right == null) {
results.push({parent, side: "right", node: null });
} else {
parent = parent.right;
}
}
}
}
return results;
}
// Define the Node class
class Node {
constructor(value) {
this.value = value;
this.left = this.right = null;
}
add(...values) { // Allow adding more than one value with one call
for (let value of values) {
if (value < this.value) {
if (this.left) this.left.add(value);
else this.left = new Node(value);
} else {
if (this.right) this.right.add(value);
else this.right = new Node(value);
}
}
}
}
// Demo (as in image):
let root = new Node(5); // root
root.add( 3, 8, // level 1
2, 4, 7, 9); // level 2
// Perform the move of the subtree rooted in 8, below the node 4
root.left.right.right = root.right;
root.right = null;
// Call the function:
let result = findSwap(root);
// Report which subtrees were swapped
function edgeName(edge) {
return "the " + edge.side + " child (" + (edge.node?.value??null) + ") of node " + edge.parent.value;
}
console.log(edgeName(result[0]) + " was swapped with " + edgeName(result[1]));

Related

Reordering a binary search tree within the tree itself

If I am given a binary tree that is unordered, what would be the best method of ordering it without just creating a new tree? When I say ordered, I mean such that all nodes in a left subtree is less than the root node and all nodes in a right subtree is greater than the root node.
I appreciate that the most optimal way to make an undordered binary tree into a binary seach tree is to extract all the nodes then insert them into a new tree, but is there another approach involving switching the placement of nodes in the original tree that could be done algorithmically?
The method of creating a new tree is certainly the way to go, but just as an excercise, it is possible to sort a binary tree in-place.
You could for instance implement bubble sort, such that all nodes remain in place, but their values are swapped in the process.
For this to work you need to implement an inorder traversal. Then keep repeating a full inorder traversal, where you compare the values of two successively visited nodes, and swap their values if they are not in the right order. When an inorder traversal does not result in at least one swap, the tree is sorted and the process can stop.
Here is an implementation in JavaScript:
It first generates a tree with 10 nodes having randomly unsigned integers below 100. The shape of the tree is random too (based on a random "path" that is provided with each insertion)
Then it sorts the tree as described above. As JavaScript has support for generators and iterators, I have used that syntax, but it could also be done with a callback system.
It displays the tree in a very rudimentary way (90° rotated, i.e. with the root at the left side), as it is before and after the sort operation.
class Node {
constructor(value) {
this.value = value;
this.left = this.right = null;
}
}
class Tree {
constructor() {
this.root = null;
}
// Method to add a value as a new leaf node, using the
// binary bits in the given path to determine
// the leaf's location:
addAtPath(value, path) {
function recur(node, path) {
if (!node) return new Node(value);
if (path % 2) {
node.right = recur(node.right, path >> 1);
} else {
node.left = recur(node.left, path >> 1);
}
return node;
}
this.root = recur(this.root, path);
}
*inorder() {
function* recur(node) {
if (!node) return;
yield* recur(node.left);
yield node;
yield* recur(node.right);
}
yield* recur(this.root);
}
toString() {
function recur(node, indent) {
if (!node) return "";
return recur(node.right, indent + " ")
+ indent + node.value + "\n"
+ recur(node.left, indent + " ");
}
return recur(this.root, "");
}
bubbleSort() {
let dirty = true;
while (dirty) {
dirty = false;
let iterator = this.inorder();
// Get first node from inorder traversal
let prevNode = iterator.next().value;
for (let currNode of iterator) { // Get all other nodes
if (prevNode.value > currNode.value) {
// Swap
const temp = prevNode.value;
prevNode.value = currNode.value;
currNode.value = temp;
dirty = true;
}
prevNode = currNode;
}
}
}
}
// Helper
const randInt = max => Math.floor(Math.random() * max);
// Demo:
const tree = new Tree();
for (let i = 0; i < 10; i++) {
tree.addAtPath(randInt(100), randInt(0x80000000));
}
console.log("Tree before sorting (root is at left, leaves at the right):");
console.log(tree.toString());
tree.bubbleSort();
console.log("Tree after sorting:");
console.log(tree.toString());
The time complexity is O(n²) -- typical for bubble sort.
This sorting does not change the shape of the tree -- all nodes stay where they are. Only the values are moved around.

Is it possible to implement binary heap without using additional data structure (e.g. array, linked list)

Can I implement a binary heap by only using a TreeNode inferface (has children, or left/right, or/and parent.. something like this)?
I want to not rely on using array or linked list.
If I don't use array or linked list, I have a trouble inserting the next element in the correct place & keep it a complete binary tree (all non-leaf nodes are full). Also have trouble taking out the root and re-heapifying.
One key observation is this:
The path from the root to the last leaf in a complete binary tree is represented by the binary representation of the size of the tree (number of nodes in the tree).
For instance, this tree has 9 nodes.
1
/ \
4 2
/ \ / \
6 5 3 7
/ \
9 8
9 in binary is 1001. Skipping the most significant "1", this can be read from left-to-right as 0, 0, 1 or "left-left-right". That describes indeed the path from root to the leaf node with value 8!
The same principle holds for when you need to find the insertion point for a new node. Then first increase the size, so this becomes 10 in the example. The binary representation is 1010. Skipping the first digit, this represents "left-right-left". The last direction ("left") gives information about the edge that must be added. And indeed, "left-right" leads us to the node with value 5, and a new node has to be inserted as left-child of that node!
To restore the heap property after an insertion, keep track of the path towards the newly inserted leaf (for example, when coming back out of a recursive function), and wind that path back, each time verifying the heap property, and swapping values when necessary.
Similarly, for an extraction of the root value: first find the node to delete (see above), delete that node and assign the deleted value to the root node. Then sift down the heap to restore the heap property.
Here is an implementation in plain JavaScript -- it should be easy to port this to any other language:
class Node {
constructor(value) {
this.value = value;
this.left = this.right = null;
}
swapValueWith(other) { // We don't swap nodes, just their values
let temp = this.value;
this.value = other.value;
other.value = temp;
}
}
class HeapTree {
constructor() {
this.root = null;
this.size = 0;
}
insert(value) {
this.size++;
if (this.root == null) {
this.root = new Node(value);
} else { // Use the binary representation of the size to find insertion point
this.insertRecursive(this.root, 1 << (Math.floor(Math.log2(this.size)) - 1), value);
}
}
insertRecursive(node, bit, value) {
let side = this.size & bit;
let child;
if (side > 0) {
if (bit == 1) node.right = new Node(value);
child = node.right;
} else {
if (bit == 1) node.left = new Node(value);
child = node.left;
}
if (bit > 1) this.insertRecursive(child, bit>>1, value)
if (node.value > child.value) node.swapValueWith(child); // sift up
}
extract() {
if (this.root == null) return; // Nothing to extract
let value = this.root.value; // The value to return
if (this.size == 1) {
this.root = null;
} else {
// Use the binary representation of the size to find last leaf -- to be deleted
this.root.value = this.deleteRecursive(this.root, 1 << (Math.floor(Math.log2(this.size)) - 1));
// Sift down
let node = this.root;
while (true) {
let minNode = node;
if (node.left != null && node.left.value < minNode.value) minNode = node.left;
if (node.right != null && node.right.value < minNode.value) minNode = node.right;
if (minNode === node) break;
node.swapValueWith(minNode);
node = minNode;
}
}
this.size--;
return value;
}
deleteRecursive(node, bit) {
let side = this.size & bit;
let child;
if (side > 0) {
child = node.right;
if (bit == 1) node.right = null;
} else {
child = node.left;
if (bit == 1) node.left = null;
}
return bit == 1 ? child.value : this.deleteRecursive(child, bit>>1);
}
}
// Demo
let heap = new HeapTree();
for (let value of [4,2,5,8,7,9,0,3,1,6]){
heap.insert(value);
}
// Output the values in sorted order:
while (heap.root != null) {
console.log(heap.extract());
}

Linked list addition (of total size n) and reversion of last k nods at order of n

I am looking at this challenge:
Given are numbers m and p, which both may be as large as 250000. The next m lines have one of the following commands:
APPEND y, which adds y to the end of our list (queue)
ROTATE, which reverses the p last elements of the list. If the list has fewer than p elements, it reverses all of the elements of the list.
Our job is to print the list after all commands have been executed.
A brute force approach is to reverse the array manually, which would have a complexity of O(pm), but you are required to implement it with a complexity of O(m).
I have thought about using a doubly linked list, and I am quite sure it would work, but I could not complete my answer.
Example
Input
8 3
APPEND 1
APPEND 2
APPEND 3
APPEND 4
ROTATE
APPEND 5
APPEND 6
ROTATE
Output
1 4 3 6 5 2
The idea of a doubly linked list is correct. To make it work you need to step away from prev/next notions, but just keep track of the potential 2 neighbours a node may have, without any indication of direction (prev/next).
Your doubly linked list will have a head and a tail -- that must stay. And you are right to also maintain a reference to the node that is currently the start node of the "k last elements" (or fewer when there are not that many elements in the list). Keep that updated whenever you add a node. In order to know in which direction to move that reference, also maintain a reference to the node that precedes it.
Then, when a reversal needs to be performed, it is a matter of swapping the references (and back-references) to the head and tail of that "k last element" sublist. Don't go over the whole sublist to change links between each pair of consecutive nodes. By removing the idea of prev/next, you can just leave those "internal" links as they are. Whenever you need to iterate through the list, you will always know which side you are coming from (i.e. what the "previous" node was), and so you can derive which of the neighbours must be the "next" one.
Here is an implementation of that idea in JavaScript. At the end of the code the algorithm is executed for the example input you have given:
class Node {
constructor(x, neighbor1=null, neighbor2=null) {
this.x = x;
this.neighbors = [neighbor1, neighbor2]; // No specific order...
}
opposite(neighbor) {
// Return the neighbor that is on the other side of the argument-neighbor
return this.neighbors[1 - this.neighbors.indexOf(neighbor)];
}
replaceNeighbor(find, repl) {
let i = this.neighbors.indexOf(find);
this.neighbors[i] = repl;
}
}
class List {
constructor(k) {
this.nodeCount = 0;
this.k = k;
// All node references are null:
this.head = this.tail = this.tailBeforeLastK = this.headOfLastK = null;
}
add(x) {
this.nodeCount++;
let node = new Node(x, this.tail, null);
if (this.head === null) {
this.headOfLastK = this.head = this.tail = node;
return;
}
this.tail.replaceNeighbor(null, node);
this.tail = node;
if (this.nodeCount > this.k) { // Move the head of the "last K" sublist
[this.tailBeforeLastK, this.headOfLastK] =
[this.headOfLastK, this.headOfLastK.opposite(this.tailBeforeLastK)];
}
}
reverse() {
if (this.nodeCount < 2 || this.k < 2) return;
// Exchange the links to the start/end of the K-last sublist
this.tail.replaceNeighbor(null, this.tailBeforeLastK);
if (this.tailBeforeLastK) {
this.tailBeforeLastK.replaceNeighbor(this.headOfLastK, this.tail);
this.headOfLastK.replaceNeighbor(this.tailBeforeLastK, null);
}
else this.head = this.tail;
// Swap
[this.tail, this.headOfLastK] = [this.headOfLastK, this.tail];
}
toArray() {
let result = [];
for (let prev = null, node = this.head; node; [prev, node] =
[node, node.opposite(prev)]) {
result.push(node.x);
}
return result;
}
}
// Example
let k = 3;
// null means: REVERSE, a number means: ADD <number>:
let actions = [1, 2, 3, 4, null, 5, 6, null];
let list = new List(k);
for (let action of actions) {
if (action === null) list.reverse();
else list.add(action);
}
console.log(list.toArray());

My A* pathfinding implementation does not produce the shortest path

I am building a flash game that requires correct pathfinding. I used the pseudo code in this tutorial and a diagonal heuristic. I did not closely follow their code. The language is ActionScript 3 and I am also using flashpunk libraries.
My current issue is that the program is producing a path that is clearly not the shortest path possible. Here is a screenshot showing the problem:
The grey blocks are non traversable, the green blocks mark nodes that have been "visited" and the blue blocks show the path generated by the algorithm.
It looks as if the diagonal travel cost is equal to the non-diagonal travel cost, despite my attempt to make the diagonal cost higher (1.414).
This is the overall algorithm implementation.
function solveMaze() {
// intitialize starting node
startingNode.g = 0;
startingNode.h = diagonalHeuristic(startingNode, destinationNode);
startingNode.f = startingNode.g + startingNode.h;
// Loop until destination node has been reached.
while (currentNode != destinationNode) {
if (openNodes.length == 0) {
return null;
}
// set lowest cost node in openNode list to current node
currentNode = lowestCostInArray(openNodes);
//remove current node from openList
openNodes.splice(openNodes.indexOf(currentNode), 1);
//find 8 nodes adjacent to current node
connectedNodes = findConnectedNodes(currentNode);
//for each adjacent node,
for each (var n:Node in connectedNodes) {
// if node is not in open list AND its not in closed list AND its traversable
if ((openNodes.indexOf(n) == -1) && (closedNodes.indexOf(n) == -1) && n.traversable) {
// Calculate g and h values for the adjacent node and add the adjacent node to the open list
// also set the current node as the parent of the adjacent node
if ((n.mapX != currentNode.mapX) && (n.mapY != currentNode.mapY)) {
cost = 1.414;
} else {
cost = 1;
}
if(n.g> currentNode.g + cost){
n.g = currentNode.g + cost;
n.f=calculateCostOfNode(n);
n.parentNode =currentNode;
openNodes.push(n);
}
}
}
// turn current node into grass to indicate its been traversed
currentNode.setType("walked_path");
//var temp2:TextEntity = new TextEntity(n.h.toFixed(1).toString(), 32 * currentNode.mapX, 32 * currentNode.mapY);
//add(temp2);
// add current node to closed list
closedNodes.push(currentNode);
}
// create a path from the destination node back to the starting node by following each parent node
var tempNode:Node = destinationNode.parentNode;
tempNode.setType("path2"); // blue blocks
while(tempNode != startingNode){
tempNode = tempNode.parentNode;
tempNode.setType("path2");
}
}
These were the helper functions used:
function findConnectedNodes(inputNode:Node):Array {
var outputArray:Array=[];
// obtain all nodes that are either 1 unit away or 1.4 units away.
for each (var n:Node in listOfNodes){
if ((diagonalHeuristic(inputNode, n) == 1)||(diagonalHeuristic(inputNode, n) == 1.4) {
outputArray.push(n);
}
}
return outputArray;
}
public static function diagonalHeuristic(node:Node, destinationNode:Node, cost:Number = 1.0, diagonalCost:Number = 1.4):Number {
var dx:Number = Math.abs(node.mapX - destinationNode.mapX);
var dy:Number = Math.abs(node.mapY - destinationNode.mapY);
if (dx > dy) {
return diagonalCost * dy + (dx - dy);
}else {
return diagonalCost * dx + (dy - dx);
}
}
function lowestCostInArray(inputArray:Array):Node {
var tempNode:Node = inputArray[0];
for each (var n:Node in inputArray) {
if (n.f < tempNode.f) {
tempNode = n;
}
}
return tempNode;
}
I can provide the project source code if it would help.
I see a few potential things wrong.
You are potentially overwriting values here:
n.g = currentNode.g + cost;
n.f=calculateCostOfNode(n);
n.parentNode =currentNode;
openNodes.push(n);
It should be:
if n.g > currentNode.g + cost:
n.g = currentNode.g + cost;
n.f=calculateCostOfNode(n);
n.parentNode =currentNode;
if n not already in openNodes:
openNodes.push(n);
With n.g initiated to a very large value, or you can do the check like if n not in the open set or n.g > currentNode.g + cost.
You should remove the check if ((openNodes.indexOf(n) == -1) from where you have it now and put it where I said. If the new g cost is better, you should update it, even if it's in the open list. You only update each node once. If it so happens that you check diagonals first, you will completely ignore side steps.
This is likely the problem: by ignoring neighbors that are in the open list, you will only update their cost once. It is OK to update their cost as long as they are not in the closed list.
I don't know for sure if this is a valid concern, but I think you're playing with fire a little by using 1.414 in the heuristic function. The heuristic function has to be admissible, which means it should never overestimate the cost. If you run into some floating point issues, you might overestimate. I'd play it safe and use 1.4 for the heuristic and 1.414 for the actual cost between diagonally adjacent nodes.

Non recursive Program to find minimum height of Binary tree

I know the recursive code could be written for finding the minimum height. But for a very large tree (like million nodes in leftside vs 1 node in right side) - the approach isn't good. So please let me know if following code is fine, it uses BFS:-
if (root == null)
{
return 0;
}
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
int min = 0;
while (queue.Count > 0)
{
Node temp = queue.Dequeue();
if (temp.LeftChild == null)
{
return ++min;
}
if (temp.LeftChild != null)
{
++min;
queue.Enqueue(temp.LeftChild);
}
if (temp.RightChild == null)
{
return ++min;
}
if (temp.RightChild != null)
{
++min;
queue.Enqueue(temp.RightChild);
}
}
return 0;
So for a tree like
1
/ \
2 3
/
4
/
6
The above returns 1, (as per Floor(Log(n))?
Thanks.
The idea is perfect. But the code still can be bettered a bit.
Why do you increase min every time you dequeue item? And you do it twice, it is two times worse :) If you supose this variable to be nodes counter then it is incorrect too because you did not count root element. And hence it must be called in the other way, not min.
Why do you check if children are null twice? If statements spoil the pipe, their count must be minimized.
The idea is next. Let`s call the row of the nodes of the equal level full if every node in it has both children. Then min height is the count of full rows in the tree. It equals closest power index of 2 to the items count in all the full rows + 1.
A code:
if (root == null)
{
return 0;
}
Queue<Node> queue = new Queue<Node>();
queue.Enqueue(root);
int nodesCount = 0;
while (queue.Count > 0)
{
Node temp = queue.Dequeue();
if (temp.LeftChild == null || temp.RightChild == null)
{
return Floor(Log(nodesCount + 1)/Log(2)); // It can be made much better using, for example, bitwise operations but this is not the question`s topic
}
++nodesCount;
queue.Enqueue(temp.LeftChild);
queue.Enqueue(temp.RightChild);
}
return Infinity; // :)
Use 2 stacks to do a "Zig-zag" traversal. Count the number of times where you need to flip the "leftToRight" flag.

Resources