Remove elements from singly linked list (javascript) - javascript

I'm doing a CodeFights problem, trying to remove elements from a singly linked list that has the value k.
Below is what I have (l is the list and k is the value):
function removeKFromList(l, k) {
//figure out the head of the new list so we can reference it later
var head = l;
while (head.value === k){
head = head.next;
}
var node = head;
var temp = null;
while (node && node !== null) {
if (node.next.value === k){
temp = node.next.next;
node.next.next = null;
node.next = temp;
}
node = node.next;
console.log("+++", head)
}
console.log("---", head)
}
The CodeFight test cast is 3 -> 1 -> 2 -> 3 -> 4 -> 5. The final result would have been 1 -> 2 -> 4 -> 5. But my '---' console log keeps returning "Empty" (according to the CodeFights console).
My '+++' console log returns the correct head with element each loop.
I've been pulling my hair out over this, any idea what is missing here?

You need to return the list if you delete the first node.
Then you need a loop for taking the next not while the value is not found.
At last you need a check if the last node exist and if the value is found, then assign the next node to the last next property.
function removeNode(list, value) {
var node = list,
last;
if (node && node.value === value) {
return node.next;
}
while (node && node.value !== value) {
last = node,
node = node.next;
}
if (last && node.value === value) {
last.next = node.next;
}
return list;
}
var list = { value: 1, next: { value: 2, next: { value: 3, next: { value: 4, next: { value: 5, next: { value: 6, next: { value: 7, next: null } } } } } } };
list = removeNode(list, 5);
console.log(list)
list = removeNode(list, 1);
console.log(list)
list = removeNode(list, 7);
console.log(list)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Try this:
function myRemove(l, k){
if(l == null){
return l;
}
while(l.value == k){
l = l.next;
}
thisNode = l;
nextNode = thisNode.next;
while(nextNode != null){
if(nextNode.value == k){
thisNode.next = nextNode.next;
// No more nodes, ie last node was to be removed
if(thisNode.next == null)
break;
}
thisNode = thisNode.next;
nextNode = thisNode.next;
}
return l;
}

This could be also done by recursion:
function removeKFromList({value, next}, k) {
if(value === k){
return next ? removeKFromList(next, k) : null;
} else {
return {
next : removeKFromList(next, k),
value
};
}
}

Related

Check Palindrome Linked List

I intended to reverse the original LinkedList and compare it with reversed one. If it is identical return true. However, I keep getting the wrong results. Where I did wrong?
var isPalindrome = function(head) {
if(head == null || head.next == null) return true;
let reversedHead = reverse(head)
let count = 0
while (head != null && reversedHead != null) {
if(head.val != reversedHead.val) {
return false
}
head = head.next
reversedHead = reversedHead.next
}
if (head == null && reversedHead == null){
return true
} else {
return false
}
};
var reverse =(head)=> {
let prev = null
let next = new ListNode()
let curr = new ListNode()
curr = head
while(curr){
next = curr.next
curr.next = prev
prev = curr
curr = next
}
return prev
}
After you call:
let reversedHead = reverse(head)
You will have reversed the list, i.e. the head has become the tail, and a tail's next reference is null. Hence your loop will quickly end, as after:
head = head.next
... head will be null.
You need to rethink the algorithm. There are several ways to do it...
Instead of an inplace reversal, you could let reverse return a completely new linked list that will have the reversed order:
function reverse(head) { // Does not modify the given list. Returns a new one.
let node = head;
let reversedHead = null;
while (node) {
reversedHead = new ListNode(node.val, reversedHead);
node = node.next;
}
return reversedHead;
}
I assume here that the ListNode constructor accepts a second argument:
class ListNode {
constructor(val, next=null) {
this.val = val;
this.next = next;
}
}

Find an nth element from the end of an array

