trouble understanding how reversing a linked list works - javascript

I'm reversing a linked list. I know it's a trivial task, but I have an understanding issue.
I'm using a class syntax in JavaScript
class LinkedList {
constructor(value) {
this.head = {
value: value,
next: null
};
this.tail = this.head;
this.length = 1;
}
append(value) {
const newNode = {
value: value,
next: null
}
this.tail.next = newNode;
this.tail = newNode;
this.length++;
return this;
}
reverse() {
if (!this.head.next) {
return this.head;
}
let first = this.head;
this.tail = this.head;
let second = first.next;
let temp = null
while(second) {
temp = second.next;
second.next = first;
first = second;
second = temp;
console.log('first', first)
}
this.head.next = null;
console.log('first after', first)
this.head = first;
return this
}
}
let myLinkedList = new LinkedList(5);
myLinkedList.append(1)
myLinkedList.append(99)
myLinkedList.reverse()
The thing I can't understand is this: after the last iteration of the while loop the first variable should point to this object (it's console.log('first', first)):
{ value: 99,
next: { value: 1, next: { value: 5, next: [Circular] } } }
However, after the loop is over, first starts pointing at
this one, which gives us the right answer (it's console.log('first after', first)):
{ value: 99,
next: { value: 1, next: { value: 5, next: null } } }
I even tried to draw diagrams but still fail to understand why it happens (why first.next.next.next starts pointing at null)

It's because of the line this.head.next = null;.
first.next.next.next and this.head point to the same node: 5.
Below is your code with some extra comments:
reverse() {
// At the start of the invocation, the linked list will be:
// head: 5
// (mid): 1
// tail: 99
if (!this.head.next) {
return this.head;
}
let first = this.head;
this.tail = this.head;
let second = first.next;
let temp = null
while(second) {
temp = second.next;
second.next = first;
first = second;
second = temp;
console.log('first', first)
}
// At this point, the `next` pointer of each node is updated together
// with `this.tail`, but `this.head` still refers to the previous head (now tail).
// `this.head` and `first.next.next.next` reference the same node '5'.
this.head.next = null;
console.log('first after', first)
// Only after this assignment will the head be updated, resulting in the following linked list:
// head: 99
// (mid): 1
// tail: 5
this.head = first;
return this
}

Related

Create LinkedList with JavaScript but logging the instance is not as expected

I am learning the LinkedList data structure with JavaScript. So here is what I tried with the Chrome snippets
(function(){
function LinkedList() {
let Node = function(element) {
this.element = element;
this.next = null;
}
let head = null;
this.append = function(element) {
let node = new Node(element);
let current;
if (head == null) {
head = node;
} else {
current = head;
while(current.next){
current = current.next;
}
current.next = node;
}
}
};
let l1 = new LinkedList();
l1.append(3);
l1.append(4);
l1.append(2);
console.log(l1);
})()
Basically I created a LinkedList function with the append method, then I created a new instance named l1 and append 3 elements to it. At the end of the code, I am expecting to log the instance l1 which maybe something similar to
[
{element: 3, next: 4},
{element: 4, next: 2},
{element: 2, next:null},
]
But what I actually got is the LinkedList function rather than the new instance
LinkedList {append: ƒ}
append: ƒ (element)
So I am not sure what's my misunderstanding in between? and what might be the right way to test a LinkedList in JavaScript? Thanks
You can directly loop over the linked list to create an array.
(function(){
function LinkedList() {
let Node = function(element) {
this.element = element;
this.next = null;
}
let head = null;
this.append = function(element) {
let node = new Node(element);
let current;
if (head == null) {
head = node;
} else {
current = head;
while(current.next){
current = current.next;
}
current.next = node;
}
},
this.toArray = function(){
var arr = [];
for(var curr = head; curr != null; curr = curr.next) arr.push(curr);
return arr;
}
};
let l1 = new LinkedList();
l1.append(3);
l1.append(4);
l1.append(2);
console.log(l1.toArray());
})()
You can try console.log(l1.append(1)) But if you want to see all your data neat and clean you need to define a function in your linked list to console.log the data(or element, in your case) of each node

Implement the addInPos method inside the LinkedList prototype in Javascript

I need to implement the addInPos method inside the LinkedList prototype but I don't know what it is bad in my code...because the test no pass.
Implement the addInPos method inside the LinkedList prototype that must add an element in the indicated position. Both data will be provided as a parameter (pos, value). Where "pos" will be the position in which the "value" value should be added. In the event that the position in which the insertion is to be made is invalid, that is, it exceeds the size of the current list, it must return false.
If the node was added correctly return true.
Note: the zero position corresponds to the head of the LinkedList.
My code:
LinkedList.prototype.addInPos = function(pos, value) {
if(pos > this.size) {
return false;
}
const newNode = new Node(pos, value);
let current = this.head;
let previous;
if(pos === 0) {
newNode.next = current;
current.prev = newNode;
this.head = newNode;
} else {
for(let i = 0; i < pos; i++){
previous = current;
current = current.next;
}
newNode.next = current;
newNode.prev = previous;
current.prev = newNode;
previous.next = newNode;
}
}
Thanks.
With your structure ideally insertAt function would look like:
var insertAt = function(head, item, index) {
var curr = head;
var count = 0;
while (curr !== null) {
if (count === index) {
var newNode = { data: item, next: curr.next, prev: curr };
curr.next = newNode;
break;
}
count++;
curr = curr.next;
}
};
insertAt(head, 2, 3);
Let me know if this works.
Also look at this LinkedList class I have created, insertAt function is currently missing, but from this stack question, I am planning to add it in my class as well.
Class GitHub
NPM package - #dsinjs/linked-list
Complete documentation

LinkedList in JavaScript, how to append changes to list?

I'm running into a referential issue with variables in JavaScript, and I've been banging my head against the wall trying to figure this out.
I'm preparing to teach a class on data structures, and I'm reviewing the material after not looking at it for at least 10 years.
I know Linked Lists in theory, but for some reason, I'm struggling to come up with code that actually works in JavaScript (I chose JavaScript because that's what my class knows best)
This is my code:
let LinkedList = {
head: {},
tail: {}
};
let Node = {
data: {},
next: {}
}
function isObjectEmpty(obj1) {
return Object.keys(obj1).length === 0 && obj1.constructor === Object;
}
function count(node, counter) {
if (node.next) {
return 1 + count(node.next, counter);
}
return counter;
}
/**
* Adds data to LinkedList
* #param {LinkedList} list
* #param {Node} data
*/
function add_node(list, data) {
let temp = Object.assign({}, Node);
temp.data = data;
temp.next = {};
if (Object.keys(list.head).length === 0) {
list.head = temp;
list.tail = temp;
} else {
list.tail.next = temp;
list.tail = temp;
}
return list;
}
function insert(l, n, position) {
if (position <= 0) {
position = 0;
} else {
position = position - 1;
}
var list = Object.assign({}, l);
var node = Object.assign({}, Node);
node.data = n;
// this only counts elements on the list.
var elements = count(list.head, 0);
if (position > elements) {
return list;
}
var currentPosition = list.head;
var counter = 0;
while (!isObjectEmpty(currentPosition)) {
if (position === counter) {
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp.next;
return list;
}
currentPosition = currentPosition.next;
counter++;
}
return list;
}
// how to use the function
let songs = [
{id: '1', name: 'Kamikaze', artist: 'Eminem', releaseDate: '2018-08-31'},
{id: '2', name: 'despacito', artist: 'Luis Fonsi', releaseDate: '2018-08-31'},
{id: '3', name: 'La tortura', artist: 'Shakira', releaseDate: '2018-08-31'},
{id: '4', name: 'Roar', artist: 'Roar', releaseDate: '2018-08-31'},
];
let list = Object.assign({}, LinkedList);
songs.forEach((song) => {
add_node(list, song); // nothing special, just builds the linkedlist
});
list = insert(list, {id: '5', name: 'Havana', artist:'who knows', releaseDate:'2018-01-01'}, 3);
console.log(list); // new object isn't there.
This function is supposed to insert an element in an arbitrary position in the linked list. It kinda works. The problem is that the returned list keeps the reference to the old object before the re-association.
If you put a debugger in this block:
if (position === counter) {
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp.next;
return list;
}
You can see that I'm actually successfully inserting the new node where I want to.
But if you console.log the list structure, you'll see that the newly inserted Node is nowhere to be found.
I'm not sure where I'm failing or why the list keeps the old references and doesn't follow the new "path".
Any pointers in the right direction is greatly appreciated.
If you put a debugger in this block:
if (position === counter) {
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp.next;
return list;
}
You can see that I'm actually successfully inserting the new node
where I want to
No, you don't. If we drop the tmp and currentPosition assignment confusion, that bit of code is equivalent to
if (position === counter) {
node.next = currentPosition.next;
return list;
}
All that happens is that you are copying the tail of the list onto the new node, but you never really insert the node as the next of the one currently in the list. It's missing a
currentPosition.next = node;
A couple of other points:
Don't use isObjectEmpty and empty objects to represent "no node". Use null instead. If for some didactic reason you don't want to introduce null, use a boolean .isNode property on the objects to distinguish a node with data from an empty one.
Avoid Object.assign. Your usage is really unidiomatic. In
let temp = Object.assign({}, Node);
temp.data = data;
temp.next = {};
you are directly overwriting the values that you just copied from Node - better simplify to using an object literal:
let temp = {data, next: {}};
In var list = Object.assign({}, l); you don't really want to create a new object at all. You are going to mutate the passed in list, so you should just keep that. (If you wanted to make pure functions with immutable data structures, you would have to make all the nodes immutable as well, and for inserting clone the entire list until the desired position).
If your intention for Object.assign was to create new objects that later might involve other properties (or methods) that you weren't going to overwrite, use factory functions instead.
Don't count the list beforehand. Do the insertion in a single pass, and if you reach the end of the list before the position to inert then return.
function makeLinkedList() {
return { head: null, tail: null };
}
function makeNode(data, next = null) {
return { data, next };
}
function append(list, node) {
if (list.head) {
list.tail.next = node;
} else {
list.head = node;
}
list.tail = node;
}
function insert(list, data, position) {
if (position < 0) throw new Error("position must be nonnegative");
let newNode = makeNode(data);
let prev = null, cur = list.head;
while (cur != null && position > 0) {
prev = cur;
cur = cur.next;
position--;
}
if (cur == null && position > 0) throw new Error("position must be <= list length")
if (cur == null) {
list.tail = newNode;
} else {
newNode.next = cur;
}
if (prev == null) {
list.head = newNode;
} else {
prev.next = newNode;
}
}
You could use a version without Object.assign and work with objects as list and nodes who are created by same named function. This function could be replaced later with instanciable functions for more OOP styled approach.
This proposal takes a node and inset it, depending on the function, at the end of the list or at any place in the list. If no node exists in the list, it is inserted at the beginning of the linked list.
function linkedList() {
return { head: null, tail: null };
}
function node(data, next) {
return { data, next };
}
function insertTail(list, node) {
if (list.head) {
list.tail.next = node;
list.tail = list.tail.next;
} else {
list.head = node;
list.tail = node;
}
return list;
}
function insertPos(list, node, n) {
var temp = list.head,
previous;
if (!temp) {
return insertTail(list, node);
}
while (temp.next && n--) {
previous = temp;
temp = temp.next;
}
node.next = temp;
previous.next = node;
return list;
}
var songs = [{ id: '1', name: 'Kamikaze', artist: 'Eminem', releaseDate: '2018-08-31' }, { id: '2', name: 'despacito', artist: 'Luis Fonsi', releaseDate: '2018-08-31' }, { id: '3', name: 'La tortura', artist: 'Shakira', releaseDate: '2018-08-31' }, { id: '4', name: 'Roar', artist: 'Roar', releaseDate: '2018-08-31' }],
list = linkedList();
songs.forEach(song => insertTail(list, node(song)));
console.log(list);
insertPos(list, node({ id: '5', name: 'Havana', artist: 'who knows', releaseDate: '2018-01-01' }), 3);
console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Couple of problems. First, you're replacing a node, not strictly inserting. I think you want your .next to be the tmp node, not tmp.next.
currentPosition.next = tmp;
Second, you need to keep a reference to the node immediately before the insertion point, so that you can set its next value to the newly inserted node:
previousNode.next = node //<- node being the one you're inserting
that's why you're not seeing the difference in your linked list.
*Edit (putting it all together):
var prevNode = null;
while (!isObjectEmpty(currentPosition)) {
if (position === counter) {
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp;
if (prevNode === null) list.head = currentPosition;
else prevNode.next = currentPosition;
return list;
}
prevNode = currentPosition;
currentPosition = currentPosition.next;
counter++;
}
To make it safe for inserting into the initial position, we have to have the ability to set the list.head. Otherwise we set the previous node's next property.

