Several years ago (November 03, 2016) I generated the post Insertion Sort in this blog. The code snippets do not look nice. It seems that the tool I was using to format source code is no longer working as expected.

In this post I will revisit the subject. Will use the Java programming language and the VSCode IDE on a Windows 11 computer.

9, 2, 7, 1, 8, 3 main <<< arr: [9, 2, 7, 1, 8, 3] main <<< sorted arr: [1, 2, 3, 7, 8, 9] 9,2,7,1,8,3,4 main <<< arr: [9, 2, 7, 1, 8, 3, 4] main <<< sorted arr: [1, 2, 3, 4, 7, 8, 9] 6 main <<< arr: [6] main <<< sorted arr: [6] 8,5 main <<< arr: [8, 5] main <<< sorted arr: [5, 8] 6,5,3,1,8,7,2,4 main <<< arr: [6, 5, 3, 1, 8, 7, 2, 4] main <<< sorted arr: [1, 2, 3, 4, 5, 6, 7, 8]

We have a set of test cases. Test cases are separated by two blank lines.

The first is the input line. It contains a list of unsorted integers.

Our test scaffold seems to read the input line and populate an int[] arr and display its contents. This is done to verify that all is well before calling the function of interest that implements the Insertion Sort algorithm.

The next and last line seems to display the sorted array.

/** * Test scaffold * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read data and populate array **** int[] arr = Arrays.stream(br.readLine().trim().split(",")) .map( s -> s.trim() ) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< arr: " + Arrays.toString(arr)); // **** make a copy of the input array **** int[] shuffled = arr.clone(); // **** loop a few times **** int n = 13; for (int i = 0; i < n; i++) { // **** get a copy of the input array **** arr = shuffled.clone(); // ???? ???? // System.out.println("main <<< arr: " + Arrays.toString(arr)); // **** sort the elements in the array **** insertionSort(arr); // **** check if the elements in the array are sorted **** boolean isSorted = isSorted(arr); if (isSorted && i == 0) System.out.println("main <<< sorted arr: " + Arrays.toString(arr)); else if (!isSorted) { System.err.println("main <<< not sorted arr: " + Arrays.toString(arr)); System.exit(-1); } } }

This is the code that implements our test scaffold.

The input line is read and the integer values are placed in the int[] arr array.

A copy of the array is made in the int[] shuffled which will be used to restore the int[] arr after it has been sorted.

We then enter a loop that repeats n times.

In the loop we restore the original array and call the function of interest to sort it.

The auxiliary function isSorted() is called to determine if the resulting array is or is not sorted.

/** * Check if the elements in the array are sorted. */ static boolean isSorted(int[] arr) { // **** sanity checks **** int n = arr.length; if (n <= 1) return true; // **** traverse array **** for (int i = 0; i < n - 1; i++) { if (arr[i] > arr[i + 1]) return false; } // **** array is sorted **** return true; }

The isSorted () utility function determines if the specified array is or is not sorted.

/** * Insertion Sort * * Execution: O(n^2) - Space: O(1) */ static void insertionSort(int[] arr) { // **** sanity checks **** int n = arr.length; if (n <= 1) return; // **** sort array contents **** for (int i = 1; i < n; i++) { // **** key to be placed in sorted order **** int key = arr[i]; // **** **** int j = i - 1; // ???? ???? // System.out.println("<<< i: " + i + " key: " + key + " j: " + j); // **** **** for ( ; j >= 0 && arr[j] > key; j--) { // **** **** arr[j + 1] = arr[j]; // // ???? ???? // System.out.println("<<< arr: " + Arrays.toString(arr)); } // **** put key in array in sorted order **** arr[j + 1] = key; // ???? ???? // System.out.println("<<< arr: " + Arrays.toString(arr)); } }

This is the implementation of the Insertion Sort in Java.

The function performs some sanity checks.

We then enter a loop which will move from the second to the last integer in the int[] arr placing the key in order.

In the loop the key value is specified and j is used to process the candidate location in which to stop after the first i – 1 locations have been compared and moved looking for the location of the key.

This is a good time to get a deck of cards, shuffle them, and serve a hand of two or more cards. Holding the cards, order them from left to right in ascending order. Pay attention to what you are doing and check it against the code. Chances are that you are following the algorithm implemented in this function.

Hope you enjoyed working on this algorithm as much as I did. The entire code for this project can be found in my GitHub repository named InsertionSort.

If you have comments or questions regarding this, or any other post in this blog, please do not hesitate and leave me a note below. I will reply as soon as possible.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge, and enhance your developer / engineering toolset.

Thanks for reading this post and feel free to connect with me John Canessa on LinkedIn.

Enjoy;

John

]]>In the meantime I have been taking a few on-line courses.

I am almost done with the course C++20 Fundamentals by Paul J. Deitel. The course was released July, 2021 and published by Pearson3. The ISBN: 013687518. This online course is made available through O’Reilly. I am a member of the Association of Computing Machinery and have access to several O’Reilly titles.

There is an associated book which is still being produced. The title is C++20 for Programmers: An Object’s-Natural Approach (Deitel Developer Series) 3rd Edition by Paul Deitel.

I placed my order on Amazon on December 6, 2021. I just checked the status of the delivery. Not yet shipped. We will email you when we have an estimated delivery date.

Last I heard it was going to be released in March, 2022. Today is the 28th of the month so it can still arrive on time.

I am also taking a set of seven on-line courses for “C Programming with Linux Professional Certificate” on edX | Dartmouth College, IMT. So far I have completed three of them. If interested, you can see the certificates on my LinkedIn profile. Currently working on the fourth course which I hope to complete later this week.

I also want to thank all **12’314** subscribers to this blog.

A day or so ago I was looking at my blog and noticed that it did not have a blog related to **Bubble Sort** so I decided to generate this post.

We will implement and test the Bubble Sort function using the Java programming language and the VSCode IDE on a Windows 11 computer.

Given an array of integers (could be any other type or class), we need to sort the elements in ascending or descending order.

7, 2, 19, -3, 17 main <<< arr: [7, 2, 19, -3, 17] main <<< ascending: [-3, 2, 7, 17, 19] main <<< arr: [7, 2, 19, -3, 17] main <<< descending: [19, 17, 7, 2, -3] 7, 2, 19, -3, 17, 9, -8, 3, -1, 5 main <<< arr: [7, 2, 19, -3, 17, 9, -8, 3, -1, 5] main <<< ascending: [-8, -3, -1, 2, 3, 5, 7, 9, 17, 19] main <<< arr: [7, 2, 19, -3, 17, 9, -8, 3, -1, 5] main <<< descending: [19, 17, 9, 7, 5, 3, 2, -1, -3, -8]

We have two test cases. The test cases are separated by two blank lines.

The first line in the test case is the input line. It contains a set of unordered integers.

Our test scaffold seems to allocate an int[] array and load the input values. The contents of the array are then displayed in order to verify that all is well before calling the implementation of the Bubble Sort algorithm.

It seems that the test code makes a call to the function of interest indicating that the values should be sorted in **ascending **order. The returned array is displayed.

The test scaffold then displays the initial array. This is done to verify that the next call to the function of interest will use the values in the original order.

The test scaffold calls the function of interest indicating that the values of the array are sorted in **descending **order. After the call to the function of interest completes, the returned array is displayed.

/** * Test scaffold. * * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read array of integers **** int[] arr = Arrays.stream(br.readLine().trim().split(",")) .map(x -> x.trim()) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? display array ???? System.out.println("main <<< arr: " + Arrays.toString(arr)); // **** sort array using bubble sort **** int[] sorted = bubbleSort(arr, ASC_ORDER); // ???? display sorted array ???? System.out.println("main <<< ascending: " + Arrays.toString(sorted)); // **** sort array using bubble sort **** sorted = bubbleSort(arr, DES_ORDER); // ???? display sorted array ???? System.out.println("main <<< descending: " + Arrays.toString(sorted)); }

The test scaffold starts by reading the input line and declaring and populating the int[] array arr. The contents of the array are displayed to verify that all is well so far.

The function of interest is invoked to sort the array in ascending order. The resulting array is displayed.

The function of interest is called a second time but the second argument indicates to sort the array in descending order. The resulting array is displayed.

/** * Sort the array using Bubble Sort. * * Execution: O(n^2) Space: O(1) * * @param args * @throws IOException */ static int[] bubbleSort(int[] arr, int sortOrder) { // **** sanity check(s) **** if (arr == null) return null; int n = arr.length; if (n <= 1) return arr; // **** initialization **** int[] sorted = arr.clone(); // **** **** for (var i = 0; i < n - 1; i++) { // **** **** for (var j = 0; j < n - i - 1; j++) { // **** swap values in ascending order **** if (sortOrder == ASC_ORDER && sorted[j] > sorted[j + 1]) { sorted = swapAdjacent(sorted, j); } // **** swap values in descending order **** else if (sortOrder == DES_ORDER && sorted[j] < sorted[j + 1]) { sorted = swapAdjacent(sorted, j); } } } // **** return sorted array **** return sorted; }

The function of interest starts by performing some sanity checks.

The initialization step is not part of the algorithm. I decided to clone the input array so the order of the values would be preserved; otherwise the ordering would be different in the second invocation of the function of interest. I should have cloned the array in the main() function, not here.

We need to take the first entry in the array and compare it to the second. Depending on the sorting order, we need to swap them in order to bubble the highest or lowest value up (right) depending if the sorting order is ascending or descending respectively.

Take a moment and verify what happens to the first two entries in the array. The inner loop will bubble the lowest or highest entry to the right of the array. As the process repeats the values will end up in sorted values on the right side of the array. When done all array values will be sorted.

/** * Swap adjacent elements in array index i and i + 1. * Auxiliary function. */ private static int[] swapAdjacent(int[] arr, int i) { // **** swap elements **** int tmp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = tmp; // **** return swapped array **** return arr; }

This auxiliary function is used to swap the two adjacent entries specified by `i` and `i + 1` in the specified array. The updated array is returned to the caller.

// **** define sort order **** static final int ASC_ORDER = 0; static final int DES_ORDER = 1;

These lines declare the sorting order that needs to be specified to the function of interest.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named BubbleSort.

Please note that the code here presented might not be the best possible solution. After solving a problem, you should take a look at the best accepted solutions provided by the different websites (i.e., HackerRank, LeetCode to name a few). The idea is to learn concepts, not to memorize solutions.

If you have comments or questions regarding this, or any other post in this blog, please do not hesitate and leave me a note below. I will reply as soon as possible.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge, and enhance your developer / engineering toolset.

Thanks for reading this post and feel free to connect with me John Canessa on LinkedIn.

Enjoy;

John

]]>My original post Largest Triple Products was superseded by Largest Triple Products – Revisited and by this latest post.

The motivation for this post came from a question left by Brent Boyer which suggested an implementation for the function of interest. I have included it in this post as `findMaxProduct3`.

You're given a list of n integers arr[0..(n-1)]. You must compute a list output[0..(n-1)] such that, for each index i (between 0 and n-1, inclusive), output[i] is equal to the product of the three largest elements out of arr[0..i] (or equal to -1 if i < 2, as arr[0..i] then includes fewer than three elements). Note that the three largest elements used to form any product may have the same values as one another, but they must be at different indices in arr. Input: o n is in the range [1, 100,000]. o Each value arr[i] is in the range [1, 1,000]. Output: Return a list of n integers output[0..(n-1)], as described above.

This is the original text for the problem description. I am not sure if it is still valid for the current version of the problem if it is still available.

int[] findMaxProduct(int[] arr) { }

The signature for the function of interest in Java has not changed.