i looked at this challenge from codesignal (nthelementfromtheend) and put my code (below) in a test site
function nthElementFromTheEnd(l, n) {
if (n > l.length){
return -1;
}else{
// console.log();
let index = l.length - n;
// console.log(index);
// console.log(l[index]);
return l[index];
}
}
let l = [1, 2, 3, 4];
let n=7;
nthElementFromTheEnd(l, n);
results seem to pass the test site, but not codesignal.
open links below in new tab
challenge
tester
array length
You need to analyze the input that is coming into the function. l represents a singly-linked list. This doesn't exist natively in JavaScript, but it has been re-created using an object, as the comment describes:
// Singly-linked lists are already defined with this interface:
function ListNode(x) {
this.value = x;
this.next = null;
}
In the first test, the input that comes to the function looks like this:
ListNode {
value: 1,
next: ListNode {
value: 2,
next: ListNode {
value: 3,
next: null
}
}
}
So this is not as simple as returning a particular index from an array, because the function is not receiving an array but an object. You have to navigate the data structure continually checking for next values. There are probably more efficient ways to do this, but here's an example that at least passes the 8 sample tests:
function nthElementFromTheEnd(l, n) {
let values = [];
let node = l;
while (node) {
values.push(node.value);
node = node.next;
}
let len = values.length;
if (n > len) {
return -1;
} else {
return values[len-n];
}
}
The trick here is in the comment indicating the interface of a singly-linked lists.
// Singly-linked lists are already defined with this interface:
// function ListNode(x) {
// this.value = x;
// this.next = null;
// }
//
So you need to use l.next and l.value to navigate and get values from the linked list.
Here is a possible solution (not optimized):
function nthElementFromTheEnd(l, n) {
// find the length of the linked list
let len = 1;
let c = l;
while (c.next) {
len++;
c = c.next;
}
if (n > len) {
return -1
}
else {
// iterate linked list and get desired value (len-n)
let i = 0;
while (i < len-n){
l = l.next;
i++;
}
return l.value;
}
}
function nthElementFromTheEnd(l, n) {
var input = l;
var rev= input.reverse();
let value = rev[n-1];
if(value){
return value;
}
else
return -1;
}

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.

Leetcode Add Two Numbers Q: how to create a linkedlist from a number?