Doubly Linked List in javascript

I was building linked list in javascript.
I dont understand one part.
function Node(element) {
this.element = element;
this.next = null;
this.previous = null;
}
function LList() {
this.head = new Node("head");
this.find = find;
this.findLast = findLast;
this.remove = remove;
this.insert = insert;
this.display = display;
this.dispReverse = dispReverse;
}
function find(item) {
var currNode = this.head;
while(currNode.element != item) {
currNode = currNode.next;
}
return currNode;
}
function display(list) {
var currNode = this.head.next;
while (currNode != null) {
console.log(currNode.element);
currNode = currNode.next;
}
}
function insert(newElement, item) {
var newNode = new Node(newElement);
var current = this.find(item);
newNode.next = current.next;
newNode.previous = current;
current.next = newNode;
// Why I dont need this part?
// Since new node got inserted, on my thoughts,
// the next node of the current node should point the new node as a previous one
// current.next.previous = newNode;
}
function remove(item) {
var currNode = this.find(item);
if (currNode.next != null) {
currNode.previous.next = currNode.next;
currNode.next.previous = currNode.previous;
currNode.next = null;
currNode.previous = null;
}
}
function findLast() {
var currNode = this.head;
while (currNode.next != null) {
currNode = currNode.next;
}
return currNode;
}
function dispReverse() {
var currNode = this.head;
currNode = this.findLast();
while(currNode.previous != null) {
console.log(currNode.element);
currNode = currNode.previous;
}
}
var cities = new LList();
cities.insert("Conway", "head");
cities.insert("Russellville", "Conway");
cities.insert("Carlisle", "Russellville");
cities.insert("Alma", "Carlisle");
cities.display();
cities.remove("Carlisle");
cities.display();
cities.dispReverse();
/*
Output should look like this:
Conway
Russellville
Carlisle
Alma
Conway
Russellville
Alma
Alma
Russellville
Conway
*/
Problem is the insert function!
Let's say if I have A B C node already.
And I want to insert K after B.
Currently, the next and previous of B are C and A for each.
The previous element of C is B.
Once I put K after B,
A B K C
(1) the next element of K will be C
(2) the previous element of K will be B.
(3) The next element of B is K
(4) previous element of C is K.
On the code I wrote in Insert function, each line of the codes below should process the upper statements.
(1) newNode.next = current.next;
(2) newNode.previous = current;
(3) current.next = newNode;
(4) current.next.previous = newNode;
But when I run this whole code including (4), error has occurred.
I don understand why...
Without the (4) line of codes, it works.
Is there anyone who can help me understand this?
You need to do step 4 before step 3:
current.next.previous = newNode
current.next = newNode
As it is, the reference of current.next (C) is being set to newNode (K) before you lookup the "old" current.next's previous property ( current.next.previous when points to B). Your reference to the current node is changed as soon as you assign it a new value. This is why current.next.previous is actually returning newNode.previous instead of the node reference you expect.
Your insert logic seems wrong in the last line :
current.next = newNode;
current.next.previous = newNode;
This actually means
newNode.previous=newNode;
Since you are setting the value of current.next to newNode in the 3rd statement
It should be :
newNode.next.previous = newNode.

