Remove nodes from Linked List based on a value - javascript

I am working through a Hackerrank problem and I am trying to remove all nodes that are greater than a particular value.
This is their basic implementation
const SinglyLinkedListNode = class{
constructor(value) {
this.value = value;
this.next = null;
}
};
const SinglyLinkedList = class {
constructor() {
this.head = null;
this.tail = null;
}
insertNode(value) {
const node = new SinglyLinkedListNode(value);
if(!this.head) {
this.head = node;
} else {
this.tail.next = node;
}
this.tail = node;
}
};
My function to removeNodes is as follows...
SinglyLinkedList.prototype.removeNodes = function(listHead, x){
let currentNode = listHead;
while(currentNode !== null && currentNode.next !== null) {
let next = currentNode.next;
while (next !== null && next.value > x) {
next = next.next
}
currentNode.next = next
if(currentNode.next === null) {
break;
}
}
return currentNode
}
The parameters are: listhead - reference to the root node, and x - the integer value to filter the Linked List
So for example, the LL is 1-> 2 -> 4 -> 3 -> 5 and I need to remove all nodes greater than x (3) and maintain the integrity of the LL order.
The result should be 1 -> 2 -> 3.
I am getting confused on why I keep getting this.tail.value = 5 instead of
this.tail.value = 3, this.tail.next = null.
this is a REPL

Cause the tail has to be rewritten explicitly as it keeps a reference to an unlinked node otherwise. Before running the function the list looks like:
list: head tail
1 -> 2 -> 3 -> 4 -> 5
and afterwards the tail still points to 5 although that was unlinked:
list: head tail
1 -> 2 -> 3 5
To resolve this, just rewrite the tail at the end of your function:
return this.tail = currentNode;
Also you have to actually traverse the list, so add a
currentNode = currentNode.next;
at the end of the outer while.

Related

Splay Tree implementation: Search function - Zag zag rotation wrong

