I just struggled through a simple interview question: Please reverse a singly linked list.
While I failed to provide a working answer in time to save the interview, I was able to come up with a solution afterwards.
Is my solution correct? How would you analyze this with Big-Oh? Are there more efficient ways to reverse a singly linked list?
// reverse a linked list
var reverseLinkedList = function(linkedlist) {
var node = linkedlist;
var previous = null;
while(node) {
// reverse pointer
node.next = previous;
// increment previous to current node
previous = node;
// increment node to next node
if (node.next){
node = node.next
} else {
node = null;
}
}
}
Note: In my search for similar posts, I did find one example in JavaScript. I was wondering if my code is possible (without a temp variable). Thank you.
There are a couple of problems with your code. This should make it clear.
// reverse a linked list
var reverseLinkedList = function(linkedlist) {
var node = linkedlist;
var previous = null;
while(node) {
// save next or you lose it!!!
var save = node.next;
// reverse pointer
node.next = previous;
// increment previous to current node
previous = node;
// increment node to next node or null at end of list
node = save;
}
return previous; // Change the list head !!!
}
linkedlist = reverseLinkedList(linkedlist);
You could solve this problem recursively in O(n) time as ckersch mentions. The thing is, that you need to know that recursion is memory intensive since functions accumulate in the calls stack until they hit the stop condition and start returning actual things.
The way I'd solve this problem is:
const reverse = (head) => {
if (!head || !head.next) {
return head;
}
let temp = reverse(head.next);
head.next.next = head;
head.next = undefined;
return temp;
}
When reverse() reaches the end of the list, it will grab the last node as the new head and reference each node backwards.
This would be O(n) in time, since you do a constant number of operations on each node. Conceptually, there isn't a more efficient way of doing things (in terms of big-O notation, there's some code optimization that could be done.)
The reason why you can't exceed O(n) is because, in order to do so, you would need to skip some nodes. Since you need to modify each node, this wouldn't be possible.
Efficiency then comes down to a constant factor. The fewer operations you can do per item in the list, the faster your code will execute.
I'd implement like this:
function reverseLinkedList(list, previous){
//We need to use the the current setting of
//list.next before we change it. We could save it in a temp variable,
//or, we could call reverseLinkedList recursively
if(list.next !== null){
reverseLinkedList(list.next, list);
}
//Everything after 'list' is now reversed, so we don't need list.next anymore.
//We passed previous in as an argument, so we can go ahead and set next to that.
list.next = previous;
}
reverseLinkedList(list, null);
Of course, this is recursive, so it would be inefficient in terms of space, but I like recursive code :)
This also doesn't return the reversed linked list, but we could fairly easily modify things to do so if that were important.
ES6 solution:
Just keep a track of the reversed list and keep adding that to tmp.
const reverseLinkedList = (head) => {
let reversed = null;
while(head) {
const tmp = head;
head = head.next;
tmp.next = reversed;
reversed = tmp;
}
return reversed;
};
console.log(JSON.stringify(reverseLinkedList({
data: 1,
next: {
data: 2,
next: {
data: 3,
next: {
data: 4,
next: {
data: 5,
next: {
data: 5,
next: {
data: 6
}
}
}
}
}
}
})));
Reversing the SinglyLinkedList:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
To understand The Solution we have to keep track of previous head and next variables
for example in above input Head = 1 ; next = 2 we don't have previous so assume previous = null
loop the list till head is not null. reverse the connections(previous and next) of head.
Below is the code
var reverseList = function(head) {
let previous = null;
while(head !== null){
let next = head.next;
head.next = previous;
previous= head
head = next;
}
return previous;
};
//O(n) | O(1) wherre n is the number of nodes in the linked list
class Node{
constructor(val){
this.val = val;
this.next = null;
}
}
function reverseLinkedList(head) {
if(!head) return null;
let p1 = head;
let p2 = null;
while(p1){
let temp = p1.next;
p1.next = p2;
p2 = p1;
p1 = temp;
}
return p2;
}
const a = new Node(1);
a.next = new Node(2);
a.next.next = new Node(3)
console.log("Current Node",a);
console.log("Reversed List",reverseLinkedList(a))
class LinkedList {
constructor () {
this.head = this.tail = null
}
// add to the end of the list
append (value) {
if (!this.tail) {
this.head = this.tail = new Node(value)
} else {
let oldTail = this.head
this.head = new Node(value)
this.head.next = oldhead
}
}
reverseList() {
//your code here
let currentNode = this.head
this.head = null
while(currentNode) {
if (!this.head) {
this.head = new Node(currenthead.data)
} else {
let oldhead = this.head
this.head = new Node(currentNode.data)
this.head.next = oldhead
}
currentNode = currentNode.next
}
}
}
class Node {
constructor (value, next) {
this.data = value
this.next = next || null
}
}
const list = new LinkedList()
list.append(1)
list.append(2)
list.reverseList()
Since inserting data at the beginning of the linked list pushes other first nodes till the end, and since it's a O(1) process.
Then I created the following function reverse()
where it basically insert node elements in the beginning which basically will get a reversed list at the end.
Here's a demo down below:
class Node {
constructor(data, next = null) {
this.data = data;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
insertFirst(data = null) {
// make new head point to the previous head
this.head = new Node(data, this.head);
this.size ++;
}
insertLast(data = null) { // insert last in the beginning will be the first in the linked list
const node = new Node(data);
// If empty, insert first
if (!this.head) this.insertFirst(data);
else {
let current = this.head;
// while next is not null, continue
while (current.next)
current = current.next;
// eventually next is null, we want to set next here to the node we want to add
current.next = node;
}
this.size ++;
}
// print linked list
print() {
let current = this.head;
let output = "";
while (current) { // while current is not null, eventually it will be null
output += current.data + " => ";
current = current.next; // current jumping to the next node
}
output += "| NULL"; // ending
console.log(output);
return output;
}
reverse() {
if (!this.head) return; // if no head, do nothing
let current = this.head;
const linkedList = new LinkedList(); // create a new linked list
// don't worry, it will be garbage collected once this function ends since it's not a global variable
while (current) {
linkedList.insertFirst(current.data); // insert first at the beginning will be the end of the linked list at the end
current = current.next;
}
// assign current head to the reversed linked list head
this.head = linkedList.head;
}
}
const linkedList = new LinkedList();
// fill data as 100 -> 200 -> 300 -> 400
linkedList.insertLast(100);
linkedList.insertLast(200);
linkedList.insertLast(300);
linkedList.insertLast(400);
// To view results
const bodyElement = document.getElementsByTagName("body")[0];
bodyElement.innerHTML = `<p>Original Linked List: <b>${linkedList.print()}</b></p>`; // 100 200 300 400
linkedList.reverse();
bodyElement.innerHTML += `<p>Reversed Linked List: <b>${linkedList.print()}</b></p>`; // 400 300 200 100
b {
color: green;
}
<body></body>
Overall, the whole process of this reverse() function is O(n).
Hopefully this sounds clear to you, and correct me if I'm wrong.
This is my recursive solution:
https://codesandbox.io/s/reverse-linked-list-tqh2tq?file=/src/index.js
let d = { me: "d" };
let c = { me: "c", next: d };
let b = { me: "b", next: c };
let a = { me: "a", next: b };
const reverseMe = (o) => {
let lastDude;
if (o.next.next) lastDude = reverseMe(o.next);
else lastDude = o.next;
o.next.next = o;
o.next = null;
return lastDude;
};
console.log("result", reverseMe(a));
Related
I have tried working on this for a good while and can't seem to find a way to terminate the loop. I am not sure if I am even on the right track. I am trying to do a breadth first (level order) and apply a callback on each node while doing the traversal.
Here is the constructor function and the method to do the breadth first search...
function BinarySearchTree(value) {
this.value = value;
this.right = null;
this.left = null;
}
BinarySearchTree.prototype.add = function(value) {
if (value < this.value) {
if (this.left) this.left.add(value);
else this.left = new BinarySearchTree(value);
}
if (value > this.value){
if (this.right) this.right.add(value);
else this.right = new BinarySearchTree(value);
}
};
BinarySearchTree.prototype.breadthFirst = function(callback) {
let queue = [];
queue.push(this.value);
while (queue.length) {
queue.pop();
callback(this.value);
if (this.left) queue.push(this.left);
if (this.right) queue.push(this.right);
}
};
Any ideas as to why I am getting an infinite loop? Any tips or help will be greatly appreciated!
UPDATED: Sample data...
var array = [];
var func = function(value){ array.push(value); };
binarySearchTree.add(2);
binarySearchTree.add(3);
binarySearchTree.add(7);
binarySearchTree.add(6);
console.log(binarySearchTree.breadthFirst(func)); -> should output [ 5, 2, 3, 7, 6 ]
I have tried this...
BinarySearchTree.prototype.breadthFirst = function(callback) {
const queue = [];
let queueLength = this.value.length;
if (queueLength) {
queueLength--;
callback(this.value);
if (this.left) {
queue.push(this.left);
this.left.breadthFirst(callback);
}
if (this.right) {
queue.push(this.right);
this.right.breadthFirst(callback);
}
};
};
and this...
BinarySearchTree.prototype.breadthFirst = function(callback) {
const queue = [];
let queueLength = this.value.length;
while (queueLength) {
queueLength--;
callback(this.value);
if (this.left) {
queue.push(this.left);
callback(this.left);
}
if (this.left) {
queue.push(this.left);
callback(this.left);
}
};
};
as well as other variations and I still get an empty array as output!
Since you are removing a value from the queue array by using pop method but then after callback function is called with this.value you are pushing to the queue array the condition of while statement is always true which is causing the infinite loop.
maybe you can have something like this;
BinarySearchTree.prototype.breadthFirst = function(callback) {
const queue = [];
let queueLength = this.value.length;
while (queueLength) {
queueLength--;
callback(this.value);
if (this.left) queue.push(this.left);
if (this.right) queue.push(this.right);
};
};
Apart from the issue that you fixed in the first update to your question, there are still some other issues:
The driver code with sample input lacks a statement that calls the constructor, which I assume is this one:
var binarySearchTree = new BinarySearchTree(5);
This binarySearchTree will be the this object when breadthFirst is executed. The value of this does not change during the loop, so the loop keeps pushing the same this.left and this.right nodes
Although pop() is called on the queue, its return value is ignored, yet that is what you need to process.
The queue is initialised with a node value, but the queue should have nodes, not node values. Otherwise you can never retrieve the node's children.
The final console.log in your driver code is not going to print anything useful, since the breadthFirst method is not designed to return anything. Instead it provides its results via the callback. So this console.log is useless.
As the callback in the driver code collects values in an array, you would want to print that array when the tree traversal has completed.
Correction
Here is your script with those issues resolved:
function BinarySearchTree(value) {
this.value = value;
this.right = null;
this.left = null;
}
BinarySearchTree.prototype.add = function(value) {
if (value < this.value) {
if (this.left) this.left.add(value);
else this.left = new BinarySearchTree(value);
}
if (value > this.value){
if (this.right) this.right.add(value);
else this.right = new BinarySearchTree(value);
}
};
BinarySearchTree.prototype.breadthFirst = function(callback) {
let queue = [];
queue.push(this); // Don't push the value, push the root node
while (queue.length) {
let node = queue.pop(); // Pop returns the node: capture it!
callback(node.value); // Work with that node, not with `this`
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
};
var array = [];
var func = function(value){ array.push(value); };
var binarySearchTree = new BinarySearchTree(5); // This was missing
binarySearchTree.add(2);
binarySearchTree.add(3);
binarySearchTree.add(7);
binarySearchTree.add(6);
binarySearchTree.breadthFirst(func); // Does not return anything: don't print
console.log(array); // Output what was collected: [ 5, 2, 3, 7, 6 ]
Modern version
Some aspects of your code could use some nicer programming patterns:
There is no way to define an empty tree with your code. It would be better to define a container that supports this state, meaning that you don't have to call its constructor with a first value, but can add all values using the add method.
Since ECMAScript 2015 we can use the class syntax
Instead of using a callback system, use a generator
Here is how the code would look with these ideas and some other updates:
class Node {
constructor(value) {
this.value = value;
this.right = this.left = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
add(value) {
function recur(node, value) {
if (!node) return new Node(value);
if (value < node.value) {
node.left = recur(node.left, value);
} else if (value > node.value) {
node.right = recur(node.right, value);
}
return node;
}
this.root = recur(this.root, value);
}
*breadthFirst() {
const queue = [];
if (this.root) queue.push(this.root);
while (queue.length) {
const node = queue.pop();
yield node.value;
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
}
const binarySearchTree = new BinarySearchTree;
for (const value of [5, 2, 3, 7, 6]) binarySearchTree.add(value);
console.log(...binarySearchTree.breadthFirst());
I'm working on a problem where given an array of file paths I would like to print the file structure. For example with the given array ["/a/b/c", "a/a/a", "/a/b/d"], the ideal structure would look like :
a
b
c
d
a
a
But my structure ends up looking more like this:
a
b
c
a
a
a
b
From what I can gather this is being caused by my tree not recognizing when a node already exists in a tree. So it is adding the node "a" three times as opposed to recognizing that an "a" already exists and traversing into it.
let paths = ["/a/b/c", "a/a/a", "/a/b/d"]
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(element) {
this.children.push(element)
}
}
const head = new TreeNode('Head');
let cur = head;
paths.forEach(element => {
cur = head;
let filePath = element.split('/');
filePath.shift();
filePath.forEach(element => {
let newNode = new TreeNode(element);
if(!cur.children.includes(newNode)) {
cur.addChild(newNode);
cur = cur.children[cur.children.length - 1];
} else {
cur = cur.children.indexOf(newNode);
}
})
})
var spaceAppend = function(num) {
let i = 0;
let space = "";
while(i < num) {
space += " ";
i++;
}
return space;
}
var traverse = function(node, level = 0){
if(node === null)
return;
console.log(spaceAppend(level), node.value)
if(node.children) {
for(const n of node.children) {
traverse(n, level + 1);
}
}
}
traverse(head)
Is there an issue with my tree implementation?
Some issues:
.includes() is not the right way to find a matching value. Use .find() instead.
.indexOf() will return an index, so that is not the right value you want to assign to cur in the else block.
shift may throw away an essential part of the path when it does not start with /. You can ease the processing by using .match() instead of .split(), so that you get exactly the non-empty parts of the path.
Less of an issue:
There is no need to define cur outside of the outer loop.
JavaScript has a native function for something like spaceAppend. You can use .repeat().
new TreeNode(element) is also called when you actually don't need it. Only create a new node when you know there is no matching node.
You could replace the inner .forEach() loop with .reduce(), which gives a better scope-handling for the cur variable.
Here is your code with those remarks taken into account:
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
addChild(element) {
this.children.push(element);
}
}
let paths = ["/a/b/c", "a/a/a", "/a/b/d"];
const head = new TreeNode('Head');
paths.forEach(element => {
// Use .match() to only get non-empty elements
let filePath = element.match(/[^\/]+/g);
filePath.reduce((cur, element) => {
// Use .find() instead of .includes()
let node = cur.children.find(child => child.value === element);
// Only create the node when needed:
if (!node) {
node = new TreeNode(element);
cur.addChild(node);
}
// Walk down one step in the tree
return node; // ...becomes the value of `cur`
}, head); // Initial value of reduction
});
const traverse = function(node, level=0) {
if (node === null) return;
// Use .repeat():
console.log(" ".repeat(level), node.value);
if (node.children) {
for (const n of node.children) {
traverse(n, level + 1);
}
}
}
traverse(head);
Is the starter array meant to be ["/a/b/c", "/a/a/a", "/a/b/d"] ("/a/a/a" instead of ("a/a/a")?
I think the crux of the problem you're having is the line
if(!cur.children.includes(newNode)) { ... }
When a new node is created, even if it has the same value as a previous one, it will not result in equity when comparing the two TreeNode objects. You need to compare the value of the nodes, not the nodes themselves.
So an example with a simplified version of your node object:
class TreeNode {
constructor(value) {
this.value = value;
}
}
a1 = new TreeNode('a');
a2 = new TreeNode('a');
console.log("a1 == a2");
console.log(a1 == a2); // false
console.log("a1.value == a2.value");
console.log(a1.value == a2.value); // true
I adjusted the inner forEach loop with one that compares the values instead of the TreeNode objects
filePath.forEach(element => {
let newNode = new TreeNode(element);
let tempNode = null;
for (var i = 0; i < cur.children.length; i++) {
if (cur.children[i].value == newNode.value) {
tempNode = cur.children[i];
}
}
if (tempNode == null) {
cur.addChild(newNode);
cur = newNode;
} else {
cur = tempNode;
}
});
Full code snippet on codepen
Object equality in javascript isn't particularly nice to deal with see this other answer for more information
Here is a solution using lodash and object-treeify. While it's simpler code, there is obviously a trade-off introducing additional dependencies.
This solution works by first converting the paths into a tree structure and then visualizing it using object-treeify
// const lodash = require('lodash');
// const objectTreeify = require('object-treeify');
const myPaths = ['/a/b/c', 'a/a/a', '/a/b/d'];
const treeify = (paths) => objectTreeify(paths.reduce((p, c) => {
lodash.set(p, c.match(/[^/]+/g));
return p;
}, {}), {
spacerNoNeighbour: ' ',
spacerNeighbour: ' ',
keyNoNeighbour: '',
keyNeighbour: ''
});
console.log(treeify(myPaths));
/* =>
a
b
c
d
a
a
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/lodash#4.17.20"></script>
<script src="https://bundle.run/object-treeify#1.1.31"></script>
Disclaimer: I'm the author of object-treeify
I have written the following function in JavaScript to check if a singly Linked List is a palindrome. However, I'm failing 2 out of 10 tests, and I can't figure out why.
Here are the tests I'm falling.
l: [0, 1, 0]
l: [1, 1000000000, -1000000000, -1000000000, 1000000000, 1]
Both should return true for the palindrome, but my function is returning false.
Here's my code:
function isListPalindrome(l) {
let curr = l;
let arr = [];
if (l == null)
return true;
// push all elements of l into the stack.
// a stack in JS is an array.
while(curr != null){
arr.push(l.value);
// move ahead:
curr = curr.next;
}
let curr2 = l;
// Traverse the list again & check by popping from the stack:
while(curr2 != null){
// get the top most element on the stack:
let num = arr.shift();
// check if the node data isn't the same as the element popped:
if (curr2.value != num){
return false;
}
// move ahead:
curr2 = curr2.next;
}
return true;
}
Thank you!
Inside the first while loop you're pushing l.value but l is not being incremented so it's pushing the same value to arr.
You now have arr which is suppose to be l in reverse. In the second while loop, instead of using arr.shift() use arr.pop(). This will take the first element off the arr stack. Remember that a stack is first in, last out.
Notice also that when you're comparing the list front to back you'll reach a point of irrelevancy, the half way point. Once you know that half the values in the forward list are the same as the values in the reverse list you know the rest will pass the test.
Here's what it's suppose to look like. You should try to figure out how to do odds yourself.
function isListPalindrome(l) {
let curr = l;
let arr = [];
if (l == null)
return true;
// push all elements of l into the stack.
// a stack in JS is an array.
while(curr != null){
arr.push(curr.value);
// move ahead:
curr = curr.next;
}
let curr2 = l;
let length = arr.length;
// Traverse the list again & check by popping from the stack:
while(curr2 != null){
// get the top most element on the stack:
let lastNum = arr.pop();
// check if the node data isn't the same as the element popped:
if (curr2.value != lastNum){
return false;
}
// Half way point for evens
if (length / 2 === arr.length) {
return true;
}
// move ahead:
curr2 = curr2.next;
}
return true;
}
solving with pushing values to an array and then check if the array is palindromic will have S:O(N). with reversing the second half and then traversing will have S:O(1). T:O(N) is same for both:
var isPalindrome = function (head) {
let fast_pointer = head;
let slow_pointer = head;
// when fast_ppointer reaches to the tail, slow_pointer will be in the middle
while (fast_pointer && fast_pointer.next) {
fast_pointer = fast_pointer.next.next;
slow_pointer = slow_pointer.next;
}
// now, slow_pointer is in the middle and we reverse from slow_pointer till the head
let prev = null;
while (slow_pointer) {
// slow_pointer=slow_pointer.next how we iterate in linked lists.
// so make sure we keep a reference to the next iteration
temp = slow_pointer.next;
slow_pointer.next = prev;
prev = slow_pointer;
slow_pointer = temp;
}
let left = head;
let right = prev;
while (right) {
if (left.val !== right.val) {
return false;
}
left = left.next;
right = right.next;
}
return true;
};
var isPalindrome = function (head) {
let values = [];
while (head) {
values.push(head.val);
head = head.next;
}
let rev = [];
head.map((e) => {
rev.unshift(e);
});
if (values.every((val, index) => val === rev[index])) {
return true;
} else {
return false;
}
};
class Node {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
const is_palindromic_linked_list = function (head) {
let front = head;
const traverse = (node) => {
if (!node) return true;
//reverse the LL
const reverse = traverse(node.next);
//check value if they are equal
const valChecker = front.value == node.value;
front = front.next;
return reverse && valChecker;
}
return traverse(head)
};
head = new Node(2)
head.next = new Node(4)
head.next.next = new Node(6)
head.next.next.next = new Node(4)
head.next.next.next.next = new Node(2)
console.log(`Is palindrome: ${is_palindromic_linked_list(head)}`)
head.next.next.next.next.next = new Node(2)
console.log(`Is palindrome: ${is_palindromic_linked_list(head)}`)
I push all the elements of the list in an array and then I convert the array with join function to a string so that i can compare if the string is the same as the inverse using reverse function if it is then it is a palindrome
function isListPalindrome(l) {
if(l === null) return true;
let array =[];
let current = l;
while (current != null){
array.push(current.value);
current = current.next
}
if(array.join('')=== array.reverse().join('')) return true;
return false
}
Conceptually closures make a bit of sense to what's happening, but in practice, I have no idea what's going on. For the problem I am dealing with I want to count the occurrences of numbers I read in and store it in a histogram. This is supposed to happen in the countOccurences function, but the histogram array never gets updated.
var LinkedList = function() {
this.head = null;
}
LinkedList.prototype.add = function(val) {
this.head = {data: val, next: this.head};
};
LinkedList.prototype.forEach = function(action) {
for (var temp = this.head; temp; temp = temp.next) {
action(temp.data);
}
};
// This is where the closure concept should take effect
var countOccurrences = function(histogram) {
return function(val){ // Not working as expected, The val should equal
// a number found in the list. Take for example if
// 1 is found then histogram[1]++. Meaning we've
// seen the number 1 once in the list so far. This
// continues until the entire list has been processed.
histogram[val]++;
};
}
function printHistogram(histogram) {
for (var i in histogram) {
if (histogram[i]) {
println("(#" + i + ":" + histogram[i] +")")
}
}
}
var main = function() {
var list = new LinkedList(); //Creates empty linkedlist
var histogram = [];
while (ln = readln().trim()) { //Reads in numbers from stdin
list.add(ln)
}
list.forEach(countOccurrences(histogram))
printHistogram(histogram)
}; main()
The actual closure was going wrong because of the lack of a return statement initially. However, after that, I found another bug that was unrelated to closure. The problem was in the function because I was incrementing a number that wasn't initialized. So the fix is as follows:
var countOccurrences = function(histogram) {
return function(val) {
if(histogram[val])
histogram[val]++;
else
histogram[val] = 1;
};
}
Regardless, much apperciation for the help.
Your closure is not actually returning a function. Not exactly sure how you are counting or what those values will be but you definitely need to start by returning your function from your closure.
var LinkedList = function() {
this.head = null;
}
LinkedList.prototype.add = function(val) {
this.head = {
data: val,
next: this.head
};
};
LinkedList.prototype.forEach = function(action) {
for (var temp = this.head; temp; temp = temp.next) {
action(temp.data);
}
};
// This is where the closure concept should take effect
var countOccurrences = function(histogram) {
return function(val) { // Not working as expected, I'm tring to get val to
// be the data passed in from forEach which will
// then be incremeted accordingly in the histogram
// array
histogram[val] = histogram[val] ? histogram[val] + 1 : 1;
};
}
function printHistogram(histogram) {
for (var i in histogram) {
if (histogram[i]) {
println("(#" + i + ":" + histogram[i] + ")")
}
}
}
var main = function() {
var list = new LinkedList(); //Creates empty linkedlist
var histogram = [];
while (ln = readln().trim()) { //Reads in numbers from stdin
list.add(ln)
}
list.forEach(countOccurrences(histogram))
printHistogram(histogram)
};
main()
I have the following Node constructor:
const Node = function(data){
this.data = data
this.next = null
this.previous = null
}
that is used inside of my LinkedList constructor:
const LinkedList = function(){
this.head = new Node('head')
}
and I can insert nodes with the following method:
LinkedList.prototype.insert = function(item,after){
const newNode = new Node(item)
const curr = after ? this.find(after) : this.head
newNode.next = curr.next
newNode.previous = curr
curr.next = newNode
}
with the find method being:
LinkedList.prototype.find = function(item){
let currentNode = this.head
while(currentNode && currentNode.data !== item){
currentNode = currentNode.next
}
return currentNode
}
And can view the items as an array with the following method:
LinkedList.prototype.toArray = function(){
const arr = []
let currItem = this.head.next
while(currItem){
arr.push(currItem.data)
currItem = currItem.next
}
return arr
}
My issue is now I am trying to implement a switch function on the LinkedList where I can pass in two values and switch their location in the list. Below is what I have and it seems to work for items that are not next to each other:
LinkedList.prototype.switch = function(a,b){
const aNode = this.find(a),
bNode = this.find(b)
if(!aNode || !bNode){
throw new Error('Both nodes were not inside of the list')
}
const aNext = aNode.next,
aPrevious = aNode.previous,
bNext = bNode.next,
bPrevious = bNode.previous
aNode.next = bNext
aNode.previous = bPrevious
aNode.previous.next = aNode
bNode.next = aNext
bNode.previous = aPrevious
bNode.previous.next = bNode
}
I am wondering what I am doing wrong here that is causing this to make my computer hit infinite recursion when I swap elements that are right next to each other. For instance, the below lines of code works:
const list = new LinkedList()
list.insert(1)
list.insert(2,1)
list.insert(3,2)
list.switch(1,3)
list.toArray() // [3,2,1]
However if I have the following code, it
const list = new LinkedList()
list.insert(1)
list.insert(2,1)
list.switch(1,2)
list.toArray() // crashes terminal
I know it is a stupid logical error in my switch method but I cannot for the life of me figure out what.
The problem that I see is in your insert function. If you have a linked list with two items and you call insert('New Node', null) your list looks like this:
You still need to set the previous pointer to the new node like this:
LinkedList.prototype.insert = function(item,after){
const newNode = new Node(item);
const curr = after ? this.find(after) : this.head;
newNode.next = curr.next;
curr.next.previous = newNode; <----- This is the extra line
newNode.previous = curr;
curr.next = newNode;
}
If bNode.previous is null, and if you assign as the following,
aNode.previous = bPrevious
aNode.previous.next = aNode
then you are trying to reach the next field of null, which causes to crash.