220284
220284

Reputation: 185

Cracking the coding interview 2.1 remove duplicate values from singly linked list javascript

class LinkedListNode {
    constructor(value) {
        this.value = value;
        this.next = null;
    }
}

let head = new LinkedListNode("head");

let x = [1, 1, 1, 1, 4, 10, 10, 3, 10, 9, 5, 5, 5, 8];

for (let ele of x) {
    let y = new LinkedListNode(ele);
    let pointer = head;
    while (pointer.next != null) {
        pointer = pointer.next;
    }
    pointer.next = y;
}

Can someone explain why the following 'solution' leads to an infinite loop?

let removeDup = function(sll) {
    let array = []
    let pointer = sll;
    while (pointer) {
        if (array.includes(pointer.value)){
        } else {
            array.push(pointer.value);
            sll.next = pointer;
            sll = sll.next;
        }
        pointer = pointer.next;
    }
}

It appears that if I

let pointer = sll.next;

or

let array = [sll.value]

then it works fine but if I run it as is then it leads to an infinite loop. I can see why it may make a linked list with 2 duplicates of the first value but I can't understand why it makes an infinite loop. Alternatively, if anyone can point me in the right direction for debugging this then that would be appreciated also!

Upvotes: 0

Views: 392

Answers (3)

Redu
Redu

Reputation: 26191

You may write a micro Linked List library perhaps. Here is one with function descriptions;

  • toList : takes an array converts it into a list
  • foldList : Something like reduce. Takes a list, a function and an accumulator.
  • sum : takes a list and gives it's sum :)
  • prt : pretty print a list
  • nub : remove dupes from a list.

function List(e){
  this.data = e;
  this.next = null;
}

List.fromArray = function([a,...as]){
                   var h = new List(a),
                       t = as.reduce((l,e) => [l[0],l[1].next = new List(e)], [h,h]);
                   return t[0];
                 };
List.prototype.fold = function (f,a){
                        var newa = f(a, this.data);
                        return this.next ? this.next.fold(f, newa)
                                         : newa
                      };
List.prototype.log = function(){
                       return this.fold((a,e) => a + e + " -> ", "") + "null";
                     };
List.prototype.nub = function(){
                       var o = this.fold((a,e) => (a[e] = e, a), {}),
                           a = Object.values(o);
                       return List.fromArray(a);
                     }

var arr = [1, 1, 1, 1, 4, 10, 10, 3, 10, 9, 5, 5, 5, 8],
    lst = List.fromArray(arr),
    sum = l => l.fold((a,e) => a + e, 0), // just for fun
    res = lst.nub().log();

console.log(res);

nub is O(n).

Upvotes: 0

grodzi
grodzi

Reputation: 5703

  1. How to investigate your bug

You can use a debugger, but for primitive people like me, you can also use console.log

  • In case of infinite loop, what you want is to break free
let count = 0
while (pointer && count++<5) {
  //whatever
}

Even if you fail in your algorithm you will still exit

  • Output your variable

Be creative and spam console.log wherever you see fit. put some string to remind you what junk you outputed

while (pointer) {
  if (array.includes(pointer.value)){
    console.log('cached skip')
  } else {
    console.log('pointervalue', pointer.value)
    array.push(pointer.value);
    sll.next = pointer;
    sll = sll.next;
    console.log('sllendloop', sll)
  }
  pointer = pointer.next;
}
  1. Fixing your code

Note: do not use array for cache because it is O(n) look

You may use a Set (O(1)) instead

const removeDup = function(sll) {
  const dupes = new Set()
  let cur = { next: null }
  // you can also as you suggested initialize cur as sll
  // in which case you must mark it as "consumed" idem add the value to dupes
  let sllIter = sll
  while (sllIter) {
    if (dupes.has(sllIter.value)) {
      // early continue to avoid nesting else
      // subjective preference
      sllIter = sllIter.next
      continue
    }
    dupes.add(sllIter.value)
    // link our node to the currently being iterated node
    cur.next = sllIter;

    // advance our node
    cur = sllIter

    // advance iter
    sllIter = sllIter.next
  }
  return sll
}
const l = (value, next) => ({ value, next })
const sllStr = ({ value: a, next: b }) => b ? `${a} -> ${sllStr(b)}`: a
const sll = l(1, l(1, l(2, l(1, l(2, l(3))))))
console.log(sllStr(removeDup(sll))) // 1 -> 2 -> 3

Upvotes: 0

Chase
Chase

Reputation: 3126

Looks like you end up defining a node that references itself within your else condition.

You may be looking for something like this:

class LinkedListNode {
    constructor(value) {
        this.value = value;
        this.next = null;
    }
}

let head = new LinkedListNode("head");

let x = [1, 1, 1, 1, 4, 10, 10, 3, 10, 9, 5, 5, 5, 8];

for (let ele of x) {
    let y = new LinkedListNode(ele);
    let pointer = head;
    while (pointer.next != null) {
        pointer = pointer.next;
    }
    pointer.next = y;
}

function removeDup(currentNode = sll) {
	const seen = {};
	let lastUnique;
	while (currentNode) {
		if (seen[currentNode.value]) {
			lastUnique.next = currentNode.next;
		} else {
			seen[currentNode.value] = true;
			lastUnique = currentNode;
		}
		currentNode = currentNode.next;
	}
}

removeDup(head);

let outputNode = head;
while (outputNode) {
	outputNode = outputNode.next;
	if (outputNode) {
		console.log(outputNode.value);
	}
}

Upvotes: 1

Related Questions