I am testing Zag-zag rotation with a right-skewed tree. Before that, I had tested Zig-zig, Zig-zag rotation with a left-skewed tree and OK.
Here is my code:
// An AVL tree node
class Node {
constructor(key) {
this.key = key;
this.left = this.right = null;
}
}
function rightRotate(root) {
const rootLeft = root.left; // saving the reference
root.left = rootLeft.right;
rootLeft.right = root;
return rootLeft; // new root
}
function leftRotate(root) {
const rootRight = root.right;
root.right = rootRight.left;
rootRight.left = root;
return rootRight;
}
// This function modifies the tree and returns the new root
// - If has key, Brings the key at root
// - Else, brings the last accessed item at root.
function splay(root, key) {
// Base cases
if (root === null || root.key === key) return root; // bring last accessed element or founded element as root of left-left (sub)tree
// Grandparent start here
// Key lies in Left
if (key < root.key) {
// Key is not in tree
if (root.left === null) return root; // If not founded, Bring last accessed element root of left (sub)tree
// Parent start here
// Zig-Zig (Left Left)
if (key < root.left.key) {
root.left.left = splay(root.left.left, key);
// Do first rotation for root, brind child as parent of subtree
root = rightRotate(root);
}
// Zig-Zag (Left Right)
else if (key > root.left.key) {
root.left.right = splay(root.left.right, key);
// Do first rotation for root.left, brind child as parent of subtree
if (root.left.right != null) root.left = leftRotate(root.left);
}
// 1 cong 2 viec
// 1. Do second rotation, bring child as grandparent of subtree
// 2. Bring parent (level 2) as root of tree when last recursive splay() finish
return rightRotate(root);
// Parent end
}
// Key lies in Right
else {
if (root.right === null) return root;
// Parent start here
// Zag-Zag (Right Right)
if (key > root.right.key) {
root.right.right = splay(root.right.right, key);
root = leftRotate(root);
}
// Zag-Zig (Right Left)
else if (key < root.right.key) {
root.right.left = splay(root.right.left, key);
if (root.right.left != null) root.right = rightRotate(root.right);
}
return leftRotate(root);
// End parent
}
// Grandparent end
}
// The search function for Splay tree.
// This function returns the new root of Splay Tree.
// If key is present in tree then, it is moved to root.
// else, last accessed element is moved to root
function search(root, key) {
return splay(root, key);
}
// A utility function to print
// preorder traversal of the tree.
// The function also prints height of every node
function preOrder(root) {
if (root != null) {
console.log(root.key);
preOrder(root.left);
preOrder(root.right);
}
}
// Right-skewed tree for Zag-zag and Zag-zig test
// Using root2 to console.log in other function like splay, search (because they have argument name "root")
let root2 = new Node(10);
root2.right = new Node(15);
root2.right.right = new Node(16);
root2.right.right.right = new Node(20);
root2.right.right.right.right = new Node(21);
root2.right.right.right.right.right = new Node(22);
root2 = splay(root2, 20);
console.log(root2)
Assume we search for node 20. The rotation should happen as follow:
Zag-zag rotation: Replace node 15 with 20.
Zag rotation: Replace node 10 with 20
However, my code result is:
Demo of image used this
Both your algorithm and the imaged algorithm perform double rotations correctly, every time taking a pair of edges on the path to the target node, and applying a double rotation on that pair.
However, when the path to the target node has an odd length, then there is one edge that will undergo a single rotation. The choice of this single edge is different:
your algorithm will "split" the path in pairs starting from the root node, treating the remaining single edge at the end of the path separately.
the imaged algorithm will "split" the path in pairs starting from the target node, treating the remaining single edge at the start of the path (at the root) separately.
Here is your code aligned with that second strategy:
function splay(root, key) {
function splaySub(root) {
if (!root) throw new RangeError; // Value not found in tree
if (root.key === key) return root;
let side = key < root.key ? "left" : "right";
root[side] = splaySub(root[side]);
// Check if odd: then caller to deal with rotation
if (root[side].key === key) return root;
// Apply a double rotation, top-down:
root = key < root.key ? rightRotate(root) : leftRotate(root);
return key < root.key ? rightRotate(root) : leftRotate(root);
}
try {
root = splaySub(root);
return !root || root.key === key
// Path had even length: all OK
? root
// Odd length: Perform a final, single rotation
: key < root.key ? rightRotate(root) : leftRotate(root);
} catch(e) {
if (!(e instanceof RangeError)) throw e;
return root; // Not found: return original tree
}
}
When the searched value is not in the tree, this code will trigger an Exception, so to exit quickly out of recursion without any update to the tree. In this case that approach makes the code a bit cleaner (fewer checks for null values...).
Addendum
As in comments you explained that the splay function should also bring a node to the top when it doesn't find the value, I provide here the updated code. I took the opportunity to turn the code into more OOP style, so you'll find here some of your functions turned into methods:
class Node {
constructor(key, left=null, right=null) {
this.key = key;
this.left = left;
this.right = right;
}
* inOrder(depth=0) {
if (this.right) yield * this.right.inOrder(depth+1);
yield [depth, this.key];
if (this.left) yield * this.left.inOrder(depth+1);
}
_rotate(toRight) {
const [side, otherSide] = toRight ? ["right", "left"] : ["left", "right"];
const orig = this[otherSide];
this[otherSide] = orig[side];
orig[side] = this;
return orig;
}
rightRotate() {
return this._rotate(true);
}
leftRotate() {
return this._rotate(false);
}
insert(key) {
const side = key < this.key ? "left" : "right";
if (this[side]) return this[side].insert(key);
this[side] = new Node(key);
}
}
class SplayTree {
constructor(...values) {
this.root = null;
for (const value of values) this.insert(value);
}
insert(value) {
if (this.root) this.root.insert(value);
else this.root = new Node(value);
}
splay(key) {
if (!this.root) return;
function splaySub(root) {
if (root.key === key) return root;
const side = key < root.key ? "left" : "right";
// Not found? Then do as if we looked for current node's key
if (!root[side]) {
key = root.key; // Modifies the outer-scoped variable
return root;
}
root[side] = splaySub(root[side]);
// Check if odd: then caller to deal with rotation
if (root[side].key === key) return root;
// Apply a double rotation, top-down:
root = root._rotate(key < root.key);
return root._rotate(key < root.key);
}
this.root = splaySub(this.root);
if (this.root.key !== key) this.root = this.root._rotate(key < this.root.key);
}
search(key) {
this.splay(key);
}
* inOrder() {
if (this.root) yield * this.root.inOrder();
}
toString() {
return Array.from(tree.inOrder(), ([depth, key]) => " ".repeat(depth) + key).join("\n");
}
}
const tree = new SplayTree(10, 15, 16, 20, 21, 22);
console.log("Initial tree:");
console.log(tree.toString());
tree.search(19); // Not found, so it splays 20.
console.log("Final tree:");
console.log(tree.toString());

How can I unite two trees in javascript?