1,2,3,4,5 main <<< arr: [1, 2, 3, 4, 5] main <<< findMaxProduct0: [-1, -1, 6, 24, 60] main <<< findMaxProduct1: [-1, -1, 6, 24, 60] main <<< findMaxProduct2: [-1, -1, 6, 24, 60] main <<< findMaxProduct3: [-1, -1, 6, 24, 60] 2,1,2,1,2 main <<< arr: [2, 1, 2, 1, 2] main <<< findMaxProduct0: [-1, -1, 4, 4, 8] main <<< findMaxProduct1: [-1, -1, 4, 4, 8] main <<< findMaxProduct2: [-1, -1, 4, 4, 8] main <<< findMaxProduct3: [-1, -1, 4, 4, 8] 1,2,3,4,3,2,1 main <<< arr: [1, 2, 3, 4, 3, 2, 1] main <<< findMaxProduct0: [-1, -1, 6, 24, 36, 36, 36] main <<< findMaxProduct1: [-1, -1, 6, 24, 36, 36, 36] main <<< findMaxProduct2: [-1, -1, 6, 24, 36, 36, 36] main <<< findMaxProduct3: [-1, -1, 6, 24, 36, 36, 36] -2 main <<< n: 67680 main <<< findMaxProduct0: 16 ms main <<< findMaxProduct1: 2 ms main <<< findMaxProduct2: 1 ms main <<< findMaxProduct3: 1 ms -2 main <<< n: 85454 main <<< findMaxProduct0: 17 ms main <<< findMaxProduct1: 3 ms main <<< findMaxProduct2: 2 ms main <<< findMaxProduct3: 1 ms -2 main <<< n: 43555 main <<< findMaxProduct0: 14 ms main <<< findMaxProduct1: 1 ms main <<< findMaxProduct2: 1 ms main <<< findMaxProduct3: 1 ms -2 main <<< n: 12685 main <<< findMaxProduct0: 13 ms main <<< findMaxProduct1: 0 ms main <<< findMaxProduct2: 0 ms main <<< findMaxProduct3: 0 ms -2 main <<< n: 24203 main <<< findMaxProduct0: 15 ms main <<< findMaxProduct1: 1 ms main <<< findMaxProduct2: 0 ms main <<< findMaxProduct3: 1 ms

Each test case is separated from the other by two blank lines.

The first line is the input line. It holds a list of integers which will be used to populate an int[] array which will be used as the int[] arr argument to the function of interest. In addition if you wish to test larger input arrays, you may enter a single “-2”. This will populate a large array with random integer values.

Our test scaffold which **IS NOT PART OF THE SOLUTION**, reads the input line. Based on the contents it populates the int[] `arr` with the specified set of numbers or generates a large array with random numbers.

If you specify a list of integers as shown in the first three test cases, the test scaffold displays the contents of the int[] `arr` for us to verify all is well before calling the four implementations of the function of interest.

If you specify “-2” the contents of the input array are not displayed. Instead the number of random values is displayed as illustrated by the variable `n`.

The results as required are displayed only for the test cases in which the input values are specified. For the test cases with a large number of values, the time in milliseconds is displayed instead.

Before we look at the actual code, please take a moment to look at the results of the test cases.

Without a doubt the first implementation of the function of interest is the slowest. We will see why as we look at the code for the implementation.

The other three implementations are close and for many test cases which I have not included, the execution time improves from top to bottom. That said, you always need to check different implementations against the actual input data.