variable always NaN

I'm learning my way into JS (but not new to programming). So, I'm trying to implement a LinkedList just to play around with JS.
It works okay except that count always returning NaN. I've googled, and thought that the reason was I wasn't initially setting the count to a number, but I did.
Below is my code:
function LinkedList() {
var head = null,
tail = null,
count = 0;
var insert = function add(data)
{
// Create the new node
var node = {
data: data,
next: null
};
// Check if list is empty
if(this.head == null)
{
this.head = node;
this.tail = node;
node.next = null;
}
// If node is not empty
else
{
var current = this.tail;
current.next = node;
this.tail = node;
node.next = null;
}
this.count++;
};
return {
Add: insert,
};
}
var list = new LinkedList();
list.Add("A");
list.Add("B");
The this in this.count refers to to the instance of the LinkedList object.
The part:
var head = null,
tail = null,
count = 0;
These are private variables and not considered a property of the LinkedList object.
What you want to do instead is:
this.head = null;
this.tail = null;
this.count = 0;
And that will make head, tail, count a property of the LinkedList object so that you can do this.count++.
Edit: To keep head, tail, count as private to the LinkedList object, your other code would be something like this:
// Check if list is empty
if(head == null)
{
head = node;
tail = node;
node.next = null;
}
// If node is not empty
else
{
var current = tail;
current.next = node;
tail = node;
node.next = null;
}
count++;
Also keep in mind that objects are pass-by-reference. So that applies to:
var current = tail;
current.next = node;
tail = node;
node.next = null;
More: If you want count to be a public property, then instead of returning:
return {
Add: insert,
};
You need to do this:
this.Add = insert;
return this;
So that the current object context gets returned upon object creation.

Categories

Resources