11package com .leetcode .trees ;
22
3+ import java .util .LinkedList ;
4+ import java .util .List ;
5+ import java .util .Queue ;
6+ import java .util .Stack ;
7+
38import static org .junit .jupiter .api .Assertions .assertEquals ;
49
510/**
611 * Level: Hard
712 * Problem Link: https://leetcode.com/problems/closest-binary-search-tree-value-ii/
813 * Problem Description:
14+ * Given a non-empty binary search tree and a target value, find k values in the BST that are closest to the target.
15+ *
16+ * Note:
17+ * - Given target value is a floating point.
18+ * - You may assume k is always valid, that is: k ≤ total nodes.
19+ * - You are guaranteed to have only one unique set of k values in the BST that are closest to the target.
20+ *
21+ * Example:
22+ * Input: root = [4,2,5,1,3], target = 3.714286, and k = 2
923 *
24+ * 4
25+ * / \
26+ * 2 5
27+ * / \
28+ * 1 3
29+ *
30+ * Output: [4,3]
31+ *
32+ * Follow up:
33+ * Assume that the BST is balanced, could you solve it in less than O(n) runtime (where n = total nodes)?
1034 *
1135 * @author rampatra
1236 * @since 2019-07-31
1337 */
1438public class ClosestBinarySearchTreeValueII {
1539
40+
41+ /**
42+ * The idea is simple. We do the inorder traversal and keep the values less than or equal to target in a stack and
43+ * the values greater than target in a queue. And finally, we compare values from both stack and queue and take
44+ * whichever is the closest to target value each time.
45+ *
46+ * Note: We can optimize it even further in terms of space. We can get rid of the stack and queue and just fill up
47+ * the result list in the recursive inOrder call. Once the result list is of size k, we can compare and remove the
48+ * farthest value and insert the closer value. See {@link ClosestBinarySearchTreeValueII#closestKValuesOptimized(TreeNode, double, int)}.
49+ *
50+ * @param root
51+ * @param target
52+ * @param k
53+ * @return
54+ */
55+ public static List <Integer > closestKValues (TreeNode root , double target , int k ) {
56+ int count = 0 ;
57+ List <Integer > closestKValues = new LinkedList <>();
58+
59+ Stack <Integer > predecessors = new Stack <>();
60+ Queue <Integer > successors = new LinkedList <>();
61+ inOrder (root , predecessors , successors , target , k );
62+
63+ while (count < k ) {
64+ if (predecessors .empty ()) {
65+ closestKValues .add (successors .poll ());
66+ } else if (successors .isEmpty ()) {
67+ closestKValues .add (predecessors .pop ());
68+ } else if (Math .abs (target - predecessors .peek ()) < Math .abs (target - successors .peek ())) {
69+ closestKValues .add (predecessors .pop ());
70+ } else {
71+ closestKValues .add (successors .poll ());
72+ }
73+ count ++;
74+ }
75+
76+ return closestKValues ;
77+ }
78+
79+ private static void inOrder (TreeNode root , Stack <Integer > predecessors , Queue <Integer > successors , double target , int k ) {
80+ if (root == null || successors .size () == k ) return ;
81+ inOrder (root .left , predecessors , successors , target , k );
82+ if (root .val <= target ) {
83+ predecessors .add (root .val );
84+ } else {
85+ successors .add (root .val );
86+ }
87+ inOrder (root .right , predecessors , successors , target , k );
88+ }
89+
90+
1691 /**
17- * @param node
18- * @param parentNode
19- * @param val
20- * @param diff
92+ * This approach is similar to the above one but it doesn't use stack or queue.
93+ *
94+ * @param root
95+ * @param target
96+ * @param k
2197 * @return
2298 */
23- public static TreeNode findNodeWithClosestValue (TreeNode node , TreeNode parentNode , int val , int diff ) {
24- return null ;
99+ public static List <Integer > closestKValuesOptimized (TreeNode root , double target , int k ) {
100+ LinkedList <Integer > closestKValues = new LinkedList <>();
101+ inOrder (root , target , k , closestKValues );
102+ return closestKValues ;
103+ }
104+
105+ private static void inOrder (TreeNode root , double target , int k , LinkedList <Integer > closestKValues ) {
106+ if (root == null ) return ;
107+
108+ inOrder (root .left , target , k , closestKValues );
109+ if (closestKValues .size () == k ) {
110+ //if size k, add current and remove head if it's closer to target, otherwise return
111+ if (Math .abs (target - root .val ) < Math .abs (target - closestKValues .peekFirst ()))
112+ closestKValues .removeFirst ();
113+ else {
114+ return ;
115+ }
116+ }
117+ closestKValues .add (root .val );
118+ inOrder (root .right , target , k , closestKValues );
25119 }
26120
27121 public static void main (String [] args ) {
@@ -46,13 +140,8 @@ public static void main(String[] args) {
46140 root .left .left .right = new TreeNode (6 );
47141 root .right .right = new TreeNode (20 );
48142
49- assertEquals (13 , findNodeWithClosestValue (root , root , 15 , Integer .MAX_VALUE ).val );
50- assertEquals (13 , findNodeWithClosestValue (root , root , 13 , Integer .MAX_VALUE ).val );
51- assertEquals (9 , findNodeWithClosestValue (root , root , 9 , Integer .MAX_VALUE ).val );
52- assertEquals (2 , findNodeWithClosestValue (root , root , 2 , Integer .MAX_VALUE ).val );
53- assertEquals (2 , findNodeWithClosestValue (root , root , 1 , Integer .MAX_VALUE ).val );
54- assertEquals (6 , findNodeWithClosestValue (root , root , 6 , Integer .MAX_VALUE ).val );
55- assertEquals (13 , findNodeWithClosestValue (root , root , 11 , Integer .MAX_VALUE ).val ); // tie b/w 9 and 13
143+ assertEquals ("[9, 8, 7, 6, 5]" , closestKValues (root , 8.5 , 5 ).toString ());
144+ assertEquals ("[5, 6, 7, 8, 9]" , closestKValuesOptimized (root , 8.5 , 5 ).toString ());
56145
57146 /*
58147 BST looks like:
@@ -67,10 +156,11 @@ public static void main(String[] args) {
67156 root .left = new TreeNode (7 );
68157 root .right = new TreeNode (13 );
69158 root .left .left = new TreeNode (5 );
70- root .left .right = new TreeNode (8 );
71159 root .right .left = new TreeNode (13 );
160+ root .left .right = new TreeNode (8 );
72161 root .right .right = new TreeNode (20 );
73162
74- assertEquals (13 , findNodeWithClosestValue (root , root , 15 , Integer .MAX_VALUE ).val );
163+ assertEquals ("[13, 13, 9, 20, 8]" , closestKValues (root , 14 , 5 ).toString ());
164+ assertEquals ("[8, 9, 13, 13, 20]" , closestKValuesOptimized (root , 14 , 5 ).toString ());
75165 }
76166}
0 commit comments