Hello I was doing an algorithm problem where you remove duplicates from a SORTED linked List
and this is what I have :
function removeDuplicatesFromLinkedList(linkedList) {
let currentNode = linkedList;
console.log('currentNode', currentNode)
while (currentNode) {
let nextDifferentNode = currentNode.next;
console.log('assignment', nextDifferentNode)
while (nextDifferentNode && nextDifferentNode.value === currentNode.value) {
nextDifferentNode = nextDifferentNode.next
console.log('nextDifferentNode', nextDifferentNode)
}
currentNode.next = nextDifferentNode
currentNode = nextDifferentNode
}
return linkedList
}
the argument parameter linkedList refers to the head node and its next value. I was wondering why we are returning linkedList at the end, does this mean we mutated the .next properties of the original linkedList nodes?
A LinkedList is like node1 -> node2 -> node3 -> ..... null. The input parameter, linkedList is the head of the linked list. The node1 in my example. While we mutate the list we want to return the head at the end, which unfortunately is named linkedList here.
To clear that we are going to change the name of the input parameter to the function. We name it head because its really just the head of the linked list while the linked list is the whole data structure. As we move forward we mutate the next pointers as needed to drop duplicate nodes.
function removeDuplicatesFromLinkedList(head) {
let currentNode = head;
console.log('currentNode', currentNode)
while (currentNode) {
let nextDifferentNode = currentNode.next;
console.log('assignment', nextDifferentNode)
while (nextDifferentNode && nextDifferentNode.value === currentNode.value) {
nextDifferentNode = nextDifferentNode.next
console.log('nextDifferentNode', nextDifferentNode)
}
currentNode.next = nextDifferentNode
currentNode = nextDifferentNode
}
return head
As you can see we receive the head, assign it to a variable that we are change on work on and keep head to return at the end.
Related
I am trying to solve the LeetCode problem 148. Sort List
Given the head of a linked list, return the list after sorting it in ascending order.
I am trying to do it in a recursive way before trying something smarter, as I am learning to handle data structures.
This is my code:
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* #param {ListNode} head
* #return {ListNode}
*/
var sortList = function(head) {
let previousNode = head
if(!head){
return head
}
let node = head.next
if(!node){
return head
}
let start = head
let previousNode1 = head
function sortList1(node, previousNode){
if(node.next == null){
return start
}
let temp = node.next;
if(traverseFromHead(node)){
start = node
}
previousNode1 = node
return sortList1(temp, node)
}
return sortList1(node, previousNode)
function traverseFromHead(node){
let myPosition = start
let inserted = false
if(start.val > node.val){
previousNode1.next = node.next
node.next = start
console.log("found in head excahange", node)
return node;
}
let myprevious2 = start
while(myPosition.next != null){
if(myPosition.val>=node.val){
console.log("before check start was", start, "with position at", myPosition.val, "for point", node.val, "my previous is", myprevious2.val)
let temp = node.next
myprevious2.next = node
node.next = myPosition
// previousNode1.next = temp
console.log("after update start is", start, "with position at", myPosition.val, "for point", node.val)
return null
}
myprevious2 = myPosition;
myPosition = myPosition.next
}
return false
}
};
I am not able to get it working correctly; it must be I am doing something wrong by logic or by concept
For instance for the linked list 4→2→3→0 the expected output would be 0→2→3→4, but my code produces 2→0.
Where is the problem in my code?
You have tried to implement insertion sort.
There are these issues that prevent it from working correctly:
The base case of the recursive function is not correct. With if(node.next == null) you are stopping too early. It could well be that this tail node should be moved elsewhere in the list, yet this node's value is not compared with anything. The stop condition really should be node == null.
previousNode1 = node is not always correctly identifying the previous node. If the call to traverseFromHead moved node to elsewhere in the list, then previousNode1 should not change, because what used to be the node before node, will now have become the node before the next node you want to process. For the same reason the second argument you pass in the recursive call is most often wrong: sortList1(temp, node).
It is a bit overwhelming to have that many variants of previousNodeXX variables. I would suggest to at least eliminate this previousNode1 and continue to work with previousNode, passing it also as argument to traverseFromHead. So call it as traverseFromHead(node, previousNode) and make sure you pass the correct second argument to sortList1. There are two cases to distinguish:
When node wasn't moved, then sortList1(temp, node) is correct, but when node was moved, it should be sortList1(temp, previousNode). You can make the distinction with a conditional operator:
sortList1(temp, previousNode.next != node ? previousNode : node)
traverseFromHead only removes the node from its current position in the if case, but forgets to do the same in the more general case. In the general case, the node is inserted, but previousNode.next is not adapted, meaning you now have two nodes whose next property point to node. There are several ways to do it right. I would suggest to perform the node-removal action in all cases before doing anyting else. You could place the code for node extraction before the if statement so that it always happens:
previousNode.next = node.next // <-- should always happen
if(start.val > node.val){
//...
I can understand why you put previousNode1.next = temp in comments inside the loop. Most often this needs to happen, but not when node didn't move! To solve this dilemma, perform a quick exit when node is already at its correct position (in comparison with previousNode). So at the top of the function do:
if (node.val >= previousNode.val) return null;
Now you can be sure that node will move.
traverseFromHead has a strange while condition. With the above corrections in place, this while condition can just be the opposite of the if condition, so that you can deal with the insertion after the loop:
while (myPosition.val < node.val)
myprevious2 = myPosition;
myPosition = myPosition.next
}
Here is your code with those corrections:
var sortList = function(head) {
let previousNode = head
if(!head){
return head
}
let node = head.next
if(!node){
return head
}
let start = head
function sortList1(node, previousNode){
if(node == null){ // Corrected base case
return start
}
let temp = node.next;
if(traverseFromHead(node, previousNode)){ // Pass the second argument
start = node
}
// Depending on whether node was moved, the node that precedes temp is different
return sortList1(temp, previousNode.next != node ? previousNode : node)
}
return sortList1(node, previousNode)
function traverseFromHead(node, previousNode){ // Second argument
if (node.val >= previousNode.val) return null; // Quick exit for trivial case
previousNode.next = node.next // Always first extract the node
if(start.val >= node.val){ // Equal is also good, so >=
node.next = start
return node;
}
let myPosition = start.next // Can start iteration at second node
let myprevious2 = start
while (myPosition.val < node.val) { // Look for the insertion spot
myprevious2 = myPosition;
myPosition = myPosition.next
}
// Now perform the re-insertion
myprevious2.next = node
node.next = myPosition
return null
}
};
Other remarks
Insertion sort is not the most efficient among sorting algorithms, and for linked lists it is quite easy to implement better performing sorting algorithms.
See for instance Merge sort on linked list
I have here adapted that solution for the LeetCode challenge (spoiler):
var sortList = function(head) {
if (!head || !head.next) return head; // Nothing to sort
// Find last node of first half
let tail = head;
for (let fast = tail.next; fast?.next; fast = fast.next.next) {
tail = tail.next;
}
// Split list into two halves
let head2 = tail.next;
tail.next = null;
// Recursively sort the two shorter lists
head = sortList(head);
head2 = sortList(head2);
// Merge the two sorted lists
if (head.val > head2.val) [head2, head] = [head, head2];
tail = head;
while (tail.next && head2) {
if (tail.next.val > head2.val) [head2, tail.next] = [tail.next, head2];
tail = tail.next;
}
tail.next ??= head2;
return head;
};
MergeSort naturally fits for linked lists.
In merge sort you consider that merging 2 size one lists is trivial (just put the higher head value after the lower one).
Extending that idea, if you have two already sorted lists, then it's easy to merge them as well. Just create a new list where you add the highest of both lists till both lists are empty.
So you can do a merge sort by creating first 2 lists of size 1. (ie. the first 2 elements of your list) Then merging them.
Then create a second list of size 2 (by merging 2 of size 1).
And continue until you have merged the entire original list into a sorted list.
Recursion
To implement this recursively first write a merge function that given two sorted lists merges them by preserving the sort order.
Then do the following to implement sort:
If your list is empty, then return the list as your result
Now merge the first element with sort(rest of the list)
I tried to create the insert method for a binary search tree, but 'this.root' remains null.
My logic is:
As long as current (which at the beginning is this.root) is not null, continue to update the current variable, by comparing it with the value we want to insert (if it's greater or less).
When current is null, we assign it the new Node:
class Node {
constructor(value){
this.value = value
this.left = null
this.right = null
}
}
class BST {
constructor(){
this.root = null
this.count = 0
}
insert(value){
this.count++
let current = this.root;
while(current){
if(value<current){
current=current.left
}
if(value>current){
current=current.right
}
}
current = new Node(value);
}
}
let Binst = new BST(10);
Binst.insert(22)
Binst.insert(12)
Binst.insert(4)
console.log(Binst)
There are these issues:
Comparing value with current is not right: that is comparing a number with an object. You need to compare with current.value
In the main program you call the BST constructor with an argument, but the constructor does not expect an argument. Although you could adapt the constructor to take that argument, it is better to not pass an argument to the constructor and have an extra call of insert in your main program.
current = new Node(value) will not change the tree. It only assigns a new node to a local variable. In order to extend the tree, you need to assign the new node to a left or right property of an existing node (or to the root property of the BST instance). Assigning to a variable never mutates your object structure.
this.root is never assigned anything else after its initialisation with null. Again, assigning to current, is never going to change this.root.
Because of the previous points, you need to stop your loop one iteration earlier -- when current points to the node that is about to get a new child. And you also need to deal separately with the case where the new node must become the root of the tree.
The following are just suggestions, not problems:
It is better practice to separate your statements with semicolons. There is the automatic semicolon insertion algorithm, but you wouldn't be the first to fall in one if its traps. Better take control over it yourself.
It is common practice to name variables with an initial capital when they are classes (constructors), but for instances a lower case initial letter is commonly used. So binst or bIntst instead of Binst. I would even suggest a more readable name, like tree.
Here is the corrected code:
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BST {
constructor() {
this.root = null;
this.count = 0;
}
insert(value) {
this.count++;
let current = this.root;
if (!current) {
this.root = new Node(value);
return;
}
while (true) {
if (value < current.value){
if (current.left) {
current = current.left;
} else {
current.left = new Node(value);
return;
}
}
if (value > current.value) {
if (current.right) {
current = current.right;
} else {
current.right = new Node(value);
return;
}
}
}
}
}
let tree = new BST();
tree.insert(10);
tree.insert(22);
tree.insert(12);
tree.insert(4);
console.log(tree);
I'm a newbie in JavaScript and trying to learn Data Structures and Algorithms.
I'm struggling at understanding how set works by depending on getIndex.
Here's the code:
class Node{
constructor(val){
this.val = val;
this.next = null
}
}
class SinglyLinkedList{
constructor(){
this.head = null;
this.tail = null;
this.length = 0;
}
push(val){
let newNode = new Node(val);
if(!this.head){
this.head = newNode
this.tail = this.head
}else{
this.tail.next = newNode;
this.tail = newNode
}
this.length++;
return this;
}
getIndex(index){
if(index > this.length || index < 0) return null;
let counter = 0, current = this.head;
while(counter !== index){
current = current.next;
counter++;
}
return current; // Here we return a value of the node we found
}
set(val, index){
let foundNode = this.getIndex(index);
if(foundNode){
foundNode.val = val;
// We can change the value of the node we founded in getIndex. Then the set works
// I don't understand why we can do this.
// Since what we do in getIndex is just returning a value of the node.
// How does changing that returned node can change the context of the list in term of the purpose of set
return true;
}
return false;
}
}
let list = new SinglyLinkedList();
list.push(88);
list.push(33);
list.push(11)
list.getIndex(1) // Output: Node: {val: 33, next: 11}. Why does changing this returned node can change the context of the whole list?
list.set(77,1) // Output: true. List (new) : 88 -> 77 -> 11
Basically, what I'm concerning is at the getIndex method, we return a current node. Then we change it in the set method. But does getIndex just return a value of that node ? So why can we change the whole list when changing that returned node from getIndex (in set) ?
Sorry for my silly question. Feel free to adjust my knowledge, expecially the class aspect. Please help! Thanks in advance
Because you are not returning the value, you are returning a reference to the value. The whole concept of a singly-linked-list is based on references.
As an experiment, try to return a new node instead.
return new Node(current.val)
It will not perform the same. This concept at a deeper level is called a pointer.
I'm new to javascript and thus the doubt. With primary data types like string this makes sense,
let a = "goat";
let b = a;
let a = "apple"
b; //"goat"
However with a custom object,
const item = this.head;
this.head = this.head.next;
return item.val;
Why does item still point to the same head, when head has moved and is pointing to something else?
No it doesn't do a deep copy.
You declared item as a const. It means it can't change the object it refers.
Also, you made const item = this.head;, now item points at the same object as this.head points. Then you did this.head = this.head.next;, it means this.head point on another object, while item still points at the first object.
Edit: It seems you have a linked-list and you want to shift the list to the left by one node. Your logic seems to be some sort of implementation of Array.prototype.shift.
You store a reference to the head (node A)
You set the head to point to the node after the current head (A.next = B)
If you inspect the current head, it will state that it is node B
You return the value for the original head (node A)
Garbage Collect (GC) removes node A because it becomes detached from the list
class Node {
constructor(val) {
this.val = val
this.next = null
}
}
class LinkedList {
constructor(head) {
this.head = head
}
add(node) {
let start = this.head
if (start == null) this.head = node
else {
while (start.next) start = start.next
start.next = node
}
return this
}
/** Remove node from front of list and return its value */
shift() {
const item = this.head // x = Z.head (A)
this.head = this.head.next // Z.head = A.head.next (B)
console.log('(1)', this.head.val) // DEBUG: B <-- NEW
return item.val // x.val (A -- Original head ref value)
}
}
let listZ = new LinkedList()
.add(new Node('A'))
.add(new Node('B'))
.add(new Node('C'))
console.log('(2)', listZ.shift()) // A -- No longer a node in the list
.as-console-wrapper { top: 0; max-height: 100% !important; }
I have the following code that implements a BST tree in JavaScript.
function Node(value) {
this.left = null;
this.right = null;
this.value = value;
}
function BinarySearchTree() {
this.root = null;
return;
}
BinarySearchTree.prototype.push = function(value) {
if (!this.root) {
this.root = new Node(value);
return;
}
var currentRoot = this.root;
var newNode = new Node(value);
while (currentRoot) {
if (value < currentRoot.value) {
if (!currentRoot.left) {
currentRoot.left = newNode;
break;
} else {
currentRoot = currentRoot.left;
}
} else {
if (!currentRoot.right) {
currentRoot.right = newNode;
break;
} else {
currentRoot = currentRoot.right;
}
}
}
}
var a = new BinarySearchTree();
a.push(27);
a.push(14);
a.push(35);
a.push(10);
a.push(19);
a.push(31);
a.push(42);
I am trying to implement a function which can do a breadth first traversal of the tree. This is what I have tried so far.
console.log(a.root.value);
traverse(a.root);
//function to traverse
function traverse(node) {
currentNode = node;
while (currentNode.left) {
displayNodes(currentNode);
parent = currentNode;
currentNode = currentNode.left;
displayNodes(currentNode);
if(parent.right!=null){
displayNodes(parent.right);
}
}
}
//function that displays the left and right node of a node
function displayNodes(node) {
if (node.left != null) {
console.log(node.left.value);
}
if (node.right != null) {
console.log(node.right.value);
}
}
I am unable to implement a function that could scale with a large number of data. I am not sure if a recursive method to traverse would be better or using a while loop. How can I implement the function? I know that the function gives unexpected behavior? What correction should I make?
You currently traverse the path from the root node to the left-most leaf.
A simple non-recursive breadth-first traversal function invoking a callback on each traversed node could look as follows:
// Breadth-first traversal:
function traverse(node, cb) {
var current = [node];
while (current.length > 0) {
var next = [];
for (var node of current) {
cb(node);
if (node.left) next.push(node.left);
if (node.right) next.push(node.right);
}
current = next;
}
}
// Example:
traverse(root, function(node) {
console.log(node.value);
});
It works by keeping an array of already discovered or traversed nodes current which initially contains just your root node. Now, you iteratively replace each node in that list with its children. In above function, the children are stored in a next array. At the end of each iteration, all nodes of the current level in current are replaced with all their children of the next deeper level in next. See also the first suggestion given by #DavidKnipe's answer.
A non-recursive approach has the advantage of not being subject to the call stack size limit. This theoretically allows you to handle larger data structures when the call stack size is limited.
If you're looking for a way to BFS using O(1) memory, I don't think there's a nice way to do it. (DFS is another matter though. Are you sure it has to be BFS?)
There are two ways I can see to do this. You could start with the array [this.root], and write a function that iterates over an array of nodes and then returns an array of children of those nodes. Then call that function on the array of children, and keep going down the tree until you get an empty array.
If memory is an issue, there's another way to do it. Instead of remembering the array of nodes at a given level, you could just remember the depth, then redo the iteration each time. So you'd have a function which takes a natural number n and iterates over the tree, but without going deeper than n, and does whatever it is you're trying to do at the nth level only; then call this function for all values of n until there are no more nodes left.
That last one might sound very wasteful, but it might not be too bad if the last few levels of the tree contain most of the nodes. It depends on your dataset and computational capabilities.