Copy List with Random Pointer - Copy not point to - - javascript

I'm trying to solve the problem below:
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
The Linked List is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:
val: an integer representing Node.val
random_index: the index of the node (range from 0 to n-1) where random pointer points to, or null if it does not point to any node.
Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
My idea was loop the head and create a new node with the same value and set it to my ans Node. It will be a copy because I'm creating a new one. Then, I loop again for the random pointer which looks in my ans Node the node with the value that I should point to. It works perfectly for the input above. However, if I have an input with duplicate values, the findeNode method might return an invalid Node. For example it fails for the input below:
[[3,null],[5,17],[4,null],[-9,6],[-10,3],[5,15],[0,11],[6,null],[-6,16],[3,16],[-6,11],[9,12],[-2,1],[-3,11],[-1,10],[2,11],[-3,null],[-9,7],[-2,4],[-8,null],[5,null]]
My output:
[[3,null],[5,3],[4,null],[-9,null],[-10,3],[5,15],[0,11],[6,null],[-6,13],[3,13],[-6,11],[9,12],[-2,1],[-3,11],[-1,8],[2,11],[-3,null],[-9,7],[-2,4],[-8,null],[5,null]]
Correct output:
[[3,null],[5,17],[4,null],[-9,6],[-10,3],[5,15],[0,11],[6,null],[-6,16],[3,16],[-6,11],[9,12],[-2,1],[-3,11],[-1,10],[2,11],[-3,null],[-9,7],[-2,4],[-8,null],[5,null]]
Node definition:
function Node(val, next, random) {
this.val = val;
this.next = next;
this.random = random;
};
My code:
var copyRandomList = function(head) {
if(!head) return head;
let ans = new Node(0);
let temp = ans;
let cur = head;
while(cur) {
const node = new Node(cur.val);
temp.next = node;
cur = cur.next;
temp = temp.next;
}
cur = head;
temp = ans.next;
while(cur) {
const randomNode = cur.random;
if(!randomNode) {
temp.random = null;
} else {
const node = findNode(ans, randomNode.val);
temp.random = node;
}
cur = cur.next;
temp = temp.next;
}
return ans.next;
};
const findNode = (list, val) => {
if(!val)
return null;
while(list) {
if(list.val === val)
return list;
list = list.next;
}
return null;
}

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;
}
}

Javascript - Merge two sorted linked lists understanding

I'm trying to figure out why in the following function, at certein point they do mergedTail.next = temp; mergedTail = temp; Instead of to have only mergedTail = temp;:
let merge_sorted = function(head1, head2) {
// if both lists are empty then merged list is also empty
// if one of the lists is empty then other is the merged list
if (!head1) {
return head2;
} else if (!head2) {
return head1;
}
let mergedHead = null;
if (head1.data <= head2.data) {
mergedHead = head1;
head1 = head1.next;
} else {
mergedHead = head2;
head2 = head2.next;
}
let mergedTail = mergedHead;
while (head1 && head2) {
let temp = null;
if (head1.data <= head2.data) {
temp = head1;
head1 = head1.next;
} else {
temp = head2;
head2 = head2.next;
}
mergedTail.next = temp;
mergedTail = temp;
}
if (head1) {
mergedTail.next = head1;
} else if (head2) {
mergedTail.next = head2;
}
return mergedHead;
};
Many thanks.
mergedTail always refers to the last element of the merged list. temp is set to the element that should be appended to that list next.
By setting mergedTail.next = temp, the element temp is appended to the merged list. But now mergedTail no longer refers to the last element, but to the second-to-last element. So we assign mergedTail = temp to move it one step ahead, and maintain our invariant.
I would personally have preferred mergedTail = mergedTail.next, which is equivalent and maybe slightly less efficient, but makes the intent clearer.

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

Linked List remove method by value with Javascript

Trying to get this linked list remove method to work. should remove a node from the middle if that is where the value is found, and preserve the rest of the sequence. should not modify the list sequence if the last node is the node to be removed. it also should replace the start node with the second node when the first node value is x and the remove function is called with the value x.
remove(valueToRemove) {
let currentNode = this.firstNode;
let previousNode;
let foundValue = valueToRemove = currentNode.value;
while (!foundValue) {
previousNode = currentNode;
currentNode = currentNode.next;
if (!currentNode) {
return;
}
foundValue = valueToRemove === currentNode.value;
}
let nextNode = currentNode.next;
if (currentNode === this.firstNode) {
this.firstNode = nextNode;
} else {
previousNode.next = nextNode;
}
this.listSize--;
}
In the below code you are equating the values hence overriding the valueToRemove
let foundValue = valueToRemove = currentNode.value;
change it to
let foundValue = valueToRemove === currentNode.value;

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.

Categories

Resources