/** * Test scaffolding * * !!!! NOT PART OF SOLUTION !!!! * * @throws IOException */ public static void main(String[] args) throws IOException { // **** declare arr **** int[] arr = null; // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read and split strings with integers **** String[] strs = br.readLine().trim().split(","); // **** close buffered reader **** br.close(); // **** check if we need to generate our own array of values **** if (strs.length == 1 && strs[0].equals("-2")) { // **** **** Random rand = new Random(); // **** generate n **** int n = rand.nextInt(100000); // ???? ???? System.out.println("main <<< n: " + n); // **** create arr **** arr = new int[n]; // **** populate arr **** for (var i = 0; i < n; i++) arr[i] = rand.nextInt(1000); } else { // **** create integer arr from string values **** arr = Arrays.stream(strs) .mapToInt(Integer::parseInt) .toArray(); } // **** short arrays **** if (arr.length < 16) { System.out.println("main <<< arr: " + Arrays.toString(arr)); // **** find the max products and display result (take 1) **** System.out.println("main <<< findMaxProduct0: " + Arrays.toString(findMaxProduct0(arr))); // **** find the max products and display result (take 2) **** System.out.println("main <<< findMaxProduct1: " + Arrays.toString(findMaxProduct1(arr))); // **** find the max products and display result (take 3) **** System.out.println("main <<< findMaxProduct2: " + Arrays.toString(findMaxProduct2(arr))); // **** find the max products and display result (take 4) **** System.out.println("main <<< findMaxProduct3: " + Arrays.toString(findMaxProduct3(arr))); } else { // **** **** for (var i = 0; i < 4; i++) { // **** start timer **** long start = System.currentTimeMillis(); // **** call function of interest **** switch (i) { case 0: findMaxProduct0(arr); break; case 1: findMaxProduct1(arr); break; case 2: findMaxProduct2(arr); break; case 3: findMaxProduct3(arr); break; default: System.err.println("main <<< UNEXPECTED i: " + i); System.exit(-1); break; } // **** stop timer **** long end = System.currentTimeMillis(); // **** display execution time **** System.out.println("main <<< findMaxProduct" + i + ": " + (end - start) + " ms"); } }

This is the test test scaffold which **IS NOT PART OF THE SOLUTION**, and was modified from the previous two posts.

The code checks the input line if the user entered a “-2”. If so a random number is generated for the size of the array. The array is then populated with random values.

If a set of int values is offered to the program, the `arr` is populated with such values and the contents of the int[] `arr` is displayed. This is done to verify that all is well before calling the different implementations of the function of interest.

If we are dealing with short length arrays, we call the four different implementations and display the returned values. We can use such test cases to verify that the different implementations return the same results and that the values match.

Due to the size of the large arrays, we collect the `start` time, call an implementation of the function of interest, collect the `end` time and display time the implementation took to process the data.

/** * Uses a priority queue. */ static int[] findMaxProduct0(int[] arr) { // **** sanity check(s) **** if (arr == null) return null; if (arr.length == 0) return new int[0]; if (arr.length == 1) return new int[] {-1}; if (arr.length == 2) return new int[] {-1, -1}; // **** initialization **** int[] prods = new int[arr.length]; prods[0] = prods[1] = -1; int prod = arr[0] * arr[1] * arr[2]; prods[2] = prod; PriorityQueue<Integer> maxVals = new PriorityQueue<Integer>( (a,b) -> (a - b) ); maxVals.add(arr[0]); maxVals.add(arr[1]); maxVals.add(arr[2]); // **** traverse array O(n) **** for (int i = 3; i < arr.length; i++) { // **** update max values (if needed) **** if (arr[i] > maxVals.peek()) { // **** remove from head of the queue (smallest value) **** maxVals.remove(); // **** add new highest value **** maxVals.add(arr[i]); // **** compute the max product **** Integer[] tmp = (Integer[]) maxVals.toArray(new Integer[0]); prod = tmp[0] * tmp[1] * tmp[2]; } // **** set the max product in the array **** prods[i] = prod; } // **** return array of max products **** return prods; }

This first implementation of the function of interest uses a priority queue.

This function has been slightly edited. If interested in additional comments please refer to the first post in this series.

/** * Edited previous implementation. * Uses array of thee largest values. */ static int[] findMaxProduct1(int[] arr) { // **** sanity check(s) **** if (arr == null) return null; if (arr.length == 0) return new int[0]; if (arr.length == 1) return new int[] {-1}; if (arr.length == 2) return new int[] {-1, -1}; // **** initialization **** int[] prods = new int[arr.length]; prods[0] = prods[1] = -1; int prod = arr[0] * arr[1] * arr[2]; prods[2] = prod; // **** compute the largest three values and return them in the array **** int[] largest = new int[] {Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE}; for (int i = 0; i < 3; i++) largest = largestValues(largest, arr[i]); // **** traverse array multiplying the current 3 largest entries O(n) **** for (int i = 3; i < arr.length; i++) { // **** update the top three largest values **** largest = largestValues(largest, arr[i]); // **** generate the product of the three largest values **** prods[i] = largest[0] * largest[1] * largest[2]; } // **** return array of max products **** return prods; }

In this implementation which was in the previous post in this blog, we use an array of three integers named `largest` in which we keep the largest integers we have encountered so far as we traverse the input int[] `arr`.

/** * Largest values in descending order. * * Utility method. */ static private int[] largestValues(int[] largest, int val) { // **** update the top three values **** if (val > largest[0]) { largest[2] = largest[1]; largest[1] = largest[0]; largest[0] = val; } // **** update the top two values **** else if (val > largest[1]) { largest[2] = largest[1]; largest[1] = val; } // **** update the top value **** else if (val > largest[2]) { largest[2] = val; } // **** return the top three values **** return largest; }

This is an auxiliary function which is used to keep the three values in the specified array in ascending order. It does so by inserting the int `val` and updating the other two values as needed to keep the int[] `arr` in ascending order.

For additional information please refer to the second post in this blog in this series.

/** * Single sort of three elements. */ static int[] findMaxProduct2(int[] arr) { // **** sanity check(s) **** if (arr == null) return null; if (arr.length == 0) return new int[] {}; if (arr.length == 1) return new int[] { -1 }; if (arr.length == 2) return new int[] { -1, -1 }; // **** initialization **** int[] prods = new int[arr.length]; prods[0] = prods[1] = -1; int prod = arr[0] * arr[1] * arr[2]; prods[2] = prod; int[] tops = new int[]{ arr[0], arr[1], arr[2]}; // **** sort tops array - O(3 log(3)) **** Arrays.sort(tops); // **** process array - O(n) **** for (var i = 3; i < arr.length; i++) { // **** skip this arr entry **** for ( ; i < arr.length && arr[i] < tops[0]; i++) prods[i] = prods[i - 1]; // **** check if done processing arr **** if (i >= arr.length) break; // **** update all three entries **** if (arr[i] > tops[2]) { tops[0] = tops[1]; tops[1] = tops[2]; tops[2] = arr[i]; } // **** update two entries **** else if (arr[i] > tops[1]) { tops[0] = tops[1]; tops[1] = arr[i]; } // **** update one entry **** else if (arr[i] > tops[0]) { tops[0] = arr[i]; } // **** compute and save product **** prods[i] = tops[0] * tops[1] * tops[2]; } // **** return array of max products **** return prods; }

In this implementation we will use the `prods` array to keep the results as we traverse the input array.

The int[] `tops` array of three elements is initialized with the first three values from the int[] `arr` and then is sorted by calling the `Arrays.sort` method.

The main loop is used to maintain the int[] `tops` array in ascending order.

The `for` loop skips `arr` entries that are lower in value than the first `tops` entry.

For an entry that is larger we update an entry in the `tops` array. We do this by checking the current `arr` value and comparing it to the last, middle or first entry in `tops`. By making modifications in one of the cases, the `tops` array is kept in monotonically ascending order.

Since the `tops` array has changed, the associated entry in `prods` is updated with the newly computed value.

When all is said and done the function of interest returns the `prods` array.

/** * Contributed by Brent Boyer. * * Multiple three element sorts. */ static int[] findMaxProduct3(int[] arr) { // **** sanity check(s) **** if (arr == null) return null; if (arr.length == 0) return new int[0]; if (arr.length == 1) return new int[] {-1}; if (arr.length == 2) return new int[] {-1, -1}; // **** **** int[] result = new int[arr.length]; result[0] = -1; result[1] = -1; result[2] = arr[0] * arr[1] * arr[2]; int[] largest3 = new int[3]; largest3[0] = arr[0]; largest3[1] = arr[1]; largest3[2] = arr[2]; // **** sort largest array - O(3 log(3)) **** Arrays.sort(largest3); // **** **** int productLast = result[2]; // **** for top performance, since 3 is such a small number, // use inline the code to insert arr[i] into its proper // slot and drop the original largest3[0] **** for (int i = 3; i < arr.length; i++) { // **** sort and compute product **** if (arr[i] > largest3[0]) { // **** replace smallest **** largest3[0] = arr[i]; // **** sort largest array - O(3 log(3)) **** Arrays.sort(largest3); // **** compute product **** productLast = largest3[0] * largest3[1] * largest3[2]; } // **** store product **** result[i] = productLast; } // **** return products **** return result; }

This implementation of the function of interest was provided by Brent Boyer. I made simple editing modifications so the code would be consistent with previous implementations of the function of interest.

What is different in this implementation is that instead of keeping the int[] `largest` array of three elements sorted by manipulating the entries, it just replaces the smallest entry found in `largest[0]` with the current `arr[i]` and then calls the `Arrays.sort` method.

I found this approach very interesting and as you can see in our results, it seems to perform in most cases, better than the other three implementations. If interested you can get the code for this post from GitHub and experiment with it to gain a deeper understanding of this O(n log(n)) sort function in action.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named LargestTripleProducts.

Please note that the code here presented might not be the best possible solution. After solving a problem, you should take a look at the best accepted solutions provided by the different websites (i.e., HackerRank, LeetCode to name a few). The idea is to learn concepts, not to memorize solutions.

If you have comments or questions regarding this, or any other post in this blog, please do not hesitate and leave me a note below. I will reply as soon as possible.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge, and enhance your developer / engineering toolset.

Thanks for reading this post and feel free to connect with me John Canessa on LinkedIn.

Enjoy;

John

]]>The motivation came from the on-line course C++20 Fundamentals by Paul J. Deitel, included in the O’Reilly Learning Platform offered by the Association for Computing Machinery.

C++20 for Programmers by Paul Deitel and Harvey Deitel Format: Paperback Language: English ISBN: 0136905692 ISBN13: 9780136905691 Release Date: March 2022 Publisher: Pearson Education Length: 1008 Pages

I have preordered the new book associated with the course on Amazon. Hopefully it should arrive next month.

I believe we have covered Fibonacci Numbers in different posts i.e., Fibonacci Revisited, Fibonacci Sequence, and Reverse Fibonacci Series among others, in this blog.

#include <iostream> #include <chrono> using namespace std; // **** function prototype **** long fibonacci0(long number); long fibonacci(long number); // **** memoization **** long memo[50]{ 0 };

We will cover two recursive functions that generate Fibonacci numbers. The first will follow the recursive description and will not use memoization. The second will use memoization.

/* * Test scaffold. */ int main() { // **** start timer **** auto start = chrono::steady_clock::now(); // **** calculate the fibonacci values of 0 through 10 **** for (int counter{ 0 }; counter <= 10; ++counter) cout << "main <<< fibonacci0(" << counter << "): " << fibonacci0(counter) << endl; cout << endl; // **** display higher fibonacci values **** cout << "main <<< fibonacci0(20): " << fibonacci0(20) << endl; cout << "main <<< fibonacci0(25): " << fibonacci0(25) << endl; cout << "main <<< fibonacci0(30): " << fibonacci0(30) << endl; cout << "main <<< fibonacci0(35): " << fibonacci0(35) << endl; cout << "main <<< fibonacci0(40): " << fibonacci0(40) << endl; cout << "main <<< fibonacci0(45): " << fibonacci0(45) << endl; // **** end timer **** auto end = chrono::steady_clock::now(); // **** compute and display elapsed time in seconds **** chrono::duration<double> elapsed = end - start; cout << "main <<< elapsed: " << elapsed << endl << endl; // **** initialize memoization array **** memset(memo, 0, sizeof(memo) / sizeof(long)); // **** start timer **** start = chrono::steady_clock::now(); // **** calculate the fibonacci values of 0 through 10 **** for (int counter{ 0 }; counter <= 10; ++counter) cout << "main <<< fibonacci(" << counter << "): " << fibonacci(counter) << endl; cout << endl; // **** display higher fibonacci values **** cout << "main <<< fibonacci(20): " << fibonacci(20) << endl; cout << "main <<< fibonacci(25): " << fibonacci(25) << endl; cout << "main <<< fibonacci(30): " << fibonacci(30) << endl; cout << "main <<< fibonacci(35): " << fibonacci(35) << endl; cout << "main <<< fibonacci(40): " << fibonacci(40) << endl; cout << "main <<< fibonacci(45): " << fibonacci(45) << endl; // **** end timer **** end = chrono::steady_clock::now(); // **** compute and display elapsed time in seconds **** elapsed = end - start; cout << "main <<< elapsed: " << elapsed << endl; }

The test scaffold starts by getting the start time.

It is followed by a loop that calls the `fibonacci0` function for some small values. It is followed by the generation of larger Fibonacci numbers.

When all is done the time in seconds is displayed.

For the second part of the test we initialize the `memo` array which will be used to implement memoization on the `fibonacci` recursive function.

We get the start time and make the same set of calls we did for the `fibonacci0` function but now we use `fibonacci`.

We collect the `end` time and display the difference.

When timing functions it is not a good idea to do so over functions that display data. Such functions tend to be slow and add to the time being measured. After you run the test code or just look at the screen capture when the test code ran, time measurements illustrate the performance difference between the two calls of interest.

main <<< fibonacci0(0): 0 main <<< fibonacci0(1): 1 main <<< fibonacci0(2): 1 main <<< fibonacci0(3): 2 main <<< fibonacci0(4): 3 main <<< fibonacci0(5): 5 main <<< fibonacci0(6): 8 main <<< fibonacci0(7): 13 main <<< fibonacci0(8): 21 main <<< fibonacci0(9): 34 main <<< fibonacci0(10): 55 main <<< fibonacci0(20): 6765 main <<< fibonacci0(25): 75025 main <<< fibonacci0(30): 832040 main <<< fibonacci0(35): 9227465 main <<< fibonacci0(40): 102334155 main <<< fibonacci0(45): 1134903170 main <<< elapsed: 16.0337s main <<< fibonacci(0): 0 main <<< fibonacci(1): 1 main <<< fibonacci(2): 1 main <<< fibonacci(3): 2 main <<< fibonacci(4): 3 main <<< fibonacci(5): 5 main <<< fibonacci(6): 8 main <<< fibonacci(7): 13 main <<< fibonacci(8): 21 main <<< fibonacci(9): 34 main <<< fibonacci(10): 55 main <<< fibonacci(20): 6765 main <<< fibonacci(25): 75025 main <<< fibonacci(30): 832040 main <<< fibonacci(35): 9227465 main <<< fibonacci(40): 102334155 main <<< fibonacci(45): 1134903170 main <<< elapsed: 0.0038031s

The first set of functions took about **16 seconds**. The second set, using the same data, took about **4 milliseconds**.

/* * Recursive function finonacci NOT using memoization. */ long fibonacci0(long number) { // **** base case **** if ((number == 0) || (number == 1)) return number; // **** return fibonacci number **** return fibonacci0(number - 2) + fibonacci0(number - 1); }

We start by checking on the base condition. Since `number` can start at any value and Fibonacci numbers are defined as the sum of the **two previous Fibonacci numbers**, sooner or later we will hit 1 and 0. These two values are defined respectively to be 1 and 0.

If the base case has not been reached yet, then recursively we can compute the current fibonacci number fibonacci(n) using the two previous ones, which are defined as fibonacci(n – 1) and fibonacci(n – 2).

n Fibonacci - --------- 0 0 (defined) 1 1 (defined) 2 0 + 1 = 1 (computed) 3 1 + 1 = 2 (computed) 4 1 + 2 = 3 (computed) :::::::::::::::::::::::: n F(n - 1) + f(n - 2)

For numbers n = 0, 1, 2, 3, 4 we have Fibonacci = 0, 1, 1, 2, 3.

The previous implementation of the function of interest uses two stacks of calls that need to go back to 0. That takes a lot of space and we are computing values over and over. Perhaps we could do better.

/* * Recursive function fibonacci using memoization. */ long fibonacci(long number) { // **** base case **** if ((number == 0) || (number == 1)) { memo[number] = number; return memo[number]; } // **** initialization **** long fib = 0; // **** recursive step for number - 2 **** if (memo[number - 2] != 0) fib = memo[number - 2]; else { memo[number - 2] = fibonacci(number - 2); fib = memo[number - 2]; } // **** recursive step for number - 1 **** if (memo[number - 1] != 0) fib += memo[number - 1]; else { memo[number - 1] = fibonacci(number - 1); fib += memo[number - 1]; } // **** return fibonacci number **** return fib; }

We start with a base case. Note that we first populate the associated values in the `memo` array and then return the value from the `memo` array.

If we are not processing a base case, then we calculate the value for fibonacci(n – 2). If the associated value is already in the `memo` array, we just use it. The same holds for fibonacci(n – 1).

When done, we return the value in the `fib` variable.

Note that during the set of tests, we only need to compute once each value in the `memo` array.

We did not cover the obvious approach which would be to populate the `memo` array in O(n) and then serve the calls directly from the `memo` array in O(1) time. Of course, the range of `n` would have to be known in advance.

In addition, as you can see, the size of the Fibonacci numbers is growing quite fast. We would have to use `long long` and if that is not enough `BigNumber` to avoid running into overflow situations.

Hope you enjoyed this exercise as much as I did. The entire code for this project can be found in my GitHub repository named FibonacciNumbers.

Thanks for reading this post and feel free to connect with me John Canessa on LinkedIn.

Enjoy;

John

]]>Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. Note: A leaf is a node with no children. Constraints: o The number of nodes in the tree is in the range [0, 10^5]. o -1000 <= Node.val <= 1000 Related Topics: o Tree * Depth-First Search * Breadth-First Search o Binary Tree

We are given the root of a binary tree and are asked to find the **minimum **depth of the tree. A couple years ago we solved in this post Maximum Depth of a Binary Tree.

The definition of minimum depth is provided in the requirements for the problem at hand.

What called my attention to the problem was a comment that I read regarding the Minimum Depth of Binary Tree. It gave the impression that LeetCode had an issue with the solution on their site. It turned out that the author was calling attention to a YouTube video.

My approach was to read the problem at LeetCode, solve it and then watch the video to find out what the problem was about. I guess I fell for it. There was no problem.

/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public int minDepth(TreeNode root) { } }

Since we are going to solve the problem in Java, we need to use the signature for the function of interest. We are just given the root of a binary tree and we need to return the minimum depth.

LeetCode provides us with a description of the TreeNode class which is used to generate the nodes in the binary tree.

Since I will be solving the problem on my computer, I will need to develop a simple (perhaps not that simple) test scaffold which will read the input data for the binary tree, generate the tree, call the function of interest and display the result.

3,9,20,null,null,15,7 main <<< strArr: [3, 9, 20, null, null, 15, 7] main <<< arr: [3, 9, 20, null, null, 15, 7] main <<< inOrder: 9 3 15 20 7 main <<< postOrder: 9 15 7 20 3 main <<< levelOrder: 3 9,20 15,7 main <<< maxDepth: 3 main <<< minDepth: 2 2,null,3,null,4,null,5,null,6 main <<< strArr: [2, null, 3, null, 4, null, 5, null, 6] main <<< arr: [2, null, 3, null, 4, null, 5, null, 6] main <<< inOrder: 2 3 4 5 6 main <<< postOrder: 6 5 4 3 2 main <<< levelOrder: 2 3 4 5 6 main <<< maxDepth: 5 main <<< minDepth: 5 0 main <<< strArr: [0] main <<< arr: [0] main <<< inOrder: 0 main <<< postOrder: 0 main <<< levelOrder: 0 main <<< maxDepth: 1 main <<< minDepth: 1

Each test case is separated from others by two blank lines.

The first and only input line holds the values for the nodes in a depth first search mode.

Our test scaffold seems to read the input line into a String[] array. With that data a Integer[] array `arr` is populated. That array is then used to build the binary tree.

Once the binary tree has been populated, we seem to perform an in-order, a post-order, and a level-order traversal. This is done to make sure all is well and to provide us some insights as to how we could approach a solution.

In addition, it seems we compute the **maximum depth** of the binary tree before we call the function of interest and display the **minimum depth** of the tree.

Please note that the test scaffold **IS NOT PART OF THE SOLUTION**. The simplest approach is to use the on-line IDE provided by LeetCode.

/** * Test scaffold. * !!!! NOT PART OF THE SOLUTION !!!! * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read and populate string[] for binary tree **** String[] strArr = br.readLine().trim().split(","); // ***** close the buffered reader **** br.close(); // ????? ???? System.out.println("main <<< strArr: " + Arrays.toString(strArr)); // **** generate Integer[] to create and populate binary tree **** int len = strArr.length; Integer[] arr = new Integer[len]; for (int i = 0; i < len; i++) arr[i] = strArr[i].equals("null") ? null : Integer.parseInt(strArr[i]); // ????? ???? System.out.println("main <<< arr: " + Arrays.toString(arr)); // **** create and populate per level the binary tree **** BST bt = new BST(); bt.root = bt.populate(arr); // ???? in-order traversal ???? System.out.println("main <<< inOrder: " + bt.inOrder()); // ???? post-order traversal ???? System.out.println("main <<< postOrder: " + bt.postOrder()); // ???? level-order traversal ???? System.out.println("main <<< levelOrder: " + bt.levelOrder()); // ???? ???? System.out.println("main <<< maxDepth: " + bt.maxDepth(bt.root)); // **** call the function of interest and display result **** System.out.println("main <<< minDepth: " + minDepth(bt.root)); }

I am not going to add much to the description of the test scaffold code. Based on the description of a test case, you should be able to get a good understanding of what is going on.

/** * Given a binary tree, find its minimum depth. * Entry point for recursive calls. * * Runtime: 6 ms, faster than 53.62% of Java online submissions. * Memory Usage: 62.3 MB, less than 62.12% of Java online submissions. * * 52 / 52 test cases passed. * Status: Accepted * Runtime: 6 ms * Memory Usage: 62.3 MB * * Execution: O(log(n)) or O(n) - Space: O(1) */ static public int minDepth(TreeNode root) { // **** sanity check(s) **** if (root == null) return 0; if (root.left == null && root.right == null) return 1; // **** initialization **** var leftDepth = Integer.MAX_VALUE; var rightDepth = Integer.MAX_VALUE; // **** traverse left - O(log(n)) **** if (root.left != null) leftDepth = minDepth(root.left, 1); // **** traverse right - O(log(n)) **** if (root.right != null) rightDepth = minDepth(root.right, 1); // **** return min depth **** // return Math.min(leftDepth, rightDepth); return leftDepth >= rightDepth ? rightDepth : leftDepth; }

This function is the **entry point** for a recursive method we will see shortly.

We start by performing a couple sanity checks.

The `leftDepth` is used to get the depth of the left child of the root of the binary tree. The `rightDepth` is for the left child of the binary tree.

Each time we call the `minDepth` recursive call, we increase the depth by one. In general the depth of the root node is considered to be at level 1.

/** * Recursive call. * * Execution: O(log(n)) or O(n) - Space: O(1) */ static private int minDepth(TreeNode root, int depth) { // **** base case **** if (root.left == null && root.right == null) return depth + 1; // **** initialization **** var leftDepth = Integer.MAX_VALUE; var rightDepth = Integer.MAX_VALUE; // **** traverse left - O(log(n)) *** if (root.left != null) leftDepth = minDepth(root.left, depth + 1); // **** traverse right - O(log(n)) **** if (root.right != null) rightDepth = minDepth(root.right, depth + 1); // **** return min depth **** // return Math.min(leftDepth, rightDepth); return leftDepth >= rightDepth ? rightDepth : leftDepth; }

The recursive call starts with the base case in which we increment the depth by one and return it to the caller.

We initialize a couple variables that are used to track the depths of the two child nodes.

The recursive calls are made and the minimum value is returned.

Please take a look at the comment section for the entry point function. It contains performance information.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named MinimumDepthOfBinaryTree.

Please note that the code here presented might not be the best possible solution. After solving a problem, you should take a look at the best accepted solutions provided by the different websites (i.e., HackerRank, LeetCode to name a few). The idea is to learn concepts, not to memorize solutions.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge and enhance your developer / engineering toolset.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.

Enjoy;

John

]]>The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value and the median is the mean of the two middle values. For example, for arr = [2,3,4], the median is 3. For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5. Implement the MedianFinder class: o MedianFinder() initializes the MedianFinder object. o void addNum(int num) adds the integer num from the data stream to the data structure. o double findMedian() returns the median of all elements so far. Answers within 10-5 of the actual answer will be accepted. Constraints: o -10^5 <= num <= 10^5 o There will be at least one element in the data structure before calling findMedian. o At most 5 * 10^4 calls will be made to addNum and findMedian. Related Topics: o Two Pointers o Design o Sorting * Heap (Priority Queue) o Data Stream

In a nutshell the class needs to process integers in the specified range and at different points we can be asked to return the current Median. As a brute force approach, I tried sorting. I did not submit such an approach because it is quite expensive (slow) and the problem is rated **Hard **by LeetCode.

In the next approach I tried to use a priority queue and streams. We will take a look at the first implementation which **times out **so it was **not accepted** by LeetCode. The reason for including it in the post is to understand what needs to be done and the need to optimize it.

I will be solving the problem on my computer. My suggestion, unless you have some specific needs, is to solve the problem using the online IDE provided by LeetCode. Since I will solve the problem on my computer, I needed to develop a simple test scaffold to read the input data, populate the necessary data structures and variables, call the different methods in the class of interest, collect the results and display them. Please note that the test scaffold **IS NOT PART OF THE SOLUTION!**

class MedianFinder { public MedianFinder() { } public void addNum(int num) { } public double findMedian() { } } /** * Your MedianFinder object will be instantiated and called as such: * MedianFinder obj = new MedianFinder(); * obj.addNum(num); * double param_2 = obj.findMedian(); */

We need to populate the MedianFinder class with the class elements and the three required methods. The first one is the constructor and initializes the data structures we will use. The second method adds an integer to the class. The `findMedian` method is used to compute and return the median.

Note that LeetCode describes in the comments section of the class signature how our solution will be tested.

MedianFinder, addNum, addNum, findMedian, addNum, findMedian null, 1, 2, null, 3, null main <<< n: 6 main <<< cmdArr: [MedianFinder, addNum, addNum, findMedian, addNum, findMedian] main <<< argArr: [null, 1, 2, null, 3, null] main <<< m : 6 main <<< pq2 (head): [3, 1, 2] main <<< pq1 (head): [4, 5, 6] main <<< obj.pq2.peek: 3 main <<< obj.pq1.peek: 4 main <<< median: 3.5 main <<< m : 7 main <<< pq2 (head): [3, 1, 2] main <<< pq1 (head): [4, 5, 6, 7] main <<< obj.pq2.peek: 3 main <<< obj.pq1.peek: 4 main <<< median: 4 main <<< output: [null, null, null, 1.5, null, 2.0] MedianFinder, addNum, addNum, addNum, addNum, addNum, addNum, findMedian, addNum, findMedian null, 1, 2, 3, 4, 5, 6, null, 7, null main <<< n: 10 main <<< cmdArr: [MedianFinder, addNum, addNum, addNum, addNum, addNum, addNum, findMedian, addNum, findMedian] main <<< argArr: [null, 1, 2, 3, 4, 5, 6, null, 7, null] main <<< m : 6 main <<< pq2 (head): [3, 1, 2] main <<< pq1 (head): [4, 5, 6] main <<< obj.pq2.peek: 3 main <<< obj.pq1.peek: 4 main <<< median: 3.5 main <<< m : 7 main <<< pq2 (head): [3, 1, 2] main <<< pq1 (head): [4, 5, 6, 7] main <<< obj.pq2.peek: 3 main <<< obj.pq1.peek: 4 main <<< median: 4 main <<< output: [null, null, null, null, null, null, null, 3.5, null, 4.0]

Each test case is separated by two blank lines.

The first two lines in each test case represent the input for the test case.

The first input line contains the names of the methods that will be called by our test scaffold. The second line contains the associated argument that will be passed to the method in questions. A `null` indicates that there is no input argument for the specified method.

It seems that our test scaffold which **IS NOT PART OF THE SOLUTION**, reads the two input lines, populates some variables, and displays them to allow us to verify that all is well so far.

The next few lines before calling the methods of interest will make sense as we take a look at the test scaffold.

The test scaffold then calls the different methods passing the associated arguments as needed. After each method completes, the returned value is placed in the `output` Integer[] array. A `null` indicates the associated method did not return a value.

I spent time in the first implementation of the solution, which we should label as a **brute force **approach. The implementation **passed **the test case used as an example, and as expected timed out, so **it was not accepted** by LeetCode. We will take a brief look at the first implementation because it provided me with additional insights as to what might work.

Our test scaffold will not call the first implementation of the `MedianFinder` class.

/** * Test scaffold. * !!! NOT PART OF THE SOLUTION !!! * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read commands **** String[] cmdArr = Arrays.stream(br.readLine().trim().split(",")) .map(s -> s.trim()) .toArray(size -> new String[size]); // **** read arguments **** Integer[] argArr = Arrays.stream(br.readLine().trim().split(",")) .map(s -> s.trim()) .map(s -> s.equals("null") ? null : Integer.parseInt(s) ) .toArray(size -> new Integer[size]); // **** close buffered reader **** br.close(); // **** get number of commands **** var n = cmdArr.length; // ???? ???? System.out.println("main <<< n: " + n); System.out.println("main <<< cmdArr: " + Arrays.toString(cmdArr)); System.out.println("main <<< argArr: " + Arrays.toString(argArr)); // **** holds output from method calls **** Double[] output = new Double[n]; // **** median finder object **** // MedianFinder0 obj = null; MedianFinder obj = null; // ???? create object ???? obj = new MedianFinder(); // ???? populate priority queues ???? obj.addToPqs(6); // ???? display the number of elements in the priority queues ???? System.out.println("main <<< m : " + (obj.pq2.size() + obj.pq1.size())); // ???? display priority queues contents ???? System.out.println("main <<< pq2 (head): " + obj.pq2.toString()); System.out.println("main <<< pq1 (head): " + obj.pq1.toString()); // ???? display head nodes ???? System.out.println("main <<< obj.pq2.peek: " + obj.pq2.peek()); System.out.println("main <<< obj.pq1.peek: " + obj.pq1.peek()); // ???? compute median ???? System.out.println("main <<< median: " + (obj.pq2.peek() + obj.pq1.peek()) / 2.0); // ???? populate priority queues ???? obj.addToPqs(7); // ???? display the number of elements in the priority queues ???? System.out.println("main <<< m : " + (obj.pq2.size() + obj.pq1.size())); // ???? display priority queues contents ???? System.out.println("main <<< pq2 (head): " + obj.pq2.toString()); System.out.println("main <<< pq1 (head): " + obj.pq1.toString()); // ???? display head nodes ???? System.out.println("main <<< obj.pq2.peek: " + obj.pq2.peek()); System.out.println("main <<< obj.pq1.peek: " + obj.pq1.peek()); // ???? compute median ???? System.out.println("main <<< median: " + (obj.pq2.size() >= obj.pq1.size() ? obj.pq2.peek() : obj.pq1.peek())); // **** loop calling methods **** for (var i = 0; i < n; i++) { switch (cmdArr[i]) { case "MedianFinder": // obj = new MedianFinder0(); obj = new MedianFinder(); break; case "addNum": obj.addNum(argArr[i]); break; case "findMedian": output[i] = obj.findMedian(); break; default: System.err.println("main <<< UNEXPECTED cmdArr[" + i + "] ===>" + cmdArr[i] + "<=="); break; } } // **** display output **** System.out.println("main <<< output: " + Arrays.toString(output)); }

Our test code populates two arrays with the names of the methods to be called and the second with the Integer[] arguments to be passed to each method if needed. The contents of the arrays are displayed.

We then declare the `output` Double[] array which will hold the results of each method we call. A `null` is associated with methods that do not return a value.

Next we declare the `obj` object which will hold the object associated with the `MedianFinder` class after we call the constructor.

The following set of lines with `// ???? ????` comments were generated after the first implementation of the class of interest. We will return to them and the rest of the code after we take a look at the **failed implementation**.

/** * */ static class MedianFinder0 { /** * Class members. */ public int n; public PriorityQueue<Integer> pq = null; // public List<Integer> pq = null; // **** more to follow ... }

The `MedianFinder0` class uses a priority queue `pq` and an integer `n` to hold the number of elements. You can also see a commented out `pq` List<Integer> which I experimented with but did not pay off.

/** * Initializes the MedianFinder object. */ public MedianFinder0() { // pq = new PriorityQueue<Integer>((a, b) -> { // if (a < b) return -1; // else if (a == b) return 0; // else return 1; // }); // pq = new ArrayList<>(); pq = new PriorityQueue<>(); }

As you can see in the constructor, I tried ascending and descending implementations of the `pq`.

/** * Adds the integer num from the data stream to the data structure. * @param num */ public void addNum(int num) { pq.add(num); n++; }

Adding a number to the list is done by just adding the `num` to the priority queue. The `n` is actually not needed because we may use the `pq.size()` method to get the number of elements in the priority queue.

/** * Returns the median of all elements so far. * @return */ public double findMedian() { // **** size of pq **** // var n = pq.size(); // **** sanity checks **** if (n == 1) return pq.peek(); // if (n == 1) return pq.get(0); // **** **** Integer[] mid = pq.stream() // .sorted() .skip(n / 2 - 1) .limit(2) .toArray(size -> new Integer[size]); // **** return element at n / 2 **** if (n % 2 == 1) return mid[1]; // **** sum elements at n / 2 - 1 and n / 2 and divide them by 2 **** else return (mid[0] + mid[1]) / 2.0; }

The `findMedian` method returns the median of the numbers in the priority queue. After several attempts, I decided to call it quits after using the stream approach depicted in this implementation.

Note that the execution time is O(n / 2) which is quite slow depending on the size of the priority queue.

The way we generate the median is correct. We just need to find a better (more efficient method) to get to the midpoint of the data structure(s) holding the integer elements we have been provided so far.

My notes **** o PriorityQueue pq2 o PriorityQueue pq1 * All elements in the pq2 <= all elements in the pq1 * PriorityQueue sizes `equal` OR `off by one` pq2 pq1 1,2,3 4,5,6 = = median: (3 + 4) / 2 = 3.5 Performance **** o Add to PriorityQueue: O(log(n)) o Remove from PriorityQueue: O(log(n)) o Find element in PriorityQueue: O(1) Median computation **** o If pq2.size() == pq1.size() median: (pq2.peek() + pq1.peek()) / 2 o If pq2.size() > pq1.size() median: pq2.peek() o If pq2.size() < pq1.size() median: pq1.peek()

After failed attempts and additional reading in which I expressed and collected notes I moved on to the next implementation of the class of interest.

We will use two priority queues.

The `pq2` will hold all the integers that are **<=** than the ones in `pq1`. The reason for this is to keep the elements whose values are less than the median in one priority queue, and the elements greater than the median on a second one.

The other condition is to **keep the sizes of both priority queues about the same**. Remember that the median in a set of odd numbers of values is found in the midpoint of a list and the median in an even set is found using the two elements in the midpoint of the list.

Now we should be able to add elements in O(log(n)) as we did in the previous implementation, but also compute the median in order O(1) which is much more efficient than the O(n) we had in the previous class implementation.

// ???? create object ???? obj = new MedianFinder(); // ???? populate priority queues ???? obj.addToPqs(6); // ???? display the number of elements in the priority queues ???? System.out.println("main <<< m : " + (obj.pq2.size() + obj.pq1.size())); // ???? display priority queues contents ???? System.out.println("main <<< pq2 (head): " + obj.pq2.toString()); System.out.println("main <<< pq1 (head): " + obj.pq1.toString()); // ???? display head nodes ???? System.out.println("main <<< obj.pq2.peek: " + obj.pq2.peek()); System.out.println("main <<< obj.pq1.peek: " + obj.pq1.peek()); // ???? compute median ???? System.out.println("main <<< median: " + (obj.pq2.peek() + obj.pq1.peek()) / 2.0); // ???? populate priority queues ???? obj.addToPqs(7); // ???? display the number of elements in the priority queues ???? System.out.println("main <<< m : " + (obj.pq2.size() + obj.pq1.size())); // ???? display priority queues contents ???? System.out.println("main <<< pq2 (head): " + obj.pq2.toString()); System.out.println("main <<< pq1 (head): " + obj.pq1.toString()); // ???? display head nodes ???? System.out.println("main <<< obj.pq2.peek: " + obj.pq2.peek()); System.out.println("main <<< obj.pq1.peek: " + obj.pq1.peek()); // ???? compute median ???? System.out.println("main <<< median: " + (obj.pq2.size() >= obj.pq1.size() ? obj.pq2.peek() : obj.pq1.peek()));

This is the segment of code from the test scaffold that we were discussing earlier.

We create a MedianFinder object and point to it in the `obj` variable.

We make a call to the `addToPqs` method which fills two priority queues. This is being done to show how two priority queues could be used to help us get to the O(1) execution in computing the median.

In this case we just populate an even and then odd number of elements in the priority queues.

Please go back to the screen capture of the execution of the test cases to follow what is happening when we fill the priority queues.

/** * Runtime: 144 ms, faster than 57.14% of Java online submissions. * Memory Usage: 125.1 MB, less than 7.60% of Java online submissions. * * 21 / 21 test cases passed. * Status: Accepted * Runtime: 144 ms * Memory Usage: 125.1 MB */ static class MedianFinder { /** * Class members. */ public PriorityQueue<Integer> pq1 = null; public PriorityQueue<Integer> pq2 = null; // **** more to follow ... }

We have two priority queues as the class members in `MedianFinder`.

/** * Initialize the MedianFinder object. */ public MedianFinder() { // **** descending **** pq2 = new PriorityQueue<>((a, b) -> { if (a > b) return -1; if (a == b) return 0; return 1; }); // **** ascending order **** pq1 = new PriorityQueue<>(); }

Note that `pq2` keeps its elements in descending order, while `pq1` keeps them in ascending order. This will facilitate accessing the `head` elements from both queues in the needed \ required order. In a priority queue we can access the `head` element in O(1) time.

/** * Add the integer num from the data stream to the data structure. * * Execution: O(log(n)) - Space: O(1) */ public void addNum(int num) { // **** add num to pq2 (by default) **** pq2.add(num); // **** pq2 size >= pq1 size **** if (pq2.size() >= pq1.size()) { // **** sizes different by no more than one OR // pq2 > pq1 head **** if ((pq2.size() > pq1.size() + 1) || (!pq2.isEmpty() && !pq1.isEmpty() && pq2.peek() > pq1.peek())) { // **** remove from pq2 **** var e = pq2.poll(); // **** add to pq1 **** if (e != null) pq1.add(e); // **** move from pq1 head to pq2 **** if (pq2.size() + 1 < pq1.size()) { // **** remove from pq1 **** e = pq1.poll(); // **** add to pq2 **** if (e != null) pq2.add(e); } } } // **** add to low heap **** else pq1.add(num); }

We start by adding the `num` into `pq2`.

We then check the sizes of the priority queues. We need to keep the lengths the same or no more than one off. If `pq2` is longer, we move the head element from `pq2` to the `pq1` priority queue.

Once we move the element we need to check that the largest element in `pq1` (happens to be at the head of the queue) be moved to `pq2`.

By the way, this code could be modified to eliminate the use of the `e` variable, and eliminate the check before inserting `e` into `pq1`.

If the size of `pq1` is smaller than the size of `pq2` we would insert `num` directly into `pq1`.

/** * Returns the median of all elements so far. * * Execution: O(1) - Space: O(1) */ public double findMedian() { // **** median in pq2 **** if (pq2.size() > pq1.size()) return pq2.peek(); // **** median in pq1 **** else if (pq2.size() < pq1.size()) return pq1.peek(); // **** compute median **** else return (pq2.peek() + pq1.peek()) / 2.0; }

This is the implementation of the `findMedian` method.

There are three cases each of which can be implemented in O(1) time.

/** * Experiment populating the priority queues. * * !!! NOT PART OF THE SOLUTION !!! */ public void addToPqs (int n) { // **** initialization **** pq1.clear(); pq2.clear(); var i = 0; // **** populate priority queue 2 **** for ( ; i < n / 2; i++) pq2.add(i + 1); // **** populate priority queue 1 **** for ( ; i < n; i++) pq1.add(i + 1); }

The `addToPqs` method is only used to experiment with the priority queues. This method **IS NOT PART OF THE SOLUTION**.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named FindMedianFromDataStream.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge and enhance your developer / engineering toolset.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.

Enjoy;

John

]]>You are given an unordered array of `unique integers` incrementing from 1. You can swap any two elements a limited number of times. Determine the largest lexicographical value array that can be created by executing `no more` than the limited number of swaps. Constraints o 1 <= n <= 10^5 o 1 <= k <= 10^9

We are given a list of unique integers incrementing from 1. We can swap two values at a time up to a number `k`. We need to return the largest possible permutation in the list.

Please take a few moments to read the requirements and make sure you understand them and what data is being provided.

public static List<Integer> largestPermutation(int k, List<Integer> arr) { }

The **signature **for the function of interest indicates that we are provided a list of integers `arr` (should have been named `lst` or something along those lines), and an int `k`. Our function needs to return a list as indicated in the requirements.

Since I am going to develop the code on my computer, I will need to generate a simple test scaffold which will read the input arguments, populate the necessary variables, call the function of interest, and display the result. Please note that the test scaffold **IS NOT PART OF THE SOLUTION**. The simplest approach is to use the online IDE provided by HackerRank.

5 1 4 2 3 5 1 main <<< n: 5 main <<< k: 1 main <<< arr: [4, 2, 3, 5, 1] main <<< largestPermutation0: [5, 2, 3, 4, 1] main <<< largestPermutation: [5, 2, 3, 4, 1] 3 1 2 1 3 main <<< n: 3 main <<< k: 1 main <<< arr: [2, 1, 3] main <<< largestPermutation0: [3, 1, 2] main <<< largestPermutation: [3, 1, 2] 2 1 2 1 main <<< n: 2 main <<< k: 1 main <<< arr: [2, 1] main <<< largestPermutation0: [2, 1] main <<< largestPermutation: [2, 1]

Each test case is separated by two blank lines from others.

The first two lines of each test case are input lines.

Our test code reads the lines, populates some variables and displays their contents. This is done to make sure all is well before the function of interest is called.

There are two implementations of the function of interest. The first missed to pass test case 15. The second implementation passed all test cases.

/** * Test scaffold * * !!! NOT PART OF THE SOLUTION !!! * * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read n and k **** int[] nk = Arrays.stream(br.readLine().trim().split(" ")) .map(s -> s.trim()) .mapToInt(Integer::parseInt) .toArray(); // **** for ease of use **** var n = nk[0]; var k = nk[1]; // **** read list<Integer> arr **** List<Integer> arr = Arrays.stream(br.readLine().trim().split(" ")) .map(Integer::parseInt) .collect(Collectors.toList()); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< n: " + n); System.out.println("main <<< k: " + k); System.out.println("main <<< arr: " + arr.toString()); // **** copy (deep) arr list **** List<Integer> cal = arr.stream() .collect(Collectors.toList()); // **** call function of interest and display result **** System.out.println("main <<< largestPermutation0: " + largestPermutation0(k, arr)); // **** restore arr list **** arr = cal.stream() .collect(Collectors.toList()); // **** call function of interest and display result **** System.out.println("main <<< largestPermutation: " + largestPermutation(k, arr)); }

The test scaffold seems to follow closely the description of the test cases. AT this time I have nothing else to add. That said; if you have comments or questions please do not hesitate and leave me a message below.

/** * Determine the largest lexicographical value array that can be created * by executing `no more` than the limited number of swaps. * * Timed out on test case 15 :o( */ public static List<Integer> largestPermutation0(int k, List<Integer> arr) { // **** initialization **** var n = arr.size(); // **** sanity checks **** if (n == 1) return arr; if (k >= n) { Collections.sort(arr, Collections.reverseOrder()); return arr; } // **** remaining largest value **** var largest = n; // **** loop swapping elements **** for (var c = 0; k > 0 && c < n - 1; c++) { // **** get index of largest element **** var l = arr.indexOf(largest--); // **** skip this swap **** if (c == l) continue; // ???? ???? // System.out.println("<<< swap c: " + c + " l: " + l); // **** swap elements **** Collections.swap(arr, l, c); // ???? ???? // System.out.println("<<< arr: " + arr.toString()); // **** count this permutation **** k--; } // **** return updated list **** return arr; }

This implementation passed all test cases except test case #15. It timed out.

The approach is relatively simple.

The main loop swaps elements counting the number of swaps by decrementing `k`.

Have not much to add at this time.

/** * Determine the largest lexicographical value array that can be created * by executing `no more` than the limited number of swaps. * * Passed all test cases :o) */ public static List<Integer> largestPermutation(int k, List<Integer> arr) { // **** initialization **** var n = arr.size(); // **** sanity checks **** if (n == 1) return arr; if (k >= n) { Collections.sort(arr, Collections.reverseOrder()); return arr; } // **** position of numbers in list **** int[] position = new int[n + 1]; for (var i = 0; i < arr.size(); i++) position[arr.get(i)] = i; // ???? ???? // System.out.println("<<< position: " + Arrays.toString(position)); // **** loop swapping list entries (as needed) **** for (int i = n; k > 0 && i > 0; --i) { // **** actual position of `i` **** var actualPos = position[i]; // **** expected position for `i` **** var expectedPos = n - i; // ???? ???? // System.out.println("<<< i: " + i + " in actualPos: " + actualPos + " expectedPos: " + expectedPos); // **** check if i'th value is in the expected place **** if (actualPos != expectedPos) { // **** swap list elements **** Collections.swap(arr, actualPos, expectedPos); // ???? ???? // System.out.println("<<< arr: " + arr.toString()); // **** update positions **** position[arr.get(actualPos)] = actualPos; position[arr.get(expectedPos)] = expectedPos; // ???? ???? // System.out.println("<<< position: " + Arrays.toString(position)); // **** account for this swap **** k--; } } // **** return modified list **** return arr; }

This implementation passed all test cases.

It starts by performing some sanity checks.

It then populates an int[] array `position` (perhaps I should have named it `positions`) which is used to track the positions of the integer values. We could have set the count to `n` but decided to use `n + 1` to eliminate the need to index values having to subtract 1.

The main loop starts with the integer with the highest value which in this case should be `n` given that the requirements call for values in the [1:n] range.

We then compute the actual and the expected positions of the value `i` in the list noting that the largest values should be on the left (or start) of the list `arr`.

If the positions do not match, we need to swap them to make them match our expectation.

We swap the values and then update the associated values in the `position` array.

Finally the variable `k` is decremented to account for the swap just completed.

When all is said and done the function of interest returns the `arr` list.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named LargestPermutation.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge and enhance your developer / engineering toolset.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.

Enjoy;

John

]]>Given an integer array nums, handle multiple queries of the following types: o Update the value of an element in nums. o Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. Implement the NumArray class: o NumArray(int[] nums) Initializes the object with the integer array nums. o void update(int index, int val) Updates the value of nums[index] to be val. o int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive (i.e. nums[left] + nums[left + 1] + ... + nums[right]). Constraints: o 1 <= nums.length <= 3 * 10^4 o -100 <= nums[i] <= 100 o 0 <= index < nums.length o -100 <= val <= 100 o 0 <= left <= right < nums.length o At most 3 * 10^4 calls will be made to update and sumRange. Related Topics: * Array o Design o Binary Indexed Tree * Segment Tree

We are given an int[] and asked to perform two operations as part of a class.

The simplest approach is to compute the sum of all integers from left to right and keep them in an int[] array `sums`. That takes O(n) time and O(n) space. When a query for the sum at a specific index is made, we are able to respond by looking into the int[] `sums` in O(1) time. Updating a value at a specified index forces us to add to all the values in `sums` the delta between the original value and the current. This operation takes O(n) time.

It seems that the best suggestion would be to use a segment tree. To experiment with such data structure I found Segment Tree | Set 1 (Sum of given range) and Segment tree | Efficient implementation articles with associate code in the GeeksforGeeks website.

For a set of nice diagrams / illustrations for the Segment Tree implementation I found happygirlzt/algorithm-illustrations on GitHub.

A segment tree, also known as a statistic tree, is a tree data structure used for storing information about intervals, or segments. It allows querying which of the stored segments contain a given point.

In addition to meet the requirements for the problem in question, the required operations take O(log(n)) each.

class NumArray { public NumArray(int[] nums) { } public void update(int index, int val) { } public int sumRange(int left, int right) { } } /** * Your NumArray object will be instantiated and called as such: * NumArray obj = new NumArray(nums); * obj.update(index,val); * int param_2 = obj.sumRange(left,right); */

The requirements boil to completing the three methods in the signature for the NumArray class.

NumArray, sumRange, update, sumRange 1, 3, 5 0, 2 1, 2 0, 2 main <<< n: 3 main <<< m: 4 main <<< cmdArr: [NumArray, sumRange, update, sumRange] main <<< argArr: [1, 3, 5] [0, 2] [1, 2] [0, 2] main <<< root: (sum: 9 [0 : 2]) main <<< preOrder: (sum: 9 [0 : 2]) (sum: 4 [0 : 1]) (sum: 1 [0 : 0]) (sum: 3 [1 : 1]) (sum: 5 [2 : 2]) main <<< levelOrder: (sum: 9 [0 : 2]) (sum: 4 [0 : 1]) (sum: 5 [2 : 2]) (sum: 1 [0 : 0]) (sum: 3 [1 : 1]) main <<< output: [null, 9, null, 8] NumArray 1,3,5,7,9,11 main <<< n: 6 main <<< m: 1 main <<< cmdArr: [NumArray] main <<< argArr: [1, 3, 5, 7, 9, 11] main <<< root: (sum: 36 [0 : 5]) main <<< preOrder: (sum: 36 [0 : 5]) (sum: 9 [0 : 2]) (sum: 4 [0 : 1]) (sum: 1 [0 : 0]) (sum: 3 [1 : 1]) (sum: 5 [2 : 2]) (sum: 27 [3 : 5]) (sum: 16 [3 : 4]) (sum: 7 [3 : 3]) (sum: 9 [4 : 4]) (sum: 11 [5 : 5]) main <<< levelOrder: (sum: 36 [0 : 5]) (sum: 9 [0 : 2]) (sum: 27 [3 : 5]) (sum: 4 [0 : 1]) (sum: 5 [2 : 2]) (sum: 16 [3 : 4]) (sum: 11 [5 : 5]) (sum: 1 [0 : 0]) (sum: 3 [1 : 1]) (sum: 7 [3 : 3]) (sum: 9 [4 : 4]) main <<< output: [null]

Since I am not developing the solution using the online IDE provided by LeetCode, I have to develop a simple test scaffold to read and populate the input `nums` int[] array, the different commands and associated argument,, call the methods of interest passing the associated arguments, and displaying the results. The test scaffold **IS NOT PART OF THE SOLUTION**.

Each test case is separated from the next by two blank lines.

The first line holds the list of commands to execute, followed by the associated arguments for each command on separate lines.

After the input lines are parsed the collected values are displayed to allow us to verify that all is well before calling the methods of interest.

The last line on each test case displays the output associated with each method invocation. Note that the number of commands in the first input line matches the number of results in the `output` Integer[] array.

(sum: 36 [0 : 5]) / \ / \ / \ / \ (sum: 9 [0 : 2]) (sum: 27 [3 : 5]) / \ / \ / \ / \ / \ / \ / \ / \ (sum: 4 [0 : 1]) (sum: 5 [2 : 2]) (sum: 16 [3 : 4]) (sum: 11 [5 : 5]) / \ / \ / \ / \ / \ / \ / \ / \ (sum: 1 [0 : 0]) (sum: 3 [1 : 1]) (sum: 7 [3 : 3]) (sum: 9 [4 : 4])

This diagram mimics one of the diagrams in the happygirlzt github repository. I created it using the output generated by the test scaffold.

/** * Test scaffold * @throws IOException * * !!! NOT PART OF THE SOLUTION !!! */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read cmdArr **** String[] cmdArr = Arrays.stream(br.readLine().trim().split(",")) .map(cmd -> cmd.trim()) .toArray(size -> new String[size]); // **** for ease of use **** int m = cmdArr.length; // **** declare argArr **** int[][] argArr = new int[m][]; // **** populate argArr **** for (int i = 0; i < m; i++) argArr[i] = Arrays.stream(br.readLine().trim().split(",")) .map(cmd -> cmd.trim()) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // **** compute n **** int n = argArr[0].length; // ???? ???? System.out.println("main <<< n: " + n); System.out.println("main <<< m: " + m); System.out.println("main <<< cmdArr: " + Arrays.toString(cmdArr)); System.out.println("main <<< argArr: "); for (int i = 0; i < argArr.length; i++) System.out.println(Arrays.toString(argArr[i])); // **** holds output **** Integer[] output = new Integer[m]; // **** for starters **** NumArray numArray = null; // **** loop calling class methods **** for (int i = 0; i < m; i++) { switch (cmdArr[i]) { case "NumArray": numArray = new NumArray(argArr[i]); // ???? ???? System.out.println("main <<< root: " + numArray.root.toString()); System.out.println("main <<< preOrder: "); System.out.print(numArray.preOrder(numArray.root)); System.out.println("main <<< levelOrder:"); System.out.println(numArray.levelOrder(numArray.root)); break; case "sumRange": output[i] = numArray.sumRange(argArr[i][0], argArr[i][1]); break; case "update": numArray.update(argArr[i][0], argArr[i][1]); break; default: System.out.println("main <<< UNEXPECTED cmdArr[" + i + "] ==>" + cmdArr[i] + "<=="); break; } } // **** display output **** System.out.println("main <<< output: " + Arrays.toString(output)); }

The test scaffold starts by reading the input lines. The associated variables are then displayed.

The `numArray` variable is created. It will hold the `root` to the Segment Tree.

The following loop calls each command associated with the respective arguments. The results of each method call is stored in the `output` Integer[] array. The array is displayed after processing all method calls.

/** * */ static class TreeNode { /** * Class members. */ public int sum; // sum in this range public int start; // start index public int end; // end index public TreeNode left; public TreeNode right; /** * Constructor. */ public TreeNode(int s, int e) { this.start = s; this.end = e; } /** * String representation. */ @Override public String toString() { return String.format("(sum: %2d [%d : %d])", sum, start, end); } }

The TreeNode class is used to implement the segment tree. We declare the class members and a constructor. I added the `toString` method to experiment and display the TreeNode data structure. The `toString` method **IS NOT PART OF THE SOLUTION**.

/** * Runtime: 98 ms, faster than 52.21% of Java online submissions. * Memory Usage: 68.4 MB, less than 98.99% of Java online submissions. * * 15 / 15 test cases passed. * Status: Accepted * Runtime: 98 ms * Memory Usage: 68.4 MB */ static class NumArray { /** * Class members. */ TreeNode root = null; // other methods to follow ... }

In the comments section of the NumArray class one can find the performance information for the different test cases executed by LeetCode when I submit the code for evaluation.

The `root` variable holds the root for the segment tree. The methods that follow will be discussed as we see their implementation.

/** * Initializes the object with the integer array nums. */ public NumArray(int[] nums) { root = buildTree(nums, 0, nums.length - 1); }

The constructor for the `NumArray` class builds the segment tree and returns the root of the tree which is assigned to the `root` variable.

/** * Build the tree. * Recursive call. */ private TreeNode buildTree(int[] nums, int start, int end) { // **** base case **** if (start > end) return null; // **** initialization **** TreeNode res = new TreeNode(start, end); // **** leaf node **** if (start == end) { res.sum = nums[start]; } // **** not a leaf node **** else { // **** compute mid point in range **** var mid = start + (end - start) / 2; // **** generate left child subtree **** res.left = buildTree(nums, start, mid); // **** generate right child subtree **** res.right = buildTree(nums, mid + 1, end); // **** update sum **** res.sum = res.left.sum + res.right.sum; } // **** **** return res; }

This recursive method is used by the constructor to populate the nodes in the segment tree.

/** * Recursive call. */ private void update(TreeNode root, int i, int val) { // **** leaf node **** if (root.start == root.end) { root.sum = val; } else { // **** **** var mid = root.start + (root.end - root.start) / 2; // **** go left **** if (i <= mid) { update(root.left, i, val); } // **** go right **** else { update(root.right, i, val); } // **** update sum **** root.sum = root.left.sum + root.right.sum; } }

The `update` private method is used to process an update to the value `val` in the segment tree at the specified index `i`.

/** * Updates the value of nums[index] to be val. * Recursion entry point. */ public void update(int i, int val) { update(root, i, val); }

This is the entry point for the `update` method.

/** * */ private int query(TreeNode root, int i, int j) { // **** **** if (root.start == i && root.end == j) { return root.sum; } // **** **** else { // **** compute mid value **** var mid = root.start + (root.end - root.start) / 2; // **** **** if (j <= mid) { return query(root.left, i, j); } // **** **** else if (i >= mid + 1) { return query(root.right, i , j); } // **** **** else { return query(root.left, i, mid) + query(root.right, mid + 1, j); } } }

The `query` private method is used to query the segment tree for a value within the specified range.

/** * Returns the sum of the elements of nums * between indices left and right inclusive * (i.e. nums[left] + nums[left + 1] + ... + nums[right]). */ public int sumRange(int i, int j) { return query(root, i, j); }

The `sumRange` method returns the sum of the values within the specified range. It does so by calling the recursive private method with the same name which we looked at previously.

/** * In-order traversal. * Entry function. * * !!! NOT PART OF THE SOLUTION !!! */ public String inOrder(TreeNode root) { // **** initialization **** StringBuilder sb = new StringBuilder(); // **** populate string builder **** inOrder(root, sb); // **** return string **** return sb.toString(); } /** * In-order traversal. * Helper function. * * !!! NOT PART OF THE SOLUTION !!! */ private void inOrder(TreeNode root, StringBuilder sb) { // **** base case **** if (root == null) return; // **** traverse left **** inOrder(root.left, sb); // **** add node to string builder **** sb.append(root.toString() + "\n"); // **** traverse right **** inOrder(root.right, sb); }

These methods are used to perform an in-order traversal of the segment tree. I used them to debug and experiment with the code. They **ARE ****NOT PART OF THE SOLUTION**.

/** * Post-order traversal. * Entry function. * * !!! NOT PART OF THE SOLUTION !!! */ public String postOrder(TreeNode root) { // **** initialization **** StringBuilder sb = new StringBuilder(); // **** populate string builder **** postOrder(root, sb); // **** return string **** return sb.toString(); } /** * Post-order traversal. * Helper function. * * !!! NOT PART OF THE SOLUTION !!! */ private void postOrder(TreeNode root, StringBuilder sb) { // **** base case **** if (root == null) return; // **** traverse left **** postOrder(root.left, sb); // **** traverse right **** postOrder(root.right, sb); // **** add node to string builder **** sb.append(root.toString() + "\n"); }

These methods are used to perform a post-order traversal of the segment tree. I used them to debug and experiment with the code. They **ARE ****NOT PART OF THE SOLUTION.**

/** * Pre-order traversal. * Entry function. * * !!! NOT PART OF THE SOLUTION !!! */ public String preOrder(TreeNode root) { // **** initialization **** StringBuilder sb = new StringBuilder(); // **** populate string builder **** preOrder(root, sb); // **** return string **** return sb.toString(); } /** * Pre-order traversal. * Helper function. * * !!! NOT PART OF THE SOLUTION !!! */ private void preOrder(TreeNode root, StringBuilder sb) { // **** base case **** if (root == null) return; // **** add node to string builder **** sb.append(root.toString() + "\n"); // **** traverse left **** preOrder(root.left, sb); // **** traverse right **** preOrder(root.right, sb); }

These methods are used to perform a pre-order traversal of the segment tree. I used them to debug and experiment with the code. They **ARE ****NOT PART OF THE SOLUTION**.

/** * Level-order tree traversal. * Returns string with tree representation. * * !!! NOT PART OF THE SOLUTION !!! */ public String levelOrder(TreeNode root) { // **** initialization **** StringBuilder sb = new StringBuilder(); LinkedList<TreeNode> primaryQ = new LinkedList<>(); LinkedList<TreeNode> secondaryQ = new LinkedList<>(); // **** prime the primary queue **** primaryQ.offer(root); // **** loop while the primary queue is not empty O(n) **** while (!primaryQ.isEmpty()) { // **** remove head node **** TreeNode node = primaryQ.remove(); // **** append node to string **** sb.append(node.toString() + " "); // **** offer left child **** if (node.left != null) secondaryQ.offer(node.left); // **** offer right child **** if (node.right != null) secondaryQ.offer(node.right); // **** swap queues if needed **** if (primaryQ.isEmpty() && !secondaryQ.isEmpty()) { // **** primary points to secondary queue **** primaryQ = secondaryQ; // **** create new secondary queue **** secondaryQ = new LinkedList<>(); // **** update string builder **** sb.append("\n"); } } // **** return string representation **** return sb.toString(); }

This method is used to perform a level-order traversal of the segment tree. I used it to debug and experiment with the code. This **IS ****NOT PART OF THE SOLUTION**.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named RangeSumQueryMutableIII.

I spent a couple days researching and experimenting with the code.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.

Enjoy;

John

]]>Given an m x n binary matrix mat, eturn the number of submatrices that have all ones. Constraints: o 1 <= m, n <= 150 o mat[i][j] is either 0 or 1. Related Topics: o Array * Dynamic Programming o Stack o Matrix o Monotonic Stack

In this problem we are provided a two dimensional matrix that only contains 1s and 0s. The dimensions are specified by `n` and `m`. Our mission if we wish to accept it, is to return the number of submatrices of 1s.

Like in most (never say all) problems, there are several approaches one can use to generate a solution. Some are better than others. That said, this problem seems to call for using dynamic programming to solve it. We should be able to somehow count the number of 1s and then figure out the combinations of `x` numbers taken `y` at a time. In my humble opinion this takes a lot of thinking and in some cases some luck.

public int numSubmat(int[][] mat) { }

The signature for the function of interest is very simple and fits the requirements at hand.

3,3 1,0,1 1,1,0 main <<< mat: [1, 0, 1] [1, 1, 0] [1, 1, 0] <<< dp: [1, 0, 1] [1, 2, 0] [1, 2, 0] <<< score: 1 (0,0) <<< score: 1 (1,0) <<< score: 1 (2,0) <<< score: 1 (0,2) <<< score: 0 (1,2) <<< score: 0 (2,2) <<< score: 1 (1,0) <<< score: 1 (2,0) <<< score: 2 (1,1) <<< score: 2 (2,1) <<< score: 1 (2,0) <<< score: 2 (2,1) main <<< numSubmat0: 13 <<< mat: [1, 0, 1] [2, 1, 0] [2, 1, 0] <<< score: 1 (0,0) <<< score: 1 (0,0) <<< score: 1 (1,0) <<< score: 1 (2,0) <<< score: 1 (0,2) <<< score: 1 (0,2) <<< score: 2 (1,0) <<< score: 2 (1,0) <<< score: 2 (2,0) <<< score: 1 (1,1) <<< score: 1 (1,1) <<< score: 1 (2,1) <<< score: 2 (2,0) <<< score: 2 (2,0) <<< score: 1 (2,1) <<< score: 1 (2,1) main <<< numSubmat: 13 3,4 0,1,1,0 0,1,1,1 1,1,1,0 main <<< rows: 3 main <<< cols: 4 main <<< mat: [0, 1, 1, 0] [0, 1, 1, 1] [1, 1, 1, 0] <<< dp: [0, 1, 2, 0] [0, 1, 2, 3] [1, 2, 3, 0] <<< score: 1 (0,1) <<< score: 1 (1,1) <<< score: 1 (2,1) <<< score: 2 (0,2) <<< score: 2 (1,2) <<< score: 2 (2,2) <<< score: 1 (1,1) <<< score: 1 (2,1) <<< score: 2 (1,2) <<< score: 2 (2,2) <<< score: 3 (1,3) <<< score: 0 (2,3) <<< score: 1 (2,0) <<< score: 2 (2,1) <<< score: 3 (2,2) main <<< numSubmat0: 24 <<< mat: [0, 2, 1, 0] [0, 3, 2, 1] [3, 2, 1, 0] <<< score: 2 (0,1) <<< score: 2 (0,1) <<< score: 2 (1,1) <<< score: 2 (2,1) <<< score: 1 (0,2) <<< score: 1 (0,2) <<< score: 1 (1,2) <<< score: 1 (2,2) <<< score: 3 (1,1) <<< score: 3 (1,1) <<< score: 2 (2,1) <<< score: 2 (1,2) <<< score: 2 (1,2) <<< score: 1 (2,2) <<< score: 1 (1,3) <<< score: 1 (1,3) <<< score: 3 (2,0) <<< score: 3 (2,0) <<< score: 2 (2,1) <<< score: 2 (2,1) <<< score: 1 (2,2) <<< score: 1 (2,2) main <<< numSubmat: 24

We will be generating two versions of the function of interest.

Each test case is separated from others by two blank lines. The test cases are the ones provided by LeetCode.

In a test case, the first line provides the dimensions of the matrix. The following `n` lines provide the values for each row in the matrix. The values are 1s or 0s.

Our test scaffold, which **IS NOT PART OF THE SOLUTION**, seems to read the input lines and populate the int[][] `mat`, the contents of the matrix are then displayed in order to make sure all is well before calling the function of interest. The function of interest is then called and the result displayed.

Please note that there are several lines displayed by the function of interest. Such lines **ARE NOT PART OF THE SOLUTION**. They are there to debug the code and help us understand what is going on.

https://math.stackexchange.com/questions/1117236/calculate-the-total-number-of-combinations-over-n-elements-where-the-number-of Calculate the total number of combinations over n elements, where the number of elements in each subset is in {0,..,n}?

As usual I do some reading, you can call it research or refreshing, while thinking about the approach. Typically in an interview it is not possible to perform such a task, but in the real world that is how many developers (myself included) work.

/** * Test scaffold * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read r and c **** int[] rc = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** for ease of use **** int rows = rc[0]; int cols = rc[1]; // **** create matrix **** int[][] mat = new int[rows][]; // **** populate matrix **** for (int r = 0; r < rows; r++) mat[r] = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< rows: " + rows); System.out.println("main <<< cols: " + cols); System.out.println("main <<< mat: "); for (int r = 0; r < rows; r++) System.out.println(Arrays.toString(mat[r])); // **** call function of interest and display result **** System.out.println("main <<< numSubmat0: " + numSubmat0(mat)); // // ???? ???? // System.out.println("main <<< mat: "); // for (int r = 0; r < rows; r++) // System.out.println(Arrays.toString(mat[r])); // **** call function of interest and display result **** System.out.println("main <<< numSubmat: " + numSubmat(mat)); // // ???? ???? // System.out.println("main <<< mat: "); // for (int r = 0; r < rows; r++) // System.out.println(Arrays.toString(mat[r])); }

This is the test scaffold that was mentioned earlier. Please note that it **IS NOT PART OF THE SOLUTION**. The code reads the input lines, populates the int[][] `mat` matrix, calls two implementations of the function of interest and displays the associated results.

Please note that the contents of the `mat` matrix are displayed after the matrix is populated and after each invocation of the function of interest. As we will see shortly, one of the implementations modifies the input `mat` matrix. In production it is not a good thing to alter arguments. If needed one can create a copy and alter the copy to leave the argument **untouched**. You never know other functions / methods down the line might need the original matrix.

/** * Given an m x n binary matrix mat, * return the number of submatrices that have all ones. * * Dynamic programming (with dp int[][] array). * * Runtime: 8 ms, faster than 85.23% of Java online submissions. * Memory Usage: 42.8 MB, less than 43.11% of Java online submissions. * * 73 / 73 test cases passed. * Status: Accepted * Runtime: 8 ms * Memory Usage: 42.8 MB */ static public int numSubmat0(int[][] mat) { // **** initialization **** var num = 0; var rows = mat.length; var cols = mat[0].length; int[][] dp = new int[rows][cols]; var score = 0; // **** traverse rows in int[][] mat - 0(n) **** for (var r = 0; r < rows; r++) { // **** initialize score for this row **** score = 0; // **** traverse columns in this row - O(m) **** for (var c = 0; c < cols; c++) { // **** update score **** if (mat[r] == 1) score++; else score = 0; // **** place score in dp **** dp[r] = score; } } // ???? ???? System.out.println("<<< dp: "); for (var r = 0; r < rows; r++) System.out.println(Arrays.toString(dp[r])); // **** rows **** for (var r = 0; r < rows; r++) { // **** columns **** for (var c = 0; c < cols; c++) { // **** skip this column **** if (dp[r] == 0) continue; // **** initialize score **** score = dp[r]; // ???? ???? System.out.println("<<< score: " + score + " (" + r + "," + c + ")"); // **** update number **** num += score; // **** repeat rows **** for (var d = r + 1; d < rows; d++) { // **** update score **** score = Math.min(score, dp[d]); // ???? ???? System.out.println("<<< score: " + score + " (" + d + "," + c + ")"); // **** update number **** num += score; } } } // **** return number of submatrices **** return num; }

In this implementation we create an int[][] array named `dp`. We will use it to hold information that when properly used, should help us come up with the required solution.

The first loop is used to traverse the matrix one row at a time. This loop populates the `dp` int[][] matrix with counts of the number of 1s. We will need them to calculate combinations. In my opinion that was the easy part.

Now we need to traverse the `dp` matrix one column at a time, calculating the number of combinations. This took some reading about combinations, experimenting and optimizing.

The purpose of the second set of loops is to compute the number of combinations (`scores`) by traversing the columns multiple times moving the starting point one cell per pass.

The inner loop illustrates how the `score` is computed on each pass. The `num` variable is updated using the computed `score`.

When all is said and done, the contents of the `num` variable is returned.

Please take a look at the comments section of this function for performance information.

As we can tell after going over the code, this implementation uses and modifies the `dp` int[][] matrix. It does not modify the input `mat` int[][] matrix.

/** * Given an m x n binary matrix mat, * return the number of submatrices that have all ones. * * Dynamic programming (no dp int[][] array). * * Runtime: 9 ms, faster than 82.04% of Java online submissions. * Memory Usage: 53.2 MB, less than 14.37% of Java online submissions. * * 73 / 73 test cases passed. * Status: Accepted * Runtime: 9 ms * Memory Usage: 53.2 MB */ static public int numSubmat(int[][] mat) { // **** initialization **** var rows = mat.length; var cols = mat[0].length; var num = 0; // **** traverse rows (top to bottom) **** for (int r = 0; r < rows; r++) { // **** traverse columns (right to left) **** for (int c = cols - 2; c >= 0; c--) { if (mat[r] == 1) mat[r] += mat[r]; } } // ???? ???? System.out.println("<<< mat:"); for (int r = 0; r < rows; r++) System.out.println(Arrays.toString(mat[r])); // **** traverse rows (top to bottom) **** for (int r = 0; r < rows; r++) { // **** traverse columns (left to right) **** for (int c = 0; c < cols; c++) { // **** skip this column **** if (mat[r] == 0) continue; // **** initialize score **** var score = mat[r]; // ???? ???? System.out.println("<<< score: " + score + " (" + r + "," + c + ")"); // **** traverse rows (top to bottom) **** for (int d = r; d < rows; d++) { // **** skip this cell **** if (mat[d] == 0) break; // **** update score **** score = Math.min(score, mat[d]); // ???? ???? System.out.println("<<< score: " + score + " (" + d + "," + c + ")"); // **** update number **** num += score; } } } // **** return number of submatrices **** return num; }

This is the second implementation of the function of interest.

This implementation is very similar to the previous one. The main difference is that it does not make use of a `dp` int[][] matrix by making changes directly into the int[][] `mat` array. In general this is not a good idea when working on production projects.

I am not going to go though the code due to the similarities with the previous implementation.

Please take a look at the comments section of this function. The function is slightly slower (1 ms) than the previous implementation. It seems that performance reports are affected by the number of simultaneous submissions at the LeetCode website. One way or the other, this implementation uses less space O(1) than the previous O(n * m).

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named CountSubmatricesWithAllOnes.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.

Enjoy;

John

]]>Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining. Constraints: o n == height.length o 1 <= n <= 2 * 10^4 o 0 <= height[i] <= 10^5 Related Topics: * Array * Two Pointers o Dynamic Programming o Stack o Monotonic Stack

The diagram on the LeetCode page is very useful to get the general idea of what the problem is. It also helps to draw the diagram on a piece of paper and figure out an approach. We need to calculate the amount of water on each cell and add them together to get our result. The trick is in how we implement the task.

I will be solving the problem on my computer and then copying the solution to LeetCode for evaluation. I will be developing a simple test scaffold to collect the input data, populate the array, call the function of interest and display the output. Please note that the test code **IS NOT PART OF THE SOLUTION!**

public int trap(int[] height) { }

The signature for the function of interest matches very well the problem requirements. We are given in int[] with the heights of the bars and we need to return the amount of collected rain.

0,1,0,2,1,0,1,3,2,1,2,1 main <<< height: [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] <<< n: 12 <<< left: 1 right: 11 waterTrapped: 0 <<< left: 1 right: 10 waterTrapped: 0 <<< left: 2 right: 10 waterTrapped: 1 <<< left: 3 right: 10 waterTrapped: 1 <<< left: 3 right: 9 waterTrapped: 2 <<< left: 3 right: 8 waterTrapped: 2 <<< left: 3 right: 7 waterTrapped: 2 <<< left: 4 right: 7 waterTrapped: 3 <<< left: 5 right: 7 waterTrapped: 5 <<< left: 6 right: 7 waterTrapped: 6 <<< left: 7 right: 7 waterTrapped: 6 main <<< trap0: 6 <<< n: 12 <<< maxLeft: [0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3] <<< maxRight: [3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 0] <<< minLR: [0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 1, 0] <<< i: 2 water: 1 <<< i: 4 water: 1 <<< i: 5 water: 2 <<< i: 6 water: 1 <<< i: 9 water: 1 main <<< trap: 6 4,2,0,3,2,5 main <<< height: [4, 2, 0, 3, 2, 5] <<< n: 6 <<< left: 1 right: 5 waterTrapped: 2 <<< left: 2 right: 5 waterTrapped: 6 <<< left: 3 right: 5 waterTrapped: 7 <<< left: 4 right: 5 waterTrapped: 9 <<< left: 5 right: 5 waterTrapped: 9 main <<< trap0: 9 <<< n: 6 <<< maxLeft: [0, 4, 4, 4, 4, 4] <<< maxRight: [5, 5, 5, 5, 5, 0] <<< minLR: [0, 4, 4, 4, 4, 0] <<< i: 1 water: 2 <<< i: 2 water: 4 <<< i: 3 water: 1 <<< i: 4 water: 2 main <<< trap: 9

The test cases are separated by two blank lines.

The first line in each test case holds the values for the int[] `height`. Our test code seems to read the input line, declare, populate and display the int[] `height`. This is done to make sure all is well before calling the function of interest.

It seems that we have two implementations of the function of interest.

Our test scaffold calls the implementations and displays the associated results. The messages in between are used to provide us information on how the code is progressing. This helps to understand the algorithm and to debug the code during development.

/** * Test scaffold. * !!! NOT PART OF THE SOLUTION !!! * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** var br = new BufferedReader(new InputStreamReader(System.in)); // **** read height int[] array **** int[] height = Arrays.stream(br.readLine().trim().split(",")) .map(s -> s.trim()) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< height: " + Arrays.toString(height)); // **** call function of interest and display output **** System.out.println("main <<< trap0: " + trap0(height)); // **** call function of interest and display output **** System.out.println("main <<< trap: " + trap(height)); }

Our test scaffold is quite simple. It reads the input line, populates the int[] `height` (perhaps it should have been named `heights`) and then calls the first implementation of the function of interest. The result is displayed. The same approach is used with the second implementation. Please note that the test code **IS NOT PART OF THE SOLUTION!**

Key concept: water = max(maxLeft, maxRight) - height[i];

The key concept to solve the problem in both implementations is to determine the statement to calculate the amount of water in each bar in the `height` int[] array.

/** * Given n non-negative integers representing an * elevation map where the width of each bar is 1, * compute how much water it can trap after raining. * * Runtime: O(n) - Space: O(n) * * Runtime: 1 ms, faster than 92.42% of Java online submissions. * Memory Usage: 38.8 MB, less than 64.52% of Java online submissions. * * 320 / 320 test cases passed. * Status: Accepted * Runtime: 1 ms * Memory Usage: 38.8 MB */ static public int trap(int[] height) { // **** initialization **** int n = height.length; // **** sanity check (no rain can be collected) **** if (n <= 2) return 0; // **** **** int waterTrapped = 0; int[] maxLeft = new int[n]; int[] maxRight = new int[n]; int[] minLR = new int[n]; int ml = 0; int mr = 0; // ???? ???? System.out.println("<<< n: " + n); // **** populate maxLeft - O(n) **** for (int i = 0; i < n; i++) { // **** **** if (i == 0) { maxLeft[i] = height[i]; ml = 0; } // **** populate maxLeft **** maxLeft[i] = ml; // **** update ml **** if (ml < height[i]) ml = height[i]; } // ???? ???? System.out.println("<<< maxLeft: " + Arrays.toString(maxLeft)); // **** populate maxRight and min(L,R) - O(n) **** for (int i = n - 1; i >= 0; i--) { // **** **** if (i == n - 1) { maxRight[i] = height[i]; mr = 0; } // **** populate maxRight **** maxRight[i] = mr; // **** update mr **** if (mr < height[i]) mr = height[i]; // **** poulate minLR **** minLR[i] = Math.min(maxLeft[i], maxRight[i]); } // ???? ???? System.out.println("<<< maxRight: " + Arrays.toString(maxRight)); System.out.println("<<< minLR: " + Arrays.toString(minLR)); // **** compute maxWater - O(n) **** for (int i = 0; i < n; i++) { // **** water in this cell (key computation) **** int water = minLR[i] - height[i]; // **** increment water trapped (if needed) **** if (water > 0) { // ???? ???? System.out.println("<<< i: " + i + " water: " + water); // **** increment water trapped **** waterTrapped += water; } } // **** return water trapped **** return waterTrapped; }

This is the first implementation of the function of interest.

We start by performing a sanity check followed by an initialization step in which we declare three int[] arrays. The `maxLeft` array will hold the maximum left value seen at each bar when moving from left to right. The `maxRight` array will hold the maximum right value encountered at each bar when moving from right to left. The `minLR` int[] array will hold the minimum value of `maxLeft` and `maxRight` for each bar. This will become clearer as we look at the rest of the code.

In the first loop we populate the `maxLeft` int[] array.

In the second loop we populate the values in the `maxRight` int[] array and since we already have the values of `maxLeft` we are also able to fill the values of the `minLR` int[] array.

In the last loop we traverse the `height` int[] array generating the amount of water at each bar. If the amount is larger than zero we add the `water` value to the `waterTrapped` variable.

When all is said and done we returned the amount of water trapped.

For execution statistics please look at the comments section of this function in the code we just finished looking at.

/** * Given n non-negative integers representing an * elevation map where the width of each bar is 1, * compute how much water it can trap after raining. * * Runtime: O(n) - Space: O(1) * * Runtime: 9 ms, faster than 9.07% of Java online submissions. * Memory Usage: 42.2 MB, less than 17.46% of Java online submissions. * * 320 / 320 test cases passed. * Status: Accepted * Runtime: 9 ms * Memory Usage: 42.2 MB */ static public int trap0(int[] height) { // **** set n **** int n = height.length; // ???? ???? System.out.println("<<< n: " + n); // **** sanity check (no rain can be collected) **** if (n <= 2) return 0; // **** initialization **** int waterTrapped = 0; int left = 0; int right = n - 1; int leftMax = height[left]; int rightMax = height[right]; // **** traverse height array - O(n) **** while (left < right) { // **** update left or right **** if (leftMax < rightMax) { // **** update left pointer **** left++; // **** update left max value **** leftMax = Math.max(leftMax, height[left]); // **** compute collected water increment **** waterTrapped += leftMax - height[left]; } else { // **** update right pointer **** right--; // **** update right max value **** rightMax = Math.max(rightMax, height[right]); // **** compute collected water increment **** waterTrapped += rightMax - height[right]; } // ???? ???? System.out.println("<<< left: " + left + " right: " + right + " waterTrapped: " + waterTrapped); } // **** return water trapped **** return waterTrapped; }

The approach for this function is similar to the one used in the previous implementation. The idea is to determine if we can eliminate the use of the three arrays in order to eliminate the space O(n).

We start by performing a sanity check. Note that if we have an array with only two values no water can be collected.

We then declare and initialize some variables.

The while loop is used in conjunction with the `left` and `right` pointers (indices) to provide the information for the left and right ends associated with each `height[i]`.

We have an `if` condition that is used to determine if we increase the `left` pointer or decrease the `right` one.

After updating the pointers we compute the `leftMax` or `rightMax` by using the `height[i]` value.

At this point we only need to compute the amount of water in the current cell and update the contents of the `waterTrapped` variable.

Please take a few moments to compare how this implementation does the same things as the previous one but uses no additional space: O(1).

When all is said and done, the function returns the value in the `waterTrapped` variable.

Please take a look at the comments section of this function in the source code. The execution information is there.

I was surprised to note that without the three int[] arrays the execution time was slower. I believe that results might be somewhat skewed depending on the load at the LeetCode servers. If you have an alternate idea, please leave your comments below.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository named TrappingRainWater.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.

Enjoy;

John

]]>