I'm implementing an algorithm (Kruslkal) that needs to merge two or more binary trees in javascript, for example:
The following tree:
4
5 6
Can be merged into the following tree:
2
1 3
... resulting:
2
1 3
4
5 6
I put the binary tree data structure code, but when I did a test in a function that merges the trees, called 'merge', nothing happens. The first tree is not merged in the second tree, and if I try to use console.log in the function 'merge', the following message appears: "Uncaught TypeError: tree is null".
Can someone help me with this?
function binarytree()
{
this.root = null;
this.add = function(value)
{
var node = {
value : value,
left : null,
right : null
};
var current;
if (this.root == null) this.root = node;
else
{
current = this.root;
while (1)
{
if (value < current.value)
{
if (current.left == null)
{
current.left = node;
break;
}
else current = current.left;
}
else if (value > current.value)
{
if (current.right == null)
{
current.right = node;
break;
}
else current = current.right;
}
else break;
}
}
}
this.search = function(value)
{
var found = false,
current = this.root;
while (!found && current)
{
if (value < current.value) current = current.left;
else if (value > current.value) current = current.right;
else found = true;
}
return found;
}
this.print = function(no)
{
if (no)
{
this.print(no.left);
this.print(no.right);
console.log(no.value);
}
}
}
var tree = new binarytree();
var tree2 = new binarytree();
function merge(tree, tree2)
{
//console.log("tree.value " + tree.value);
if (tree == null) tree = tree2.root;
else if (tree.value < tree2.value) this.merge(tree.left, tree2);
else this.merge(tree.right, tree2);
}
tree.add(1);
tree.add(2);
tree.add(3);
console.log("First tree:");
tree.print(tree.root);
tree2.add(7);
tree2.add(8);
tree2.add(9);
console.log("Second tree:");
tree2.print(tree2.root);
merge(tree.root,tree2.root);
console.log("Merged trees:");
tree.print(tree.root);
Looking at your code, it is clear that you are dealing with not just any binary trees, but binary search trees. These trees ensure that the value of a node is never less than the value of its left child, and never greater than the value of its right child.
Your example is therefore not correctly pictured. This is not a binary search tree:
4
5 6
It would be correct if it were:
5
4 6
Moreover, your code is not creating these trees. Instead it is creating these trees:
1 and 7
2 8
3 9
If you want to create more balanced trees, you should change the order of the insertions. For example:
tree.add(2); // First!
tree.add(1);
tree.add(3);
This will create:
2
1 3
The Error
...if I try to use console.log in the function 'merge', the following message appears: "Uncaught TypeError: tree is null".
This is expected, as you make recursive calls like with this.merge(tree.left, tree2), and tree.left can be null. Even in the next statement you check this case with if (tree == null), so it is normal you get this error.
But your code shows that you think that an assignment to tree with tree = tree2.root; will somehow perform the attachment of tree2 inside tree. But this is just an assignment to a variable, not to a left or right property of a node in the tree, so nothing is happening to the tree with this assignment. Remember that JavaScript passes values, so when you pass tree.left as argument to a function, you can be sure that tree.left will still reference the same object once the function has returned.
In short, you should make the assignment one step earlier, when you arrive at a leaf, not when you arrive at a null. Something like this:
function merge(tree, tree2) {
if (tree2.value < tree.value) {
if (tree.left) {
this.merge(tree.left, tree2);
} else {
tree.left = tree2;
}
} else {
if (tree.right) {
this.merge(tree.right, tree2);
} else {
tree.right = tree2;
}
}
}
The deeper problem
However, while the above will perform a simple attachment of one tree to another, it assumes that the range of values of the first tree does not overlap with the range of values in the second tree. If there is an overlap, this procedure will not produce a binary search tree. A merge that maintains the BST property, will need to distribute the nodes of the second tree at different places in the first tree.
One way to do that is to take every value of the second tree and call add(value) on the first tree. This will work fine. It has a time complexity of O(nlogm), where m is the size of the first tree, and n of the second tree.
If the tree sizes are comparable, you'll get a better time complexity when you walk through the first tree in one sweep, inserting new nodes as you pass by the right insertion spot. This will have a time complexity of O(m+n).
Implementation
I would change a lot to your code:
Use class syntax... and define methods on the prototype, not on each instance
Define an iterator to visit nodes in inorder sequence
Avoid the code repetition in add and search.
Define a class for constructing node objects instead of using an object literal for that
... several other improvements
Here it is:
class Node { // Create a class for this
constructor(value, left=null, right=null) {
this.value = value;
this.left = left;
this.right = right;
}
* inorder() {
if (this.left) yield * this.left.inorder();
yield this.value;
if (this.right) yield * this.right.inorder();
}
}
class BinaryTree { // Use class syntax and PascalCase
constructor() {
this.root = null;
}
add(value) {
let [location, side] = this.locate(value);
if (side) location[side] = new Node(value); // Use constructor instead of plain object literal;
}
locate(value) { // Returns where node is or should be inserted
if (!this.root) return [this, "root"];
let current = this.root;
while (true) {
if (value < current.value) {
if (!current.left) return [current, "left"];
current = current.left;
} else if (value > current.value) {
if (!current.right) return [current, "right"];
current = current.right;
}
else return [current, ""];
}
}
search(value) {
return !this.locate(value)[1];
}
print() { // Use iterator to get the values
for (let value of this.inorder()) console.log(value);
}
* inorder(node) {
if (this.root) yield * this.root.inorder();
}
merge(otherTree) {
let values = otherTree.inorder();
let nextValue = values.next().value;
function recur(node, max) {
while (nextValue !== undefined && nextValue < max) {
if (nextValue < node.value) {
if (!node.left) {
node.left = new Node(nextValue);
nextValue = values.next().value;
}
recur(node.left, node.value);
} else if (nextValue > node.value) {
if (!node.right) {
node.right = new Node(nextValue);
nextValue = values.next().value;
}
recur(node.right, max);
} else {
nextValue = values.next().value;
}
}
}
recur(this.root, Infinity);
}
}
var tree = new BinaryTree();
var tree2 = new BinaryTree();
tree.add(2);
tree.add(4);
tree.add(6);
console.log("First tree:");
tree.print();
tree2.add(1);
tree2.add(3);
tree2.add(5);
console.log("Second tree:");
tree2.print();
tree.merge(tree2);
console.log("Merged trees:");
tree.print();