In this question: https://leetcode.com/problems/add-two-numbers/description/, I've figured out how to get sum of the digits (i.e. 807), but now I need to convert that number into a linked list (e.g. 7 -> 0 -> 8).
How do I create a linkedlist from a number?
Class function for creating a list node:
function ListNode(val) {
this.val = val;
this.next = null;
}
Rest of my code:
var addTwoNumbers = function(l1, l2) {
function findVal(linkedList) {
return linkedList.next == null ? linkedList.val.toString() : linkedList.val.toString() + findVal(linkedList.next);
}
var l1num = parseInt(findVal(l1).split('').reverse().join(''));
var l2num = parseInt(findVal(l2).split('').reverse().join(''));
var sum = l1num + l2num;
// Create linked list from sum
If you turn your number into an array, you can then use the array.prototype.reduce function.
let number = 807;
function ListNode(val) {
this.val = val;
this.next = null;
}
// Convert the number into a string and split that into an array of digits
let arr = Array.from(number.toString());
// Iterate through the elements, and create the nodes
let head = arr.reduce((nextNode, curr) => {
let node = new ListNode(curr);
node.next = nextNode;
return node;
}, null);
// Print out the values
let node = head;
while(node) {
console.log(node.val);
node = node.next
}
Easier with recursion:
f = n => n ? { val: n % 10, next: f(n / 10 | 0) } : null
console.log( f(807) )
f = (a, b, c = 0) => a && b && { val: (c += a.val + b.val) % 10,
next: f(a.next, b.next, c > 9) } || null
console.log( f( { val: 2, next: { val: 4, next: { val: 3 } } },
{ val: 5, next: { val: 6, next: { val: 4 } } } ) )

Traverse digits in strings

I'm trying to consider a function that takes in a flat array of string decimal integers that represent nodes in a tree, each period connotes hierarchy in that tree. I'm trying to create prevNode and nextNode functions. that take three parameters ids, id, planeLock. If a node has no prev or next id then false is returned. If planeLock is true then instead of going to the next node in the tree (e.g. from 1 to 0.1) it will go to the next node in that plane (eg. from 1 to 0) otherwise know as it's sibling, and not it's siblings deepest child.
var ids = [
'0',
'0.1',
'1',
'2',
'2.0',
'2.1',
]
prevNode(ids, '0') -> false // no prev node
prevNode(ids, '1', true) -> 0 // pass true stays on same plane
prevNode(ids, '1') -> 0.1 // prev node in tree
prevNode(ids, '2.0', true) -> false
prevNode(ids, '2.0') -> 2 // goes up one node
How can I parse these strings to get the desired results?
One possible approach:
function getLevel(id) {
return id.split('.').length;
}
function siblingNode(ids, id, planeLock, goesBack) {
var index = ids.indexOf(id);
var level = getLevel(id);
while (goesBack ? --index >= 0 : ++index < ids.length) {
var currEl = ids[index];
var currLevel = getLevel(currEl);
if (!planeLock || currLevel === level) {
return currEl;
}
if (currLevel < level) {
break;
}
}
return false;
}
function prevNode(ids, id, planeLock) {
return siblingNode(ids, id, planeLock, true);
}
function nextNode(ids, id, planeLock) {
return siblingNode(ids, id, planeLock, false);
}
Demo. Apparently, there's a tradeoff between memoizing all the levels (speedy but costs memory) and not (vice versa). If the source array is dynamic, and you'll have to look for a place when inserting new items, I'd strongly recommend memoizing approach (as you'll have to check level at each insertion).
Sorting the whole thing is a good approach. But if you want to extend this with additional functionality it may be better to convert your id list into a tree.
function createSortedTree(ids) {
var tree = {name: "", id: "root", children: {}};
function insert(tree, elem) {
if(!tree.children[elem[0]]) {
tree.children[elem[0]] = {
id: elem[0],
children: {},
parent: tree,
name: tree.id === "root" ? "" + elem[0] : tree.name + "." + elem[0]
};
}
if(elem.length > 1) insert(tree.children[elem[0]], elem.slice(1));
}
for(i in ids) insert(tree, ids[i].split("."));
function traverse(tree) {
if(current) {
current.next = tree;
tree.prev = current;
}
current = tree;
var children = Object.keys(tree.children)
.sort(function(a, b) {if(a < b) return -1; else if(a > b) return 1; else return 0;})
.map(function(key) {return tree.children[key]});
for(i in children) {
if(i > 0) children[i].prevPlane = children[i-1];
if(i < children.length - 1) children[i].nextPlane = children[i+1];
traverse(children[i]);
}
}
var current = null;
traverse(tree);
return tree;
}
function getNode(tree, id) {
if(typeof id === "string") id = id.split(".");
if(id.length === 0) return tree;
else return getNode(tree.children[id[0]], id.slice(1));
}
var tree = createSortedTree(["0", "0.1", "1", "2", "2.0", "2.1"])
var node = getNode(tree, "2.0");
console.log(node.prev.name);
console.log(node.next.name);
var node = getNode(tree, "1");
console.log(node.prev.name);
console.log(node.prevPlane.name);
http://jsfiddle.net/jxyqjq3c/
var _ = require('lodash')
function compare (n1, n2) {
var path1 = n1.split('.')
var path2 = n2.split('.')
var maxLen = Math.max(path1.length, path2.length)
var i = 0
while (i < maxLen) {
if (!path1[i] || +path1[i] < +path2[i]) {
return -1
}
if (!path2[i] || +path1[i] > +path2[i]) {
return 1
}
i++
}
return 0
}
function subset (ids, id) {
return _.filter(ids, function (_id) {
var _idArr = _id.split('.')
var idArr = id.split('.')
var _idChop = _.take(_idArr, _idArr.length - 1).join('.')
var idChop = _.take(idArr, idArr.length - 1).join('.')
if (_idChop === idChop) return true
return false
})
}
function metaInfo (ids, id) {
ids = ids.sort(compare)
var idIndex = ids.indexOf(id)
var meta = {}
meta.prev = (ids[idIndex - 1]) ? ids[idIndex - 1] : false
meta.next = (ids[idIndex + 1]) ? ids[idIndex + 1] : false
var idsSubset = subset(ids, id)
var idSubsetIndex = idsSubset.indexOf(id)
meta.prevSibling = (idsSubset[idSubsetIndex - 1]) ? idsSubset[idSubsetIndex - 1] : false
meta.nextSibling = (idsSubset[idSubsetIndex + 1]) ? idsSubset[idSubsetIndex + 1] : false
return meta
}
var ids = [ '0', '1', '2', '3', '0.0.0', '0.0.1', '0.0', '1.0' ]
var val = metaInfo(ids, '1')
console.log(val)
Here's a possibility. The implementation for nextNode would follow the same approach and re-use most of the function with the exception of changing the way the iterator is behaving.
function prevNode(collection, item, planeLock) {
var iterator = collection.indexOf(item) - 1
if (planeLock) {
while( ~iterator
&& !( item.split('.').length === 1 && collection[iterator].split('.').length === 1)
&& !( item.split('.').length === collection[iterator].split('.').length && item.split('.')[0] === collection[iterator].split('.')[0] ) )
iterator--
return ~iterator ? collection[iterator] : false
} else return collection[iterator] || false
}

Categories

Resources