I have written the following function in JavaScript to check if a singly Linked List is a palindrome. However, I'm failing 2 out of 10 tests, and I can't figure out why.
Here are the tests I'm falling.
l: [0, 1, 0]
l: [1, 1000000000, -1000000000, -1000000000, 1000000000, 1]
Both should return true for the palindrome, but my function is returning false.
Here's my code:
function isListPalindrome(l) {
let curr = l;
let arr = [];
if (l == null)
return true;
// push all elements of l into the stack.
// a stack in JS is an array.
while(curr != null){
arr.push(l.value);
// move ahead:
curr = curr.next;
}
let curr2 = l;
// Traverse the list again & check by popping from the stack:
while(curr2 != null){
// get the top most element on the stack:
let num = arr.shift();
// check if the node data isn't the same as the element popped:
if (curr2.value != num){
return false;
}
// move ahead:
curr2 = curr2.next;
}
return true;
}
Thank you!
Inside the first while loop you're pushing l.value but l is not being incremented so it's pushing the same value to arr.
You now have arr which is suppose to be l in reverse. In the second while loop, instead of using arr.shift() use arr.pop(). This will take the first element off the arr stack. Remember that a stack is first in, last out.
Notice also that when you're comparing the list front to back you'll reach a point of irrelevancy, the half way point. Once you know that half the values in the forward list are the same as the values in the reverse list you know the rest will pass the test.
Here's what it's suppose to look like. You should try to figure out how to do odds yourself.
function isListPalindrome(l) {
let curr = l;
let arr = [];
if (l == null)
return true;
// push all elements of l into the stack.
// a stack in JS is an array.
while(curr != null){
arr.push(curr.value);
// move ahead:
curr = curr.next;
}
let curr2 = l;
let length = arr.length;
// Traverse the list again & check by popping from the stack:
while(curr2 != null){
// get the top most element on the stack:
let lastNum = arr.pop();
// check if the node data isn't the same as the element popped:
if (curr2.value != lastNum){
return false;
}
// Half way point for evens
if (length / 2 === arr.length) {
return true;
}
// move ahead:
curr2 = curr2.next;
}
return true;
}
solving with pushing values to an array and then check if the array is palindromic will have S:O(N). with reversing the second half and then traversing will have S:O(1). T:O(N) is same for both:
var isPalindrome = function (head) {
let fast_pointer = head;
let slow_pointer = head;
// when fast_ppointer reaches to the tail, slow_pointer will be in the middle
while (fast_pointer && fast_pointer.next) {
fast_pointer = fast_pointer.next.next;
slow_pointer = slow_pointer.next;
}
// now, slow_pointer is in the middle and we reverse from slow_pointer till the head
let prev = null;
while (slow_pointer) {
// slow_pointer=slow_pointer.next how we iterate in linked lists.
// so make sure we keep a reference to the next iteration
temp = slow_pointer.next;
slow_pointer.next = prev;
prev = slow_pointer;
slow_pointer = temp;
}
let left = head;
let right = prev;
while (right) {
if (left.val !== right.val) {
return false;
}
left = left.next;
right = right.next;
}
return true;
};
var isPalindrome = function (head) {
let values = [];
while (head) {
values.push(head.val);
head = head.next;
}
let rev = [];
head.map((e) => {
rev.unshift(e);
});
if (values.every((val, index) => val === rev[index])) {
return true;
} else {
return false;
}
};
class Node {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
const is_palindromic_linked_list = function (head) {
let front = head;
const traverse = (node) => {
if (!node) return true;
//reverse the LL
const reverse = traverse(node.next);
//check value if they are equal
const valChecker = front.value == node.value;
front = front.next;
return reverse && valChecker;
}
return traverse(head)
};
head = new Node(2)
head.next = new Node(4)
head.next.next = new Node(6)
head.next.next.next = new Node(4)
head.next.next.next.next = new Node(2)
console.log(`Is palindrome: ${is_palindromic_linked_list(head)}`)
head.next.next.next.next.next = new Node(2)
console.log(`Is palindrome: ${is_palindromic_linked_list(head)}`)
I push all the elements of the list in an array and then I convert the array with join function to a string so that i can compare if the string is the same as the inverse using reverse function if it is then it is a palindrome
function isListPalindrome(l) {
if(l === null) return true;
let array =[];
let current = l;
while (current != null){
array.push(current.value);
current = current.next
}
if(array.join('')=== array.reverse().join('')) return true;
return false
}
Related
I'm trying to make a binary search tree. If I start with an array my function makes a binary search tree out of that array (everything fine here). like for an array like let a = [2,4,5,3,9,7,3,8,5]; the tree looks like the picture. my problem is with the insert() function. If I start with an empty array and add a node to it, it works. However, when I add a second node, that second node won't be added to my tree, and my tree is shown as having only 1 node in it (the root node). Here the snippet:
const Node = (data, left = null, right = null) => {
return {data, left, right};
};
const Tree = array => {
const remDupsAndSort = array => {
const mergeSort = array => {
if(array.length <= 1) return array;
let leftArr = array.slice(0, array.length / 2);
let rightArr = array.slice(array.length / 2);
return merge(mergeSort(rightArr), mergeSort(leftArr))
};
const merge = (leftArr, rightArr) => {
let sorted = [];
while(leftArr.length && rightArr.length){
if(leftArr[0] < rightArr[0]){
sorted.push(leftArr.shift());
}else{
sorted.push(rightArr.shift());
}
};
return [...sorted, ...leftArr, ...rightArr]
};
return mergeSort([... new Set(array)])
};
array = remDupsAndSort(array);
const buildTree = (array, start, end) => {
if(start > end) return null;
let mid = Math.floor((start + end) / 2);
let node = Node(array[mid]);
node.left = buildTree(array, start, mid - 1);
node.right = buildTree(array, mid + 1, end);
return node;
};
const insert = value => {
if(!root) return root = Node(value);
current = root;
while(current){
if(value < current){
current = current.left
}else{
current = current.right
}
}
current = Node(value)
// if(!current){
// current = Node(value)
// // }else{
// if(value < current){
// current.left = insert(value, current.left)
// }else{
// current.right = insert(value, current.right)
// }
// }
return root
};
const prettyPrint = (node = root, prefix = '', isLeft = true) => {
if(node){
if (node.right !== null) {
prettyPrint(node.right, `${prefix}${isLeft ? '│ ' : ' '}`, false);
}
console.log(`${prefix}${isLeft ? '└── ' : '┌── '}${node.data}`);
if (node.left !== null) {
prettyPrint(node.left, `${prefix}${isLeft ? ' ' : '│ '}`, true);
}
}else{
console.log(node)
}
}
let root = buildTree(array, 0, array.length - 1);
return {root, prettyPrint, insert}
};
let a = [2,4,5,3,9,7,3,8,5];
let b = [];
let c = [1,2,4,5,6,7]
let f = Tree(a)
let d = Tree(b)
let e = Tree(c)
d.insert(4)
// d.insert(8) ---> doesn't work
// d.prettyPrint()
// f.insert(1) ---> doesn't work
f.prettyPrint()
// e.prettyPrint()
// console.log(d.root)
if I run d.prettyPrint() I'll get └── 4 just as expected. But if I run d.insert(8) after that 8 isn't added to the tree and the code returns └── 4 again. To make matters more confusing if I console.log(d.root) it returns null even though my prettyPrint function returns └── 4 as the root.
Clearly I expect the nodes be added to the tree. On one of my attempts I tried to write the code like this:
const insert = (value, current = root) => {
if(!current){
current = Node(value)
}else{
if(value < current){
current.left = insert(value, current.left)
}else{
current.right = insert(value, current.right)
}
}
return current
};
even though I assigned current = root the code returned null for d.insert(4)
There are these issues in your insert function:
current is implicitly defined as a global variable -- this wouldn't parse in strict mode. It should be declared as a local variable, using let
The value is compared with a node object instead of with the data of that node. So value < current should be changed to value < current.data
The assignment of the new node object to current -- after the loop -- will not mutate the tree. It merely assigns that object to that variable. Such assignment can never change the tree, just like current = current.right does not mutate the tree either. You need instead to assign the new node to either the left or right property of the parent of current. So this assignment should happen one step earlier.
Correction:
const insert = value => {
if(!root) return root = Node(value);
let current = root; // Define with `let`
while(current){
if(value < current.data){ // Compare with the data, not the node
// Mutate the parent node when inserting
if (!current.left) return current.left = Node(value);
current = current.left
}else{
if (!current.right) return current.right = Node(value);
current = current.right
}
}
};
I am having problems with my quick-sort algorithm implementation that uses recursion. When I use the function on an array with duplicate numbers, it ignores those numbers in the final result and I don't know why. Anyone know what I did wrong?
(JavaScript)
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
let quickSort = (arr) => {
if (arr.length <= 1) return arr;
const pivot = arr[getRandomInt(arr.length)]
let smallerArr = [];
let greaterArr = [];
for (let item of arr) {
if (item !== pivot) {
if (item > pivot) {
greaterArr.push(item);
continue
}
smallerArr.push(item);
}
}
let sorted = quickSort(smallerArr);
sorted = sorted.concat([pivot],quickSort(greaterArr));
console.log(sorted);
return sorted;
}
quickSort([5,7,6,23,6]);
You only want to skip the element whose pivotIndex matches with the current index. I switched back to the for(let i = 0,index......) approach to keep track of the same. What you're currently doing is skipping all the items whose value matches with the pivot which means duplicates are also removed from being considered if the pivot element has duplicates in the array.
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
let quickSort = (arr) => {
if (arr.length <= 1) return arr;
const pivotIndex = getRandomInt(arr.length);
const pivot = arr[pivotIndex];
let smallerArr = [];
let greaterArr = [];
for(let index = 0;index<arr.length;index++)
{
if(index===pivotIndex)
continue;
const item = arr[index];
if (item > pivot)
greaterArr.push(item);
else
smallerArr.push(item);
}
let sorted = quickSort(smallerArr);
sorted = sorted.concat(pivot,quickSort(greaterArr));
console.log(sorted);
return sorted;
}
quickSort([5,7,6,23,6,6,6,6]);
What happens when item !== pivot is false? You seem to skip that item.
You need to make clear what your code should do when item === pivot.
Right now your code is only ignoring those values because it is not inserting on either greaterArr and smallerArr.
Add an else statement to fix this.
I'm doing some exercises. the question is to find the first none-repeated character in a string.
My idea is: turn string to array. assign array[0] to a new variable, and remove this array[0] from array. Check if this new array contain this variable, if not, return this variable. Else, use filter to remove same value elements and get a new array. repeat the process. the code as following.
const NoneReChar = (str) => {
let tempArr = str.split('');
let start = tempArr[0];
while (true) {
tempArr.shift();
if (!tempArr.includes(start)) {
return start;
} else {
tempArr.filter(char => char !== start);
start = tempArr[0];
}
}
}
console.log(NoneReChar("aaaabbbeccc"))
I was expect output 'e', but I keep getting 'a'...where are the mistakes I made here?
The Array.filter() method doesn't mutate the original array. You need to assign the result of filter to tempArr:
tempArr = tempArr.filter(char => char !== start);
Example:
const NoneReChar = (str) => {
let tempArr = str.split('');
let start = tempArr[0];
while (true) {
tempArr.shift();
if (!tempArr.includes(start)) {
return start;
} else {
tempArr = tempArr.filter(char => char !== start);
start = tempArr[0];
}
}
}
console.log(NoneReChar("aaaabbbeccc"))
However, you don't handle the not found case. To handle it instead of true, the while clause should stop when the array is empty:
const NoneReChar = (str) => {
let tempArr = str.split('');
let start = tempArr[0];
while (tempArr.length) {
tempArr.shift();
if (!tempArr.includes(start)) {
return start;
} else {
tempArr = tempArr.filter(char => char !== start);
start = tempArr[0];
}
}
return null;
}
console.log(NoneReChar("aabbcc"))
Another option is comparing the length of the array before and after filtering. If the length is the same, the item was not repeated:
const NoneReChar = (str) => {
let tempArr = str.split('');
while (tempArr.length) {
const [start, ...rest] = tempArr; // take the 1st item and the rest
tempArr = rest.filter(char => char !== start); // filter out start
if(tempArr.length === rest.length) { // check current and previous arrays, and if the length still matches, start didn't appear again
return start;
}
}
return null;
}
console.log(NoneReChar("aabzbcc"))
const NoneReChar = (str) => {
const tempArr = str.split('');
let result = 'Not Found';
for (let index = 0; index < tempArr.length; index++) {
const firstIndex = tempArr.indexOf(tempArr[index]);
const lastIndex = tempArr.lastIndexOf(tempArr[index]);
if (firstIndex === lastIndex) {
result = tempArr[index];
break;
}
}
return result;
}
console.log(NoneReChar("aaaabbbeccc"));
var permuteUnique = function(nums) {
var res = [];
if (nums.length == 0 || nums == null) return res;
var used = [];
var list = [];
nums.sort();
dfs(nums, used, list, res);
return res;
};
function dfs(nums, used, list, res) {
if (list.length == nums.length) {
res.push(list.slice());
return;
}
console.log(res, list);
for (var i = 0; i < nums.length; i++) {
if (used[i]) continue;
if (i > 0 && nums[i] === nums[i - 1] && !used[i]) continue;
used[i] = true;
list.push(nums[i]);
dfs(nums, used, list.slice(), res);
used[i] = false;
list.pop();
}
}
console.log(permuteUnique([1,1,2]));
after running,
when input is [1,1,2]. the result always returns []. The correct answer should be [[1,1,2],[1,2,1],[2,1,1]].
Can someone help have a look at? Thank you in advance.
To begin with: you are not reassigning the variable res in the function permuteUnique.
var permuteUnique = function(nums) {
var res = []; // var res is assigned
if (nums.length == 0 || nums == null) return res;
var used = [];
var list = [];
nums.sort();
dfs(nums, used, list, res);
return res; // you never reassigned res, so it remains []
};
The result of permuteUnique(whateverArgumentYouPass) will, regardless of the passed argument, always be []. You might want to read more about the basics of functions and return values here.
Besides, inside permuteUnique you call dfs(nums, used, list, res); but you never capture the return value. If you do the computations for finding the permutations there, you should return the computed values and assign it to a variable (in this case res). This would also not work in your code, because your function dfs will always return undefined.
There are also a couple of other things not working, but I'd first focus on above-mentioned points.
I just struggled through a simple interview question: Please reverse a singly linked list.
While I failed to provide a working answer in time to save the interview, I was able to come up with a solution afterwards.
Is my solution correct? How would you analyze this with Big-Oh? Are there more efficient ways to reverse a singly linked list?
// reverse a linked list
var reverseLinkedList = function(linkedlist) {
var node = linkedlist;
var previous = null;
while(node) {
// reverse pointer
node.next = previous;
// increment previous to current node
previous = node;
// increment node to next node
if (node.next){
node = node.next
} else {
node = null;
}
}
}
Note: In my search for similar posts, I did find one example in JavaScript. I was wondering if my code is possible (without a temp variable). Thank you.
There are a couple of problems with your code. This should make it clear.
// reverse a linked list
var reverseLinkedList = function(linkedlist) {
var node = linkedlist;
var previous = null;
while(node) {
// save next or you lose it!!!
var save = node.next;
// reverse pointer
node.next = previous;
// increment previous to current node
previous = node;
// increment node to next node or null at end of list
node = save;
}
return previous; // Change the list head !!!
}
linkedlist = reverseLinkedList(linkedlist);
You could solve this problem recursively in O(n) time as ckersch mentions. The thing is, that you need to know that recursion is memory intensive since functions accumulate in the calls stack until they hit the stop condition and start returning actual things.
The way I'd solve this problem is:
const reverse = (head) => {
if (!head || !head.next) {
return head;
}
let temp = reverse(head.next);
head.next.next = head;
head.next = undefined;
return temp;
}
When reverse() reaches the end of the list, it will grab the last node as the new head and reference each node backwards.
This would be O(n) in time, since you do a constant number of operations on each node. Conceptually, there isn't a more efficient way of doing things (in terms of big-O notation, there's some code optimization that could be done.)
The reason why you can't exceed O(n) is because, in order to do so, you would need to skip some nodes. Since you need to modify each node, this wouldn't be possible.
Efficiency then comes down to a constant factor. The fewer operations you can do per item in the list, the faster your code will execute.
I'd implement like this:
function reverseLinkedList(list, previous){
//We need to use the the current setting of
//list.next before we change it. We could save it in a temp variable,
//or, we could call reverseLinkedList recursively
if(list.next !== null){
reverseLinkedList(list.next, list);
}
//Everything after 'list' is now reversed, so we don't need list.next anymore.
//We passed previous in as an argument, so we can go ahead and set next to that.
list.next = previous;
}
reverseLinkedList(list, null);
Of course, this is recursive, so it would be inefficient in terms of space, but I like recursive code :)
This also doesn't return the reversed linked list, but we could fairly easily modify things to do so if that were important.
ES6 solution:
Just keep a track of the reversed list and keep adding that to tmp.
const reverseLinkedList = (head) => {
let reversed = null;
while(head) {
const tmp = head;
head = head.next;
tmp.next = reversed;
reversed = tmp;
}
return reversed;
};
console.log(JSON.stringify(reverseLinkedList({
data: 1,
next: {
data: 2,
next: {
data: 3,
next: {
data: 4,
next: {
data: 5,
next: {
data: 5,
next: {
data: 6
}
}
}
}
}
}
})));
Reversing the SinglyLinkedList:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
To understand The Solution we have to keep track of previous head and next variables
for example in above input Head = 1 ; next = 2 we don't have previous so assume previous = null
loop the list till head is not null. reverse the connections(previous and next) of head.
Below is the code
var reverseList = function(head) {
let previous = null;
while(head !== null){
let next = head.next;
head.next = previous;
previous= head
head = next;
}
return previous;
};
//O(n) | O(1) wherre n is the number of nodes in the linked list
class Node{
constructor(val){
this.val = val;
this.next = null;
}
}
function reverseLinkedList(head) {
if(!head) return null;
let p1 = head;
let p2 = null;
while(p1){
let temp = p1.next;
p1.next = p2;
p2 = p1;
p1 = temp;
}
return p2;
}
const a = new Node(1);
a.next = new Node(2);
a.next.next = new Node(3)
console.log("Current Node",a);
console.log("Reversed List",reverseLinkedList(a))
class LinkedList {
constructor () {
this.head = this.tail = null
}
// add to the end of the list
append (value) {
if (!this.tail) {
this.head = this.tail = new Node(value)
} else {
let oldTail = this.head
this.head = new Node(value)
this.head.next = oldhead
}
}
reverseList() {
//your code here
let currentNode = this.head
this.head = null
while(currentNode) {
if (!this.head) {
this.head = new Node(currenthead.data)
} else {
let oldhead = this.head
this.head = new Node(currentNode.data)
this.head.next = oldhead
}
currentNode = currentNode.next
}
}
}
class Node {
constructor (value, next) {
this.data = value
this.next = next || null
}
}
const list = new LinkedList()
list.append(1)
list.append(2)
list.reverseList()
Since inserting data at the beginning of the linked list pushes other first nodes till the end, and since it's a O(1) process.
Then I created the following function reverse()
where it basically insert node elements in the beginning which basically will get a reversed list at the end.
Here's a demo down below:
class Node {
constructor(data, next = null) {
this.data = data;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
insertFirst(data = null) {
// make new head point to the previous head
this.head = new Node(data, this.head);
this.size ++;
}
insertLast(data = null) { // insert last in the beginning will be the first in the linked list
const node = new Node(data);
// If empty, insert first
if (!this.head) this.insertFirst(data);
else {
let current = this.head;
// while next is not null, continue
while (current.next)
current = current.next;
// eventually next is null, we want to set next here to the node we want to add
current.next = node;
}
this.size ++;
}
// print linked list
print() {
let current = this.head;
let output = "";
while (current) { // while current is not null, eventually it will be null
output += current.data + " => ";
current = current.next; // current jumping to the next node
}
output += "| NULL"; // ending
console.log(output);
return output;
}
reverse() {
if (!this.head) return; // if no head, do nothing
let current = this.head;
const linkedList = new LinkedList(); // create a new linked list
// don't worry, it will be garbage collected once this function ends since it's not a global variable
while (current) {
linkedList.insertFirst(current.data); // insert first at the beginning will be the end of the linked list at the end
current = current.next;
}
// assign current head to the reversed linked list head
this.head = linkedList.head;
}
}
const linkedList = new LinkedList();
// fill data as 100 -> 200 -> 300 -> 400
linkedList.insertLast(100);
linkedList.insertLast(200);
linkedList.insertLast(300);
linkedList.insertLast(400);
// To view results
const bodyElement = document.getElementsByTagName("body")[0];
bodyElement.innerHTML = `<p>Original Linked List: <b>${linkedList.print()}</b></p>`; // 100 200 300 400
linkedList.reverse();
bodyElement.innerHTML += `<p>Reversed Linked List: <b>${linkedList.print()}</b></p>`; // 400 300 200 100
b {
color: green;
}
<body></body>
Overall, the whole process of this reverse() function is O(n).
Hopefully this sounds clear to you, and correct me if I'm wrong.
This is my recursive solution:
https://codesandbox.io/s/reverse-linked-list-tqh2tq?file=/src/index.js
let d = { me: "d" };
let c = { me: "c", next: d };
let b = { me: "b", next: c };
let a = { me: "a", next: b };
const reverseMe = (o) => {
let lastDude;
if (o.next.next) lastDude = reverseMe(o.next);
else lastDude = o.next;
o.next.next = o;
o.next = null;
return lastDude;
};
console.log("result", reverseMe(a));