How to implement deque data structure in javascript?

I'm Learning data structure with javascript
and my focus now on how to implement deque?
Edite: from comments below I get useful directions on how to implement deque based array. Is there a direction how to implement deque based object using class ?
I get understand some points like I need :
addFront()
removeFront()
peekFront()
addBack()
removeBack()
peekBack()
but I'm confused about some points :
how many pointers I need ?
at least I know from queue I need two(head-tail) pointer but not sure if I need more in deque
which data type in javascript convenient in this case as a base? I saw some tutors in youtube talking about circular array for example which unknown for me in JS.
edite2:
I was following a book called: learning javascript data structures and algorithms 3rd edition
in chapter5 of this book the author started to implement Deque based on object only and some variables
but I didn't understand how he did that because the code encrypted but I can still reach to his files from and test his approach github repository
I can say that #trincot answer very close of book author approach
but when I compare the results I get this [1 = author - 2 = #trincot] :
according to the book index taking about linked list comes in chapter6 so I didn't expect his solution will be based on something he didn't mentioned before
plz if I miss any point I will be grateful to tell me it ... thanks
As stated in comments, JavaScript has native support for deque operations via its Array class/prototype: push, pop, shift, unshift.
If you still want to write your own implementation, then you can go for a doubly linked list, where you just need two "pointers". It should be said that in JavaScript we don't really speak of pointers, but of objects. Variables or properties that get an object as value, are in fact references in JavaScript.
Alternatively, you can go for a circular array. Since in JavaScript standard Arrays are not guaranteed to be consecutive arrays as for example is the case in C, you don't really need to use an Array instance for that. A plain object (or Map) will do.
So here are two possible implementations:
Doubly Linked List
class Deque {
constructor() {
this.front = this.back = undefined;
}
addFront(value) {
if (!this.front) this.front = this.back = { value };
else this.front = this.front.next = { value, prev: this.front };
}
removeFront() {
let value = this.peekFront();
if (this.front === this.back) this.front = this.back = undefined;
else (this.front = this.front.prev).next = undefined;
return value;
}
peekFront() {
return this.front && this.front.value;
}
addBack(value) {
if (!this.front) this.front = this.back = { value };
else this.back = this.back.prev = { value, next: this.back };
}
removeBack() {
let value = this.peekBack();
if (this.front === this.back) this.front = this.back = undefined;
else (this.back = this.back.next).back = undefined;
return value;
}
peekBack() {
return this.back && this.back.value;
}
}
// demo
let deque = new Deque;
console.log(deque.peekFront()); // undefined
deque.addFront(1);
console.log(deque.peekBack()); // 1
deque.addFront(2);
console.log(deque.removeBack()); // 1
deque.addFront(3);
deque.addFront(4);
console.log(deque.peekBack()); // 2
deque.addBack(5);
deque.addBack(6);
console.log(deque.peekBack()); // 6
console.log(deque.removeFront()); // 4
console.log(deque.removeFront()); // 3
console.log(deque.removeFront()); // 2
console.log(deque.removeFront()); // 5
console.log(deque.removeFront()); // 6
console.log(deque.removeFront()); // undefined
Circular "Array"
class Deque {
constructor() {
this.data = {}; // Or Array, but that really does not add anything useful
this.front = 0;
this.back = 1;
this.size = 0;
}
addFront(value) {
if (this.size >= Number.MAX_SAFE_INTEGER) throw "Deque capacity overflow";
this.size++;
this.front = (this.front + 1) % Number.MAX_SAFE_INTEGER;
this.data[this.front] = value;
}
removeFront() {
if (!this.size) return;
let value = this.peekFront();
this.size--;
delete this.data[this.front];
this.front = (this.front || Number.MAX_SAFE_INTEGER) - 1;
return value;
}
peekFront() {
if (this.size) return this.data[this.front];
}
addBack(value) {
if (this.size >= Number.MAX_SAFE_INTEGER) throw "Deque capacity overflow";
this.size++;
this.back = (this.back || Number.MAX_SAFE_INTEGER) - 1;
this.data[this.back] = value;
}
removeBack() {
if (!this.size) return;
let value = this.peekBack();
this.size--;
delete this.data[this.back];
this.back = (this.back + 1) % Number.MAX_SAFE_INTEGER;
return value;
}
peekBack() {
if (this.size) return this.data[this.back];
}
}
// demo
let deque = new Deque;
console.log(deque.peekFront()); // undefined
deque.addFront(1);
console.log(deque.peekBack()); // 1
deque.addFront(2);
console.log(deque.removeBack()); // 1
deque.addFront(3);
deque.addFront(4);
console.log(deque.peekBack()); // 2
deque.addBack(5);
deque.addBack(6);
console.log(deque.peekBack()); // 6
console.log(deque.removeFront()); // 4
console.log(deque.removeFront()); // 3
console.log(deque.removeFront()); // 2
console.log(deque.removeFront()); // 5
console.log(deque.removeFront()); // 6
console.log(deque.removeFront()); // undefined
Methods will return undefined, when an attempt is made to retrieve a value from an empty deque.
Dequeue implementation in a simple way:
const dequeue = [];
// push element from rear end
dequeue.push(3); // [3]
dequeue.push(8); // [3, 8]
// push element from front end
dequeue.unshift(5); // [5, 3, 8]
dequeue.unshift(11); // [11, 5, 3, 8]
// pop element from rear end
dequeue.pop(); // [11, 5, 3]
// pop element from front end
dequeue.shift(); // [5, 3]
The tricot's Doubly Linked List, but refactored and typed:
type DequeNode<T> = {
value: T;
prev?: DequeNode<T>;
next?: DequeNode<T>;
};
class Deque<T = any> {
front?: DequeNode<T>;
back?: DequeNode<T>;
constructor(...initialValues: T[]) {
initialValues.forEach(initialValue => {
this.addBack(initialValue);
});
}
addFront(value: T) {
if (!this.front) {
this.front = this.back = { value };
return;
}
this.front = this.front.next = { value, prev: this.front };
}
removeFront() {
if (!this.front) {
return;
}
const value = this.peekFront();
if (this.front === this.back) {
this.front = undefined;
this.back = undefined;
return value;
}
(this.front = this.front.prev!).next = undefined;
return value;
}
peekFront() {
return this.front?.value;
}
addBack(value: T) {
if (!this.front) {
this.front = this.back = { value };
return;
}
this.back = this.back!.prev = { value, next: this.back };
}
removeBack() {
if (!this.back) {
return;
}
const value = this.peekBack();
if (this.front === this.back) {
this.front = undefined;
this.back = undefined;
return value;
}
(this.back = this.back.next!).prev = undefined;
return value;
}
peekBack() {
return this.back?.value;
}
}
As with any other attempt at understanding new stuff, it is helpful to have a comparative approach.
JS arrays are deques because you can modify the front out-of-the-box. You don't get this in Python where the out-of-the-box list structure supports only modification in the back (calling it append and pop). If you need to start adding and removing front items you need to explicitly add support for deques (by adding from collections import deque at the top of the module ) and create the object with a dedicated constructor (d = deque([1,2,3]).Only then you can perform popleft and appendleft operations (called unshift and shift in JS)
Again, none of this is required in JS, the implementation of JS array supports this OOTB. To get a feeling for terminology across the language landscape see the Wikipedia's table at
https://en.wikipedia.org/wiki/Double-ended_queue#Operations

My check for whether a graph is a Binary Tree always returns false

I have this question that is medium level and couldn't even think on how to solve this problem, my solution could be overkill as I have no idea on how to traverse a bunch of numbers in an array to check whether it is a binary tree or not. The program always returns false no matter what
If you have a better answer to the question that would be perfect
Have the function TreeConstructor(strArr) take the array of strings stored in strArr, which will contain pairs of integers in the following format (i1, i2) where i1 represents a child a node in a tree and the second integer i2 signifies that it is the parent of i1. For example if strArr is ["(1,2)", "(2,4)", "(7,2)"]
4
/
2
/ \
1 7
which you can see forms a proper binary tree. Your program should, in this case, return the string true because a valid binary tree can be formed. If a proper binary cannot be formed with the integer pairs, then return the string false. All of the integers within the tree will be unique, which means there can only be one node in the tree with the given integer value
Examples
input: ["(1,2)", "(2,4)", "(5,7)", "(7,2)", "(9,5)"]
output: true
input ["(1,2)", "(1,3)"]
output: false
I came out with an attempt, but it always returns false. Most likely my code is overkill.
class Node {
// The constructor
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
// Basic insert node
insert(value) {
let currentNode = this;
while (true) {
if (value < currentNode.value) {
if (currentNode.left === null) {
currentNode.left = new Node(value);
break;
} else {
currentNode = currentNode.left;
}
} else {
if (currentNode.right === null) {
currentNode.right = new Node(value);
break;
} else {
currentNode = currentNode.right;
}
}
}
return currentNode
}
// check if BST is valid or not
isValidBST(node, min = null, max = null) {
if (!node) return true;
if (max !== null && node.value >= max) {
return false;
}
if (min !== null && node.value <= min) {
return false;
}
const leftSide = this.isValidBST(node.left, min, node.value);
const rightSide = this.isValidBST(node.right, node.value, max);
return leftSide && rightSide;
}
}
// Convert the strings to a number
function convertListToNumber(str, i) {
return str[i].split('(').join('').split(')').join('').split(',').join('')
}
This is the main function
function TreeConstructorTwo(strArr) {
// code goes here
startValueFromList = convertListToNumber(strArr, 0)
// Parent Node here
startParentNode = startValueFromList[1];
// Child Node here
startChildNode = startValueFromList[0];
// Add parent Node and childNode
node = new Node(startParentNode);
node.insert(startChildNode);
// Loop through the entire array
for (i = 1; i < strArr.length; i++) {
myListValue = convertListToNumber(strArr, i);
console.log(myListValue.length)
// Loop the "12" in the string and convert it to a number
for (j = 0; j < myListValue.length; j++) {
node.insert(myListValue[j])
}
parentNode = Number(myListValue[0])
}
// Check if the BST is valid or not
return node.isValidBST(node)
}
// keep this function call here
console.log(TreeConstructorTwo(["(1,2)", "(2,4)", "(5,7)", "(7,2)", "(9,5)"]));
You seem to have misunderstood the assignment. The function should return true when the represented tree is a binary tree, not necessarily a binary search tree.
Your code is creating a tree from the first element and then takes any next node to insert it into that tree keeping with the binary search property, without taking into account that the pair from the input demands that the first is a direct child of the second. (Your variable parentNode is not used for anything)
Instead, you should just look at the child-parent relationships that are given in the input as representing edges, and use that information to build the graph. Finally you should verify that that graph represents a binary tree. Think about what are the distinctive characteristics of a binary tree and how to verify them.
Hint 1:
No node should have two parents
Hint 2:
No node should have 3 children
Hint 3:
All upward paths should end in the same node (the root)
The spoiler solution below does not return true/false, but a string that indicates whether the tree is "ok", or why it is not. This is more useful for debugging and still easy to convert to a boolean.
// This function returns the reason why it considers the input
// not a binary tree. "ok" otherwise.
function isBinaryTree(edgesStr) {
const childToParent = new Map(edgesStr.map(edge => edge.match(/\d+/g)));
// No node should have 2 parents
if (childToParent.size < edgesStr.length) return "node with two parents";
// No node should have 3 children
const degree = {};
for (const [child, parent] of childToParent) {
if ((++degree[parent] || (degree[parent] = 1)) > 2) return "node with three children";
}
// All upward paths lead to the same root (no cycles)
const nodes = {};
let current = 0;
let countRoots = 0;
for (let node of childToParent.keys()) {
current++;
while (node && !nodes[node]) {
nodes[node] = current;
node = childToParent.get(node);
}
if (!node && countRoots++) return "disconnected";
if (node && nodes[node] == current) return "cycle";
}
return "ok";
}
const tests = [
["(2,1)", "(3,1)", "(4,2)", "(5,2)", "(6,3)", "(7,3)"],
["(1,2)", "(3,2)", "(2,12)", "(5,2)"],
["(2,1)", "(3,1)", "(5,4)", "(5,2)"],
["(2,1)", "(4,3)"],
["(1,2)", "(3,4)", "(4,5)", "(5,3)"],
];
for (const test of tests) {
console.log(isBinaryTree(test));
}
NB: I would name the function with an initial lowercase letter as it is the common practice to reserve initial capital letters for class names.
Recently I've did the solution for this task, check my solution below:
const isBinaryTree = (array) => {
const getChildrenNode = (node) => node.slice(1, 2);
const childrenNodes = array.map(x => getChildrenNode(x));
const isChildrenNodesIsUnique = (array) => array.length === new Set(array).size;
return isChildrenNodesIsUnique(childrenNodes);
};
console.log(isBinaryTree(["(1,2)", "(2,4)", "(5,7)", "(7,2)", "(9,5)"]));
console.log(isBinaryTree(["(1,2)", "(1,3)"]));
I did this by collecting the parent ids in an array and checking if any parent id is exists more than twice. Not sure though it will
["(1,2)", "(3,2)", "(2,12)", "(5,2)"]
since 2 came 3 times at the right of the pair its a false (a root or leaf node cannot have more than 2 childs)
const isBinaryTree = (array) => {
// find the parent child
const parentChildFreq = (value) => {
const charFreqency = {};
for (let index = 0; index < value.length; index++) {
if (charFreqency[value[index]]) {
// if parent have more the 2 children then this is not binary-tree
if (charFreqency[value[index]] >= 2) {
return false;
}
charFreqency[value[index]] += 1;
} else charFreqency[value[index]] = 1;
}
return true;
};
const isChildrenNodesIsUnique = (array) =>
array.length === new Set(array).size;
const childrenNodes = array.map((node) => node[1]);
let parentsNodes = array.map((node) => node[3]);
parentsNodes = Array.from(parentsNodes, Number);
return isChildrenNodesIsUnique(childrenNodes) && parentChildFreq(parentsNodes)
? " \n Binary-tree"
: "not-binary-tree";
};
console.log(isBinaryTree(["(1,2)", "(2,4)", "(5,7)", "(7,2)", "(9,5)"]));
//console.log(isBinaryTree(["(1,2)", "(3,2)", "(2,12)", "(5,2)"]));
I had it at a coding interview, could not make it on time but later I did this. It seems to be working but it is as good as my tests are. It seems that this two conditions are enough:
-A node can have less than 3 child.
-Every node has a parent but one.
function ArrayChallenge(strArr) {
//Make a num array
const numArray = strArr.map((str) => {
var mySubString = str.substring(str.indexOf("(") + 1, str.lastIndexOf(")"));
return mySubString.split(",").map(Number);
});
//Get parent nodes
const parents = numArray.map((arr) => {
return arr[1];
});
//Get children nodes
const children = numArray.map((arr) => {
return arr[0];
});
//Conditions
//a parent can't have 3 children
const threeChildrenCondition = findRepeated(parents) < 3 ? true : false;
const allHaveParentsButOneCondition = allHaveParentsButOne();
//every node has a parent except for one
function allHaveParentsButOne() {
var parentOfAll = parents.filter(function (num) {
return children.indexOf(num) == -1;
});
//Will remove duplicate numbers (parent appearing two times)
let unique = [...new Set(parentOfAll)];
const result = unique.length == 1 ? true : false;
return result;
}
//Returns how many times a number is repeated in an array
function findRepeated(array) {
let repeatedNumbers = [];
for (let i = 0; i < array.length; i++) {
for (let j = i + 1; j < array.length; j++) {
if (array[i] === array[j]) {
repeatedNumbers.push(array[i]);
}
}
}
const length = repeatedNumbers.length;
return length;
}
const result = threeChildrenCondition && allHaveParentsButOneCondition;
return result;
}
//CHALLENGES
//Must be false
const f0 = ["(1,2)", "(3,2)", "(2,12)", "(5,2)"];
const f1 = ["(1,4)", "(3,2)", "(2,12)", "(5,2)"];
const f2 = ["(1,7)", "(3,7)", "(2,7)"];
const f3 = ["(10,20)", "(20,50)", "(20,8)", "(8,4)"];
console.log(ArrayChallenge(f0));
console.log(ArrayChallenge(f1));
console.log(ArrayChallenge(f2));
console.log(ArrayChallenge(f3));
//Must be true
const t0 = ["(1,2)", "(2,4)", "(5,7)", "(7,2)", "(9,5)"];
const t1 = ["(2,3)", "(1,2)", "(4,9)", "(9,3)", "(12,9)", "(6,4)"];
const t2 = ["(1,2)", "(2,4)", "(7,4)"];
const t3 = ["(5,6)", "(6,3)", "(2,3)", "(12,5)"];
const t4 = ["(10,20)", "(20,50)"];
console.log(ArrayChallenge(t0));
console.log(ArrayChallenge(t1));
console.log(ArrayChallenge(t2));
console.log(ArrayChallenge(t3));
console.log(ArrayChallenge(t4));
public static bool TreeConstructor(string[] strArr)
{
var treeList = new List<int[]>();
foreach (var node in strArr)
{
treeList.Add(Array.ConvertAll(node.Replace("(", "").Replace(")", "").Split(","), s => int.Parse(s)));
}
bool isChildNotUnique = treeList.GroupBy(s => s[0]).Any(g => g.Count() > 1);
bool isMoreThanTwoChild = treeList.GroupBy(s => s[1]).Any(g => g.Count() > 2);
int[] rootNode = null;
if (isChildNotUnique || isMoreThanTwoChild)
return false;
foreach (var node in treeList)
{
var childNodes = treeList.Where(s => node[1] == s[1]);
bool leftChild = childNodes.Any(s => s[0] < node[1]);
bool rightChild = childNodes.Any(s => s[0] > node[1]);
if (childNodes.Count() > 1 && (!rightChild || !rightChild))
{
return false;
}
else if (!leftChild && !rightChild)
return false;
bool isNotRootNode = treeList.Any(s => s[0] == node[1]);
if (!isNotRootNode)
if (rootNode != null && rootNode[1] != node[1])
return false;
else
rootNode = node;
}
return true;
}

Javascript: Detect and Remove a loop from cylic linked list

I have used Javascript to write a circular linked list and to detect and remove the loop.It is working fine untill the part of loop detection. How ever it is failing to remove the loopnode. More specifically: the removeLoop function of this code doesnot work.
Here is my code:
function Node(element){
this.element = element;
this.next = null;
}
//circular linked list class
function LList() {
this.head = new Node("head");
this.head.next = this.head;
this.find = find;
this.insert = insert;
this.display = display;
}
function find(item){
var curr = this.head;
while(curr.element != item){
curr = curr.next;
}
return curr;
}
//inserting items into linked list
function insert(newElem, after){
var newNode = new Node(newElem);
var curr = this.find(after);
newNode.next = curr.next;
curr.next = newNode;
}
function display() {
var currNode = this.head;
while ((currNode.next !== null) &&
(currNode.next.element !== "head")) {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
function findPrevious(item){
var curr = this.head;
while(curr.next !== null && curr.next.element !== item){
curr =curr.next;
}
return curr;
}
//creating a linkedlist object
var furniture = new LList();
furniture.insert("chair","head");
furniture.insert("table", "chair");
furniture.insert("couch", "table");
furniture.insert("stool","couch");
//furniture.display();
//detecting if a linked list is circular
function detectALoop(list){
var slow = list.head;
var fast = list.head;
while(slow && fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
removeLoop (slow, list);
return 1;
}
}
return 0;
}
//This part of the code doesnot work
function removeLoop(loopNode, list)
{
var ptr1 = loopNode;
var ptr2 = loopNode;
var looplen = 1,i;
// count the number of nodes in loop
while(ptr1.next != ptr2)
{
ptr1 = ptr1.next;
looplen++;
}
console.log(looplen)
ptr1 = list.head;
ptr2 = list.head;
for(i=0; i <= looplen; i++)
{
ptr2 = ptr2.next;
}
while(ptr2.next != ptr1.next)
{
ptr1 = ptr1.next;
ptr2 = ptr2.next;
}
ptr2.next = null; // breaking the loop
}
console.log(detectALoop(furniture))
furniture.display();
You are making this a lot more complicated than it needs to be if the loop has to be back onto the first element.
function breakLoop(list) {
var head = list.head, tail = head, len = 1;
while (tail.next != head) {
len++;
tail = tail.next;
}
tail.next = null;
console.log(len.toString());
}
Now if you may need to handle any arbitrary loop, I still have no idea what you need 3 loops for. Use an ES6 Set; most browsers now support this, I believe. I'm going to go ahead and return the length instead of logging it.
function breakLoopAnywhere(list) {
var seen = new Set, node = list.head;
while (!seen.has(node.next)) {
seen.add(node);
node = node.next;
}
node.next = null;
return seen.size;
}
If you don't have sets, you can hack it with an array, replacing has with indexOf and add with push.
If you feel you must have the ability to detect a loop vs a non-looping list without breaking it:
// takes a node, returns the node
// that points backwards on its next
function getLoopNode(node) {
var seen = new Set;
do {
seen.add(node);
} while (!seen.has(node.next) && node = node.next)
return node;
}
function detectLoop(node) {
return getLoopNode(node) != null;
}
function breakLoop(node) {
node = getLoopNode(node);
if (node) node.next = null;
}
Your detectALoop is less complicated, but it's wrong. The only loop this will detect is if node 2i loops back onto node i. But the list could be 3 elements long looping onto the start; it could be lots of numbers that aren't 2i and i. Since there are probably a lot of numbers, way too many to try them all, you can't fix this strategy. There is no clever way to find cycles in a graph that is any faster or more intuitive than the one I wrote above. As far as I know.
This variable is messed up...
var looplen = 1,i;
It looks like you want it to be a 1.
Your removeLoop code is wrong, it never terminates:
let's assume this list:
A -> B -> C -> A
with loop length 3.
You correctly find the loop length, 3, you then set ptr1 and ptr2 to the head of the list, and then call .next on ptr2 for the length of the loop + 1 times (because of <=).
// for i = 0; i <= 3
A.next -> B // i = 0
B.next -> C // i = 1
C.next -> A // i = 2
A.next -> B // i = 33
So in the end you have ptr2 = B and ptr1 = A, i.e. ptr2 === ptr1.next!
One is the next of the other, and in the while loop you advance both until one is equal to the other, but they will never be, because they always be one the next of the other!
If you change the <= to just < it works, but the second while loop is actually useless.

Categories