Solve InterviewCake's second-largest-item-in-bst
Return a function that returns the second largest item in a binary search tree (i.e. BST). A BST is a tree where each node has no more than two children (i.e. one left child and one right child). All of the values in a BST's left subtree must be less than the value of the root node; all of the values in a BST's right subtree must be greater than the value of the root node; both left and right subtrees must also be BSTs themselves. I solved this problem thrice -- improving the performance profile each time. The final solution has a runtime complexity of O(n) and a spacetime complexity of O(1).
This commit is contained in:
		
							parent
							
								
									56d8d1d7b2
								
							
						
					
					
						commit
						319652fe08
					
				
					 2 changed files with 221 additions and 2 deletions
				
			
		
							
								
								
									
										219
									
								
								scratch/deepmind/part_two/second-largest-item-in-bst.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								scratch/deepmind/part_two/second-largest-item-in-bst.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,219 @@ | ||||||
|  | /******************************************************************************* | ||||||
|  |  * Setup | ||||||
|  |  ******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | interface BinaryTreeNode { | ||||||
|  |   value: number; | ||||||
|  |   left: BinaryTreeNode; | ||||||
|  |   right: BinaryTreeNode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class BinaryTreeNode { | ||||||
|  |   constructor(value: number) { | ||||||
|  |     this.value = value; | ||||||
|  |     this.left  = null; | ||||||
|  |     this.right = null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   insertLeft(value: number): BinaryTreeNode { | ||||||
|  |     this.left = new BinaryTreeNode(value); | ||||||
|  |     return this.left; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   insertRight(value: number): BinaryTreeNode { | ||||||
|  |     this.right = new BinaryTreeNode(value); | ||||||
|  |     return this.right; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************************************************************************* | ||||||
|  |  * First solution | ||||||
|  |  ******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * I first solved this problem using O(n) space and O(n*log(n)) | ||||||
|  |  * time. InterviewCake informs me that we can improve both the time and the | ||||||
|  |  * space performance. | ||||||
|  |  */ | ||||||
|  | function findSecondLargest_first(node: BinaryTreeNode): number { | ||||||
|  |   const stack: Array<BinaryTreeNode> = []; | ||||||
|  |   const xs: Array<number> = []; | ||||||
|  |   stack.push(node); | ||||||
|  | 
 | ||||||
|  |   while (stack.length > 0) { | ||||||
|  |     const node = stack.pop() | ||||||
|  | 
 | ||||||
|  |     xs.push(node.value); | ||||||
|  | 
 | ||||||
|  |     if (node.left) { | ||||||
|  |       stack.push(node.left); | ||||||
|  |     } | ||||||
|  |     if (node.right) { | ||||||
|  |       stack.push(node.right); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   xs.sort(); | ||||||
|  | 
 | ||||||
|  |   if (xs.length < 2) { | ||||||
|  |     throw new Error('Cannot find the second largest element in a BST with fewer than two elements.'); | ||||||
|  |   } else { | ||||||
|  |     return xs[xs.length - 2]; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************************************************************************* | ||||||
|  |  * Second solution | ||||||
|  |  ******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * My second solution accumulates a list of the values in the tree using an | ||||||
|  |  * in-order traversal. This reduces the runtime costs from O(n*log(n)) from the | ||||||
|  |  * previous solution to O(n). The memory cost is still O(n), which InterviewCake | ||||||
|  |  * informs me can be reduced to O(1). | ||||||
|  |  */ | ||||||
|  | function findSecondLargest_second(node: BinaryTreeNode): number { | ||||||
|  |   const xs: Array<number> = accumulateInorder(node); | ||||||
|  | 
 | ||||||
|  |   if (xs.length < 2) { | ||||||
|  |     throw new Error('Cannot find the second largest element in a BST with fewer than two elements.'); | ||||||
|  |   } else { | ||||||
|  |     return xs[xs.length - 2]; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Returns an array containing the values of the tree, `node`, sorted in-order | ||||||
|  |  * (i.e. from smallest-to-largest). | ||||||
|  |  */ | ||||||
|  | function accumulateInorder(node: BinaryTreeNode): Array<number> { | ||||||
|  |   let result = []; | ||||||
|  | 
 | ||||||
|  |   if (node.left) { | ||||||
|  |     result = result.concat(accumulateInorder(node.left)); | ||||||
|  |   } | ||||||
|  |   result.push(node.value) | ||||||
|  |   if (node.right) { | ||||||
|  |     result = result.concat(accumulateInorder(node.right)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************************************************************************* | ||||||
|  |  * Third solution | ||||||
|  |  ******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Returns the largest number in a BST. | ||||||
|  |  */ | ||||||
|  | function findLargest(node: BinaryTreeNode): number { | ||||||
|  |   let curr: BinaryTreeNode = node; | ||||||
|  | 
 | ||||||
|  |   while (curr.right) { | ||||||
|  |     curr = curr.right; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return curr.value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Returns the second largest number in a BST | ||||||
|  |  */ | ||||||
|  | function findSecondLargest(node: BinaryTreeNode): number { | ||||||
|  |   let curr = node; | ||||||
|  |   let parent = null; | ||||||
|  | 
 | ||||||
|  |   while (curr.right) { | ||||||
|  |     parent = curr; | ||||||
|  |     curr = curr.right | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (curr.left) { | ||||||
|  |     return findLargest(curr.left); | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     return parent.value; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Tests
 | ||||||
|  | let desc = 'full tree'; | ||||||
|  | let treeRoot = new BinaryTreeNode(50); | ||||||
|  | let leftNode = treeRoot.insertLeft(30); | ||||||
|  | leftNode.insertLeft(10); | ||||||
|  | leftNode.insertRight(40); | ||||||
|  | let rightNode = treeRoot.insertRight(70); | ||||||
|  | rightNode.insertLeft(60); | ||||||
|  | rightNode.insertRight(80); | ||||||
|  | assertEquals(findSecondLargest(treeRoot), 70, desc); | ||||||
|  | 
 | ||||||
|  | desc = 'largest has a left child'; | ||||||
|  | treeRoot = new BinaryTreeNode(50); | ||||||
|  | leftNode = treeRoot.insertLeft(30); | ||||||
|  | leftNode.insertLeft(10); | ||||||
|  | leftNode.insertRight(40); | ||||||
|  | rightNode = treeRoot.insertRight(70); | ||||||
|  | rightNode.insertLeft(60); | ||||||
|  | assertEquals(findSecondLargest(treeRoot), 60, desc); | ||||||
|  | 
 | ||||||
|  | desc = 'largest has a left subtree'; | ||||||
|  | treeRoot = new BinaryTreeNode(50); | ||||||
|  | leftNode = treeRoot.insertLeft(30); | ||||||
|  | leftNode.insertLeft(10); | ||||||
|  | leftNode.insertRight(40); | ||||||
|  | rightNode = treeRoot.insertRight(70); | ||||||
|  | leftNode = rightNode.insertLeft(60); | ||||||
|  | leftNode.insertRight(65); | ||||||
|  | leftNode = leftNode.insertLeft(55); | ||||||
|  | leftNode.insertRight(58); | ||||||
|  | assertEquals(findSecondLargest(treeRoot), 65, desc); | ||||||
|  | 
 | ||||||
|  | desc = 'second largest is root node'; | ||||||
|  | treeRoot = new BinaryTreeNode(50); | ||||||
|  | leftNode = treeRoot.insertLeft(30); | ||||||
|  | leftNode.insertLeft(10); | ||||||
|  | leftNode.insertRight(40); | ||||||
|  | rightNode = treeRoot.insertRight(70); | ||||||
|  | assertEquals(findSecondLargest(treeRoot), 50, desc); | ||||||
|  | 
 | ||||||
|  | desc = 'descending linked list'; | ||||||
|  | treeRoot = new BinaryTreeNode(50); | ||||||
|  | leftNode = treeRoot.insertLeft(40); | ||||||
|  | leftNode = leftNode.insertLeft(30); | ||||||
|  | leftNode = leftNode.insertLeft(20); | ||||||
|  | leftNode = leftNode.insertLeft(10); | ||||||
|  | assertEquals(findSecondLargest(treeRoot), 40, desc); | ||||||
|  | 
 | ||||||
|  | desc = 'ascending linked list'; | ||||||
|  | treeRoot = new BinaryTreeNode(50); | ||||||
|  | rightNode = treeRoot.insertRight(60); | ||||||
|  | rightNode = rightNode.insertRight(70); | ||||||
|  | rightNode = rightNode.insertRight(80); | ||||||
|  | assertEquals(findSecondLargest(treeRoot), 70, desc); | ||||||
|  | 
 | ||||||
|  | desc = 'one node tree'; | ||||||
|  | treeRoot = new BinaryTreeNode(50); | ||||||
|  | assertThrowsError(() => findSecondLargest(treeRoot), desc); | ||||||
|  | 
 | ||||||
|  | desc = 'when tree is empty'; | ||||||
|  | treeRoot = null; | ||||||
|  | assertThrowsError(() => findSecondLargest(treeRoot), desc); | ||||||
|  | 
 | ||||||
|  | function assertEquals(a, b, desc) { | ||||||
|  |   if (a === b) { | ||||||
|  |     console.log(`${desc} ... PASS`); | ||||||
|  |   } else { | ||||||
|  |     console.log(`${desc} ... FAIL: ${a} != ${b}`) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function assertThrowsError(func, desc) { | ||||||
|  |   try { | ||||||
|  |     func(); | ||||||
|  |     console.log(`${desc} ... FAIL`); | ||||||
|  |   } catch (e) { | ||||||
|  |     console.log(`${desc} ... PASS`); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -23,7 +23,7 @@ | ||||||
| * Trees and graphs | * Trees and graphs | ||||||
| ** DONE Balanced Binary Tree | ** DONE Balanced Binary Tree | ||||||
| ** DONE Binary Search Tree Checker | ** DONE Binary Search Tree Checker | ||||||
| ** TODO 2nd Largest Item in a Binary Search Tree | ** DONE 2nd Largest Item in a Binary Search Tree | ||||||
| ** TODO Graph Coloring | ** TODO Graph Coloring | ||||||
| ** TODO MeshMessage | ** TODO MeshMessage | ||||||
| ** TODO Find Repeat, Space Edition BEAST MODE | ** TODO Find Repeat, Space Edition BEAST MODE | ||||||
|  | @ -34,7 +34,7 @@ | ||||||
| ** TODO The Cake Thief | ** TODO The Cake Thief | ||||||
| ** DONE Balanced Binary Tree | ** DONE Balanced Binary Tree | ||||||
| ** DONE Binary Search Tree Checker | ** DONE Binary Search Tree Checker | ||||||
| ** TODO 2nd Largest Item in a Binary Search Tree | ** DONE 2nd Largest Item in a Binary Search Tree | ||||||
| * Queues and stacks | * Queues and stacks | ||||||
| ** TODO Largest Stack | ** TODO Largest Stack | ||||||
| ** TODO Implement A Queue With Two Stacks | ** TODO Implement A Queue With Two Stacks | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue