-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
avc278 - 3.01 - JavaScript #76
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Three in One: Describe how you could use a single array to implement three stacks. | ||
const assert = require("assert"); | ||
const { Stack, arrayToStack } = require("../../lib/avc278/stacksAndQueues"); | ||
|
||
/** | ||
* We start off by assuming we cannot use the benefits that dynamic languages like JavaScript bring to the table, so as | ||
* to provide a language-agnostic solution. Hence, upon creation of an array, we must declare the size, and performing a | ||
* `push()` on the array does not allocate additional memory. | ||
* | ||
* Say we have three stacks of different lengths; we should first start off by storing the length of the longest stack, | ||
* and creating a pseudo array whose length is 3 times that length, which we'll refer to as N. Then, at indices 0, N/3, | ||
* and 2N/3, we declare the starting positions of the stacks. At this point, we need to be careful when defining peek, | ||
* pop, and push stack-like behavior for this pseudo array class we created. We need to make sure that should we ever | ||
* pop() or push() outside the bounds of the allotted space for the subject stack in the array, we do nothing (ideally, | ||
* we should raise a verbose exception explaining why the expected behavior never happened). | ||
* | ||
* The following is an implementation of the above description: | ||
*/ | ||
|
||
class _Array { | ||
constructor(arrLength) { | ||
this.arr = new Array(arrLength).fill(null); | ||
this.stackOneStartIdx = 0; | ||
this.stackOneCurrIdx = 0; | ||
this.stackTwoStartIdx = arrLength / 3; | ||
this.stackTwoCurrIdx = arrLength / 3; | ||
this.stackThreeStartIdx = 2 * arrLength / 3; | ||
this.stackThreeCurrIdx = 2 * arrLength / 3; | ||
Comment on lines
+23
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if instead of implementing three stacks you had to implement four, what would need to be changed? (spoiler alert: next question is going to be "how about five?") There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. D: oh boy. Alright, so before I go into coding, I'm going to write down a few notes just to get my brain going:
|
||
}; | ||
|
||
pop(stackNum) { | ||
let poppedVal; | ||
switch (stackNum) { | ||
case 1: | ||
if (this.arr[this.stackOneCurrIdx] === null) return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if i really wanted to store a |
||
poppedVal = this.arr[this.stackOneCurrIdx]; | ||
this.arr[this.stackOneCurrIdx] = null; | ||
this.stackOneCurrIdx = Math.max(this.stackOneCurrIdx - 1, this.stackOneStartIdx); | ||
return poppedVal; | ||
case 2: | ||
if (this.arr[this.stackTwoCurrIdx] === null) return; | ||
poppedVal = this.arr[this.stackTwoCurrIdx]; | ||
this.arr[this.stackTwoCurrIdx] = null; | ||
this.stackTwoCurrIdx = Math.max(this.stackTwoCurrIdx - 1, this.stackTwoStartIdx); | ||
return poppedVal; | ||
case 3: | ||
if (this.arr[this.stackThreeCurrIdx] === null) return; | ||
poppedVal = this.arr[this.stackThreeCurrIdx]; | ||
this.arr[this.stackThreeCurrIdx] = null; | ||
this.stackThreeCurrIdx = Math.max(this.stackThreeCurrIdx - 1, this.stackThreeStartIdx); | ||
return poppedVal; | ||
default: | ||
return; | ||
} | ||
}; | ||
peek(stackNum) { | ||
switch (stackNum) { | ||
case 1: | ||
return this.arr[this.stackOneCurrIdx]; | ||
case 2: | ||
return this.arr[this.stackTwoCurrIdx]; | ||
case 3: | ||
return this.arr[this.stackThreeCurrIdx]; | ||
default: | ||
return; | ||
} | ||
}; | ||
push(stackNum, val) { | ||
switch (stackNum) { | ||
case 1: | ||
if (this.stackOneCurrIdx === this.stackTwoStartIdx - 1) return; | ||
avc278 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (this.arr[this.stackOneCurrIdx] !== null) this.stackOneCurrIdx += 1; | ||
this.arr[this.stackOneCurrIdx] = val; | ||
break; | ||
case 2: | ||
if (this.stackTwoCurrIdx === this.stackThreeStartIdx) return; | ||
if (this.arr[this.stackTwoCurrIdx] !== null) this.stackTwoCurrIdx += 1; | ||
this.arr[this.stackTwoCurrIdx] = val; | ||
break; | ||
case 3: | ||
if (this.stackThreeCurrIdx === this.arr.length) return; | ||
if (this.arr[this.stackThreeCurrIdx] !== null) this.stackThreeCurrIdx += 1; | ||
this.arr[this.stackThreeCurrIdx] = val; | ||
break; | ||
default: | ||
return; | ||
} | ||
}; | ||
}; | ||
|
||
// Helper function to convert a stack from A -> B -> C to C -> B -> A, and return its length | ||
const invertStackAndGetLength = (stack) => { | ||
const res = new Stack(); | ||
let length = 0; | ||
while (!stack.isEmpty()) { | ||
length += 1; | ||
res.push(stack.pop()) | ||
} | ||
return [ res, length ]; | ||
} | ||
|
||
/** | ||
* @param {Stack} A input stack | ||
* @param {Stack} B input stack | ||
* @param {Stack} C input stack | ||
* @return {_Array} the array containing three stacks, with stack methods | ||
* | ||
* In implementing threeInOne, we need to know the longest stack length, which we'll call N, and the others M and P. | ||
* As we traverse through these stacks, we do so one after the other, which results in O(N + M + P), which is really | ||
* just O(N) as we can remove smaller terms. Creating the inverse stacks requires a maximum of O(N) space for the same | ||
* reason. | ||
* Now, when creating the output array (of type _Array), we require 3 * N additional time and space for traversal. | ||
* Again, however, this results in O(N) time and O(N) space as we can ignore the leading term. | ||
* | ||
* Runtime: O(N) | ||
* Space: O(N) | ||
* | ||
*/ | ||
const threeInOne = (A, B, C) => { | ||
const [ invA, lenA ] = invertStackAndGetLength(A); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. normally, queue and stack implementations have an O(1) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh interesting! |
||
const [ invB, lenB ] = invertStackAndGetLength(B); | ||
const [ invC, lenC ] = invertStackAndGetLength(C); | ||
|
||
const maxSize = Math.max(lenA, lenB, lenC); | ||
const arr = new _Array(maxSize * 3); | ||
while (!invA.isEmpty()) { | ||
arr.push(1, invA.pop()); | ||
} | ||
while (!invB.isEmpty()) { | ||
arr.push(2, invB.pop()); | ||
} | ||
while (!invC.isEmpty()) { | ||
arr.push(3, invC.pop()); | ||
} | ||
|
||
return arr; | ||
}; | ||
|
||
describe(module.filename, () => { | ||
it("should return an array containing the three input stacks.", () => { | ||
const A = arrayToStack([1,2,3]); | ||
const B = arrayToStack([4,5]); | ||
const C = arrayToStack([6,7,8,9,10]); | ||
const stackArr = threeInOne(A, B, C); | ||
|
||
const expectedStackArr = [1,2,3,null,null,4,5,null,null,null,6,7,8,9,10]; | ||
assert.deepStrictEqual(stackArr.arr, expectedStackArr); | ||
}); | ||
it("should behave like a stack when pushing, popping, and peeking any of the containing stack", () => { | ||
const A = arrayToStack([1,2,3]); | ||
const B = arrayToStack([4,5]); | ||
const C = arrayToStack([6,7,8,9,10]); | ||
const stackArr = threeInOne(A, B, C); | ||
|
||
assert.strictEqual(stackArr.pop(1), 3); | ||
assert.strictEqual(stackArr.pop(1), 2); | ||
assert.strictEqual(stackArr.peek(1), 1); | ||
stackArr.push(1, 100); | ||
assert.strictEqual(stackArr.peek(1), 100); | ||
|
||
assert.strictEqual(stackArr.peek(2), 5); | ||
assert.strictEqual(stackArr.peek(3), 10); | ||
}) | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
const assert = require("assert"); | ||
|
||
class Stack { | ||
constructor() { | ||
this.top; | ||
} | ||
pop() { | ||
if (!this.top) return; | ||
const poppedVal = this.top.val; | ||
this.top = this.top.next; | ||
return poppedVal; | ||
} | ||
push(val) { | ||
const newNode = new StackNode(val); | ||
if (this.top) { | ||
newNode.next = this.top; | ||
this.top = newNode; | ||
} else { | ||
this.top = newNode; | ||
} | ||
} | ||
peek() { | ||
if (!this.top) return; | ||
return this.top.val; | ||
} | ||
isEmpty() { | ||
return !this.top; | ||
} | ||
} | ||
|
||
class StackNode { | ||
constructor(val, next) { | ||
this.val = val === undefined ? null : val; | ||
this.next = next === undefined ? null : next; | ||
} | ||
} | ||
|
||
const arrayToStack = (arr) => { | ||
const stack = new Stack(); | ||
for (const el of arr) { | ||
stack.push(el); | ||
} | ||
return stack; | ||
}; | ||
|
||
class Queue { | ||
constructor() { | ||
this.start; | ||
this.end; | ||
} | ||
add(val) { | ||
const newNode = new QueueNode(val); | ||
if (this.end) { | ||
this.end.next = newNode; | ||
} | ||
this.end = newNode; | ||
if (!this.start) { | ||
this.start = this.end; | ||
} | ||
} | ||
remove() { | ||
if (!this.start) return; | ||
const removedVal = this.start.val; | ||
this.start = this.start.next; | ||
if (!this.start) { | ||
this.end = null; | ||
} | ||
return removedVal; | ||
} | ||
peek() { | ||
return this.start.val; | ||
} | ||
isEmpty() { | ||
return !this.start; | ||
} | ||
} | ||
|
||
class QueueNode { | ||
constructor(val, next) { | ||
this.val = val === undefined ? null : val; | ||
this.next = next === undefined ? null : next; | ||
} | ||
} | ||
|
||
const arrayToQueue = (arr) => { | ||
const queue = new Queue(); | ||
for (const el of arr) { | ||
queue.add(el); | ||
} | ||
return queue; | ||
}; | ||
|
||
describe(`${module.filename} - Stack`, () => { | ||
it("should create a stack and perform stack methods correctly", () => { | ||
const arr = [1, 2, 3, 4, 5]; | ||
const stack = arrayToStack(arr); | ||
assert.strictEqual(stack.peek(), 5); | ||
assert.strictEqual(stack.pop(), 5); | ||
assert.strictEqual(stack.peek(), 4); | ||
assert.ok(!stack.isEmpty()); | ||
assert.strictEqual(stack.pop(), 4); | ||
assert.strictEqual(stack.pop(), 3); | ||
assert.strictEqual(stack.pop(), 2); | ||
assert.strictEqual(stack.pop(), 1); | ||
assert.ok(stack.isEmpty()); | ||
stack.push(10); | ||
assert.strictEqual(stack.peek(), 10); | ||
}); | ||
it("should create a queue and perform queue methods correctly", () => { | ||
const arr = [1, 2, 3, 4, 5]; | ||
const queue = arrayToQueue(arr); | ||
assert.strictEqual(queue.peek(), 1); | ||
assert.strictEqual(queue.remove(), 1); | ||
assert.strictEqual(queue.peek(), 2); | ||
assert.ok(!queue.isEmpty()); | ||
assert.strictEqual(queue.remove(), 2); | ||
assert.strictEqual(queue.remove(), 3); | ||
assert.strictEqual(queue.remove(), 4); | ||
assert.strictEqual(queue.remove(), 5); | ||
assert.ok(queue.isEmpty()); | ||
queue.add(10); | ||
assert.strictEqual(queue.peek(), 10); | ||
}); | ||
}); | ||
|
||
module.exports = { arrayToQueue, arrayToStack, Queue, Stack }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the array that is being created is pretty real. nothing pseudo about it.
also, what if instead of asking for the total array length, the maximum length of each stack is passed as parameter? would that simplify stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm yeah definitely changing the signature seems to be the way to go here. I'll tinker around and see what I can come up with