Last week I read the article (let me see if I can find it in my Twitter account **@john_canessa** … found it) Cybersecurity bills gain new urgency after rash of attacks. It seems that both political parties, Democrats and Republicans are pushing a bill that will help our country against cyber attacks. In the past few months we have seen hundreds if not thousands of attacks, some to disable our government, companies and organizations or to ask for ransom. One of the proposed ideas is to force targets to disclose in a short period of time (24 hours or so) any cyber breach they have experienced. This will allow the government and other organizations to take action as needed to prevent the same type of attacks. In my opinion there should be some type of penalty for the bad actors in an attempt to curb their proliferation (transparency and accountability). Will see what happens with the bill and its implementation!

The main subject for this post is LeetCode 430 Flatten a Multilevel Doubly Linked List problem. Earlier this year, we solved a similar problem, which you can find in this blog under Flatten a Linked List in Java.

You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below. Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list. Constraints: o The number of Nodes will not exceed 1000. o 1 <= Node.val <= 105

The idea is that we are given a doubly linked list in which nodes have a third element named `child`. Some nodes may have a reference to a different linked list. The process may go on a few times.

Our task, if we accept this challenge, is to coalesce all linked lists into a single one. Each list should be incorporated / patched after the node holding the `child` link.

We are going to attempt to solve the problem using the Java programming language and the VSCode IDE on a Windows computer. Due to this approach we will also have to develop a test scaffold to read the input data, generate the multilevel linked list, call the function of interest and display the results. If this is not appealing to you, the best approach is to directly use the on-line IDE provided by LeetCode.

/* // Definition for a Node. class Node { public int val; public Node prev; public Node next; public Node child; }; */ class Solution { public Node flatten(Node head) { }

We have to develop the linked list using the provided Node class. The signature of the function of interest is presented with a single argument which is the head of the multilevel doubly linked list we need to flatten.

1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12 main <<< arr: [1, 2, 3, 4, 5, 6, null, null, null, 7, 8, 9, 10, null, null, 11, 12] main <<< head: (1)->(2)->(3)->(7)->(8)->(11)->(12)->(9)->(10)->(4)->(5)->(6) 1,2,null,3 main <<< arr: [1, 2, null, 3] main <<< head: (1)->(3)->(2) 1 main <<< arr: [1] main <<< head: (1) main <<< arr: null main <<< head: 1,null,2,null,3,null main <<< arr: [1, null, 2, null, 3, null] main <<< head: (1)->(2)->(3)

We are provided with a single input line. In the first test case there are three different linked lists. The first one starts at the head. The second starts at node 3, and the third at node 8. If interested, LeetCode provides a nice diagram of this linked list.

Our test code seems to read the input line and assign it to an Integer[] array. The array is then displayed to allow us to check if all is well so far. The function of interest is then called and the results are displayed.

Our test cases display a single result, but we have two implementations. The first does not use recursion. The second does.

/** * Test scaffold. * @throws IOException */ public static void main(String[] args) throws IOException { // **** initialization **** Integer[] arr = null; // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // ???? does not support null values ???? // Integer[] arr = Arrays.stream(br.readLine().trim().split(",")) // .mapToInt(Integer::parseInt) // .boxed() // .toArray(Integer[]::new); // ???? supports null values ???? // Integer[] arr = Arrays.stream(br.readLine().trim().split(",")) // // .mapToInt(x -> ((x == null) ? (Integer)null : Integer.parseInt(x))) // .map( x -> ((x == null) ? (Integer)null : Integer.parseInt(x)) ) // // .boxed() // .toArray(Integer[]::new); // **** populate String[] with input values **** String[] strs = br.readLine().trim().split(","); // ???? ???? // System.out.println("main <<< strs.length: " + strs.length); // for (int i = 0; i < strs.length; i++) // System.out.println("main <<< strs[" + i + "] ==>" + strs[i] + "<=="); // **** allocate and populate arr **** if (!(strs.length == 1 && strs[0].equals(""))) { // **** allocate arr **** arr = new Integer[strs.length]; // **** populate arr **** for (int i = 0; i < arr.length; i++) { if (strs[i].equals("null")) arr[i] = null; else arr[i] = Integer.parseInt(strs[i]); } } // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< arr: " + Arrays.toString(arr)); // **** create and populate multilevel doubly linked list **** Node head = buildLinkedList(arr); // // **** flatten the linked list **** // head = flatten0(head); // // **** display flatten linked list **** // System.out.print("main <<< head: "); forward(head); // **** flatten the linked list **** head = flatten(head); // **** display flatten linked list **** System.out.print("main <<< head: "); forward(head); }

Our test code, which is **NOT PART OF THE SOLUTION**, reads the input line and populates an Integer[] array as was described while looking at the test cases.

If needed, multiple additional linked lists are created, populated and attached to the specified node in the main linked list. This is done by the `buildLinkedList` function which takes as an argument the `arr`.

The first attempt, which was accepted by LeetCode, has been commented out. We will look at this code shortly.

The second attempt, which was also accepted by LeetCode, uses recursion. We will also take a look at it in a few.

/** * Build multilevel dobly linked list. * * !!! NOT PART OF SOLUTION !!!! */ static Node buildLinkedList (Integer[] arr) { // **** sanity checks **** if (arr == null) return null; // **** initialization **** Node head = null; Node h = null; Node t = null; Node n = null; // **** process the array creating the linked list **** for (int i = 0; i < arr.length; i++) { // **** process integer **** if (arr[i] != null) { // **** create node and set value **** Node node = new Node(); node.val = arr[i]; // **** **** if (n != null) { n.child = node; n = null; h = null; t = null; } // **** this node is a head **** if (h == null) { // **** set head and tail of queue **** if (head == null) { head = node; } // **** set head and tail of this linked list **** h = node; t = node; } // **** this node is not a head **** else { // **** update next on previous node **** t.next = node; // **** update node links **** node.prev = t; // **** update tail **** t = node; } } // **** process null **** else { // **** first n **** if (n == null) n = h; // **** next n **** else n = n.next; } } // **** return head **** return head; }

This is the code we used to build the multilevel doubly linked list. I will not elaborate on it at this time given that this code is **NOT PART OF THE SOLUTION**.

/** * Traverse linked list forward (head -> tail). * * !!! NOT PART OF SOLUTION !!! */ static void forward(Node head) { // **** display all nodes in the linked list **** for (Node p = head; p != null; p = p.next) { System.out.print(p.toString()); if (p.next != null) System.out.print("->"); } // **** end of linked list **** System.out.println(); }

When I deal with different data structures, I typically like to display their contents while I develop the code, or to display the final result. This function displays a linked list from head to tail.

/** * Traverse linked list backwards (head <- tail). * * !!! NOT PART OF SOLUTION !!! */ static void backward(Node tail) { // **** display all nodes in the linked list **** for (Node p = tail; p != null; p = p.prev) { System.out.print(p.toString()); if (p.prev != null) System.out.print("->"); } // **** start of linked list **** System.out.println(); }

This function displays a linked list from tail to head. We could have generated a single function that displays the values and checks the links instead of displaying the linked list in two directions. I have been using the first approach and seem to do the job. One of these days I will implement the second approach.

/** * Flatten the list so that all the nodes appear in a single-level, doubly linked list. * You are given the head of the first level of the list. * * Runtime: 1 ms, faster than 17.76% of Java online submissions. * Memory Usage: 37 MB, less than 58.65% of Java online submissions. */ static Node flatten0(Node head) { // **** traverse main list until end **** for (Node p = head; p != null; p = p.next) { // **** check if this node has a child list **** if (p.child != null) { // **** **** Node child = p.child; // **** to avoid endless loop **** p.child = null; // **** **** child = flatten0(child); // **** set `t` to tail in child linked list **** Node t = child; for ( ; t.next != null; t = t.next); // **** splice child linked list AFTER `p` node **** t.next = p.next; if (p.next != null) p.next.prev = t; p.next = child; child.prev = p; } } // **** return head node of list **** return head; }

This was my first approach. It is recursive since it calls itself at the start of each linked list. The taks of attaching the linked list to the original one is done in the previous recursive call.

When all is said and done we return the linked list.

/** * Flatten the list so that all the nodes appear in a single-level, doubly linked list. * You are given the head of the first level of the list. * * 26 / 26 test cases passed. * Status: Accepted * Runtime: 0 ms * Memory Usage: 38.3 MB */ static Node flatten(Node head) { // **** flatten list (recursively) **** flattenRecursive(head); // **** return head node **** return head; }

In this approach, we start by calling a method that recursively flattens the linked list. When all is said and done we return the resulting linked list. This implementation seems to be cleaner than the previous one, yet both achieve the same results.

/** * Recursive call. * * Runtime: 0 ms, faster than 100.00% of Java online submissions. * Memory Usage: 38.3 MB, less than 25.33% of Java online submissions. * * O(n) */ static private Node flattenRecursive(Node head) { // **** end condition **** if (head == null || (head.next == null && head.child == null)) return head; // **** recursive calls **** Node ct = flattenRecursive(head.child); Node nt = flattenRecursive(head.next); // **** **** if (ct != null) { if (nt != null) { // **** link after flatten child list **** ct.next = head.next; head.next.prev = ct; ct = nt; } // **** link flatten child list **** head.next = head.child; head.next.prev = head; head.child = null; // **** return child tail **** return ct; } else { // **** return next tail **** return nt; } }

This function starts by checking the base case. It then makes a couple recursive calls.

The first uses head child node which in most cases is set to null. The second uses the next node in the current linked list.

Base on the results we patch the linked lists as needed.

As you should be able to tell the approach performs the same tasks with different implementations. Note that the execution times may be found in the comment section of both implementations. The second approach is 1 ms faster.

I enjoy experimenting and working with linked lists. As a matter of fact one of the `**guardrails**` that I have developed some time ago in the form of a DLL is based on a queue implemented as a doubly linked list. Currently the DLL named **sncrque.dll** holds 60+ functions.

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

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 toolset.

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

Regards;

John

]]>I selected LeetCode 96 Unique Binary Search Trees problem. I looked for problems with the **binary search** tag and this one came up. That said I did not see the use of the binary search technique. Perhaps the problem was not properly classified.

Given an integer n, return the number of structurally unique BST's (binary search trees) which has exactly n nodes of unique values from 1 to n. Constraints: o 1 <= n <= 19

By reading the requirements it might seem that the problem has to do with binary search trees. By spending a few more on the requirements, the problem has to do more with combinations.

I understand that no one knows everything about solving problems like the ones presented by LeetCode or any other similar website. In an interview one might not be allowed to search the web to gather information. That is not how software developers / engineers or system architects work. We consult the web and books. In this case that is what I did.

I ran into something called Catalan number. I know about the Catalan language and have been a few times in Barcelona, Spain which is in the region of Catalonia.

Catalan numbers are a sequence of natural numbers that occur in many interesting counting problems like following: 1. Count the number of expressions containing n pairs of parentheses which are correctly matched. For n = 3, possible expressions are ((())), ()(()), ()()(), (())(), (()()). 2. Count the number of possible Binary Search Trees with n keys. 3. Count the number of full binary trees (A rooted binary tree is full if every vertex has either two children or no children) with n + 1 leaves. 4. Given a number n, return the number of ways you can draw n chords in a circle with 2 x n points such that no 2 chords intersect.

It seems that Catalan numbers is a sequence that occurs in many counting problems. There are many applications but take a look at item #3 in the previous list. In addition I found on-line Applications of Catalan Numbers and most important Program for nth Catalan Number. I read the articles listed in this post and then started experimenting.

public int numTrees(int n) { }

Getting back to the problem at hand, we are given a number of nodes and we need to return the number of different binary search trees we can generate or deduct. **Spoiler alert**: there is no need to generate a single binary search tree. The signature for the function of interest receives the number of nodes and the function should return the number of combinations.

3 main <<< n: 3 main <<< catalan0(0): 1 main <<< catalan0(1): 1 main <<< catalan0(2): 2 main <<< catalan0(3): 5 main <<< catalan(0): 1 main <<< catalan(1): 1 main <<< catalan(2): 2 main <<< catalan(3): 5 main <<< numTrees: 5 1 main <<< n: 1 main <<< catalan0(0): 1 main <<< catalan0(1): 1 main <<< catalan(0): 1 main <<< catalan(1): 1 main <<< numTrees: 1 13 main <<< n: 13 main <<< catalan0(0): 1 main <<< catalan0(1): 1 main <<< catalan0(2): 2 main <<< catalan0(3): 5 main <<< catalan0(4): 14 main <<< catalan0(5): 42 main <<< catalan0(6): 132 main <<< catalan0(7): 429 main <<< catalan0(8): 1430 main <<< catalan0(9): 4862 main <<< catalan0(10): 16796 main <<< catalan0(11): 58786 main <<< catalan0(12): 208012 main <<< catalan0(13): 742900 main <<< catalan(0): 1 main <<< catalan(1): 1 main <<< catalan(2): 2 main <<< catalan(3): 5 main <<< catalan(4): 14 main <<< catalan(5): 42 main <<< catalan(6): 132 main <<< catalan(7): 429 main <<< catalan(8): 1430 main <<< catalan(9): 4862 main <<< catalan(10): 16796 main <<< catalan(11): 58786 main <<< catalan(12): 208012 main <<< catalan(13): 742900 main <<< numTrees: 742900

Since we will be developing this code in Java using the VSCode IDE on a Windows computer, and not in the on-line IDE provided by LeetCode, we will need to generate test code to collect the input, call the function in question and display the result. Note that the test code **IS NOT PART OF THE SOLUTION**.

The first and only input line represents `n` which is an integer passed to the function of interest. Our test code generated and populates the variable `n` and displays it. We do this to make sure that all is well so far.

It seems that the code call makes a set of calls to a couple functions which seem to return the same values when called with the same argument. Note that in the first example, we are given `n` with a value of 3 and the last output for each function returns the proper answer for our problem.

Our test code then makes a call to the function of interest and returns a result which is then displayed.

You might wish to take a look at the other examples.

/** * Test scaffold. * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read `n` **** int n = Integer.parseInt(br.readLine().trim()); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< n: " + n); // ???? ???? for (int i = 0; i <= n; i++) System.out.println("main <<< catalan0(" + i + "): " + catalan0(i)); // ???? ???? for (int i = 0; i <= n; i++) System.out.println("main <<< catalan(" + i + "): " + catalan(i)); // **** call function of interest and display result **** System.out.println("main <<< numTrees: " + numTrees(n)); }

Our test code seems to match very closely to the description of processing a test case. We can see that the `catalan0()` and `catalan()` functions are called out of the context of our problem. This was done to get familiar with them.

The last line invokes the function of interest and displays the results.

/** * Compute the specified catalan number. * This is a recursive call. * * Runtime: 1960 ms, faster than 8.41% of Java online submissions. * Memory Usage: 35.6 MB, less than 77.23% of Java online submissions. */ static int catalan0(int n) { // **** base case **** if (n <= 1) return 1; // **** initialization **** int res = 0; // **** recursive calls **** for (int i = 0; i < n; i++) res += catalan0(i) * catalan0((n - 1) - i); // **** return result **** return res; }

This function generates the Catalan number associated with the specified `n` argument. It is a recursive call. Note that it is a little **heavy** and prone to recalculating values.

I made name modifications and submitted it as the function of interest. The results are in the comment section of the function. Slow but it was **accepted**.

/** * A Binomial coefficient based function to find the * nth catalan number in O(n) time. * * Runtime: 0 ms, faster than 100.00% of Java online submissions. * Memory Usage: 35.8 MB, less than 49.43% of Java online submissions. */ static long catalan(int n) { // **** calculate value of 2nCn **** long c = binomialCoeff(2 * n, n); // **** return 2nCn / (n+1) **** return c / (n + 1); }

This is the second implementation of the Catalan number function. We calculate a binomial coefficient and return it divided by `n + 1`.

/** * Returns value of Binomial Coefficient C(n, k) */ static long binomialCoeff(int n, int k) { // **** initialization **** long res = 1; // **** since C(n, k) = C(n, n-k) **** if (k > n - k) k = n - k; // **** generate value of [n*(n-1)*---*(n-k+1)] / [k*(k-1)*---*1] **** for (int i = 0; i < k; i++) { res *= (n - i); res /= (i + 1); } // **** return binomial coefficient **** return res; }

This function generates the binomial coefficient and returns it to the caller.

Please take a look at the comments section of the `catalan` function to verify the **increase** in performance. I also modified the name and submitted as the function of interest.

/** * Given an integer n, return the number of structurally unique BST's (binary search trees) * which has exactly n nodes of unique values from 1 to n. * * Runtime: 0 ms, faster than 100.00% of Java online submissions. * Memory Usage: 35.3 MB, less than 93.57% of Java online submissions. */ static int numTrees(int n) { // **** sanity check(s) **** if (n <= 1) return 1; // **** initialization **** int arr[] = new int[n + 1]; arr[0] = arr[1] = 1; // **** populate array **** for (int i = 2; i <= n; i++) { for (int j = 0; j < i; j++) { arr[i] += arr[j] * arr[i - j - 1]; } } // ???? ???? // System.out.println("numTrees <<< arr: " + Arrays.toString(arr)); // **** return result **** return arr[n]; }

Finally, after additional research I found a different way to compute the binomial coefficient. Once again, I submitted this implementation and got a very slight improvement from the previous pass.

Once again, most of the times in an interview, one is not allowed to consult the web or books. That said; engineers always consult both sources. The key is to read and understand what is being done editing the code.

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

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 toolset.

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

Regards;

John

]]>Today I picked up the LeetCode 349 Intersection of Two Arrays problem. To be honest with you I do not recall why I did it. Due to thunderstorms I had to power down my computers a couple times today. I just lost track of the reason why I picked this problem.

Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order. Constraints: o 1 <= nums1.length, nums2.length <= 1000 o 0 <= nums1[i], nums2[i] <= 1000

We are given two integer arrays and are charged with returning an array of all the values that intersect the arrays. In other words the **unique** common values to both arrays.

In this post we will be solving the problem using the Java programming language and the VSCode IDE on a Windows platform connected to a UPS due to the possibility of thunderstorms in this area. Your choice of platform should not affect the code since Java is quite portable.

We will be generating our own test code since we will be developing the code on my Windows machine. Unless you have a good reason to generate your own test code, you should use the on-line IDE provided by LeetCode. Note that the test code **IS NOT PART OF THE SOLUTION!**

public int[] intersection(int[] nums1, int[] nums2) { }

The first two are input lines. They hold the integer values for the two arrays `nums1` and `nums2`. Our test code seems to be able to read the values, populate the two arrays and then display them. This is done to verify that all is well so far.

1,2,2,1 2,2 main <<< nums1: [1, 2, 2, 1] main <<< nums2: [2, 2] main <<< intersection0: [2] main <<< intersection: [2] 4,9,5 9,4,9,8,4 main <<< nums1: [4, 9, 5] main <<< nums2: [9, 4, 9, 8, 4] main <<< intersection0: [4, 9] main <<< intersection: [4, 9]

It seems we have tow implementations. They both seem to provide the same results in the two cases. Hopefully it will carry when the code is submitted at LeetCode.

/** * Test scaffold. * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read `nums1` array **** int[] nums1 = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** read `nums2` array **** int[] nums2 = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< nums1: " + Arrays.toString(nums1)); System.out.println("main <<< nums2: " + Arrays.toString(nums2)); // **** call function of interest and display result **** System.out.println("main <<< intersection0: " + Arrays.toString(intersection0(nums1, nums2))); // **** call function of interest and display result **** System.out.println("main <<< intersection: " + Arrays.toString(intersection(nums1, nums2))); }

The source code for the test scaffold is self explanatory. I do not have much to add at this time.

/** * Given two integer arrays nums1 and nums2, return an array of their intersection. * Each element in the result must be unique and you may return the result in any order. * * Sorting arrays. * * Runtime: 5 ms, faster than 20.88% of Java online submissions. * Memory Usage: 38.9 MB, less than 91.27% of Java online submissions. * * 55 / 55 test cases passed. * Status: Accepted * Runtime: 5 ms * Memory Usage: 38.9 MB */ static int[] intersection0(int[] nums1, int[] nums2) { // **** initialization **** List<Integer> ans = new ArrayList<Integer>(); // **** sort arrays - O(n * log(n)) **** Arrays.sort(nums1); Arrays.sort(nums2); // **** traverse arrays looking for intersection(s) **** int i = 0, j = 0; while (i < nums1.length && j < nums2.length) { if (nums1[i] < nums2[j]) i++; else if (nums1[i] == nums2[j]) { // **** check for duplicate values **** if (!ans.contains(nums1[i])) ans.add(nums1[i]); // **** **** i++; j++; } else j++; } // **** populate result with the contents of the list **** int[] result = new int[ans.size()]; i = 0; for (int r : ans) result[i++] = r; // **** return int[] result **** return result; }

In this implementation we will sort both arrays and then we will traverse them in ascending order looking for the common (intersecting) value(s).

We start by initializing the `ans` list. It will hold the intersecting values.

Next we sort both arrays. We will be traversing the arrays in ascending order using a couple indices while adding intersecting values to the `ans` list.

We declare a couple indices and set them to 0. We then enter the while loop which will cycle while the values of I and j are in range.

We then have logic to check the relative values of the arrays pointed by the indices. There are three conditions. The first and last are just skipping values in the arrays. When the values are the same, we check if we need to add it to the `ans` list. If so we do it. One way or the other we update both indices because the values are the same. We have just dealt with an intersecting value.

After the while loop we need to return the data in the list as an array. There are different approaches we could have used. In this case we declare the `result` array and then populate it with the values in the `ans` list. When all is set and done we return the `result` array.

You can see the execution time and memory usage in the comments section of the function.

/** * Given two integer arrays nums1 and nums2, return an array of their intersection. * Each element in the result must be unique and you may return the result in any order. * * Using HashSet. * * Runtime: 2 ms, faster than 95.11% of Java online submissions. * Memory Usage: 38.9 MB, less than 82.51% of Java online submissions. * * 55 / 55 test cases passed. * Status: Accepted * Runtime: 2 ms * Memory Usage: 39 MB */ static int[] intersection(int[] nums1, int[] nums2) { // **** initialization **** HashSet<Integer> hs = new HashSet<Integer>(); List<Integer> ans = new ArrayList<Integer>(); // **** populate hash set - O(n) **** for (int i = 0; i < nums1.length; i++) hs.add(nums1[i]); // **** look for common values - O(m) **** for (int i = 0; i < nums2.length; i++) { // **** check if this is an intersection **** if (hs.contains(nums2[i])) { // **** add value to list **** ans.add(nums2[i]); // **** to avoid duplicates **** hs.remove(nums2[i]); } } // **** populate result with contents of list **** int[] result = new int[ans.size()]; int j = 0; for (int r : ans) result[j++] = r; // **** return int[] result **** return result; }

In this implementation of the function of interest we will use a hash set instead of sorting the arrays.

We start by declaring a couple data structures and then populating the hash set with the values from the `nums1` array.

A loop is then encountered. In this loop we will traverse the contents of the `nums2` array checking for intersections with values in the hash map. When an intersection is found, we update the `ans` list and remove the value form the hash map. We need to do so to avoid multiple instances of the same value.

When all is done, we need to generate and return an integer array. We use the same approach as we did in the previous implementation.

Please check the execution time and space used by this implementation in the comments section of the function.

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

Regards;

John

]]>Earlier today I was snooping around in the LeetCode website. For the first time I ran into a page that shows every submission you make for a problem. Perhaps I am mistaken, but it seems that the site counts as attempts every submission you make. For example, you ran your first pass and let’s say it is accepted but you want to learn more by improving performance. You may even generate one or more implementations. It seems that they count against you. This seems awkward to me. If I am trying to learn I should not be penalized. If my assumption is incorrect, ** PLEASE LEAVE ME A NOTE BELLOW SETTING ME STRAIGHT**.

I took a stab at LeetCode 541 Reverse String II problem.

Given a string s and an integer k, reverse the first k characters for every 2k characters counting from the start of the string. If there are fewer than k characters left, reverse all of them. If there are less than 2k but greater than or equal to k characters, then reverse the first k characters and left the other as original. Constraints: o 1 <= s.length <= 104 o s consists of only lowercase English letters. o 1 <= k <= 104

We are given a string `s` and an integer `k`. We need to reverse and append characters. Quite convoluted but there it is.

public String reverseStr(String s, int k) { }

We are going to solve this problem using the Java programming language and the VSCode IDE on a Windows platform. Since Java is quite portable the platform and IDE of choice should not affect the code.

abcd 2 main <<< s ==>abcd<== main <<< k: 2 main <<< reverseStr0 ==>bacd<== main <<< reverseStr ==>bacd<== abcde 2 main <<< s ==>abcde<== main <<< k: 2 main <<< reverseStr0 ==>bacde<== main <<< reverseStr ==>bacde<== abcdefgh 3 main <<< s ==>abcdefgh<== main <<< k: 3 main <<< reverseStr0 ==>cbadefhg<== main <<< reverseStr ==>cbadefhg<== abcdefg 2 main <<< s ==>abcdefg<== main <<< k: 2 main <<< reverseStr0 ==>bacdfeg<== main <<< reverseStr ==>bacdfeg<== abcdef 2 main <<< s ==>abcdef<== main <<< k: 2 main <<< reverseStr0 ==>bacdfe<== main <<< reverseStr ==>bacdfe<== abcdefghijklmnopq 2 main <<< s ==>abcdefghijklmnopq<== main <<< k: 2 main <<< reverseStr0 ==>bacdfeghjiklnmopq<== main <<< reverseStr ==>bacdfeghjiklnmopq<== abcdefghijklmnopq 3 main <<< s ==>abcdefghijklmnopq<== main <<< k: 3 main <<< reverseStr0 ==>cbadefihgjklonmpq<== main <<< reverseStr ==>cbadefihgjklonmpq<== abcdefghijklmnopq 4 main <<< s ==>abcdefghijklmnopq<== main <<< k: 4 main <<< reverseStr0 ==>dcbaefghlkjimnopq<== main <<< reverseStr ==>dcbaefghlkjimnopq<==

We are provided two input lines. The first contains a sequence of lower case characters. The second line contains the value for `k`.

Our test code seems to read both input lines and assign them to variables `s` and `k`. Both variables are then displayed. This is done to verify that the input data matches the contents of our variables which will be shortly used as arguments to the function of interest.

It seems that the function of interest is called and the results are displayed. But wait, there is more. A second implementation that returns the same value is also called.

/** * Test scaffold. * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read string `s` **** String s = br.readLine().trim(); // **** read `k` **** int k = Integer.parseInt(br.readLine().trim()); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< s ==>" + s + "<=="); System.out.println("main <<< k: " + k); // **** generate and display answer **** System.out.println("main <<< reverseStr0 ==>" + reverseStr0(s, k) + "<=="); // **** generate and display answer **** System.out.println("main <<< reverseStr ==>" + reverseStr(s, k) + "<=="); }ot

Our test code seems to closely follow the description of a test case. Not much to add to it at this time.

/** * Given a string s and an integer k, * reverse the first k characters for every 2k characters counting from the start of the string. * * If there are fewer than k characters left, reverse all of them. * * If there are less than 2k but greater than or equal to k characters, * then reverse the first k characters and left the other as original. * * Runtime: 1 ms, faster than 77.39% of Java online submissions. * Memory Usage: 39 MB, less than 60.02% of Java online submissions. * * 60 / 60 test cases passed. * Status: Accepted * Runtime: 1 ms * Memory Usage: 39 MB * * This implementation uses StringBuilders. */ static String reverseStr0(String s, int k) { // **** initialization **** int len = s.length(); StringBuilder ans = new StringBuilder(); int i = 0; int twoK = 2 * k; // **** take care of complete 2 * k sub strings **** for ( ; i < len / twoK; i++) { // **** extract sub string **** String sub = s.substring(i * twoK, (i + 1) * twoK); // **** first k characters **** StringBuilder sb = new StringBuilder(sub.substring(0, k)); // **** reverse first k characters **** sb.reverse(); // **** append last k characters **** sb.append(sub.substring(k)); // **** append to answer **** ans.append(sb); } // **** check if done **** if (i * twoK == len) return ans.toString(); // **** if fewer than k characters left (reverse all of them) **** if (len - (i * twoK) < k) { // **** extract the sub string **** String sub = s.substring(i * twoK); // **** entire sub string **** StringBuilder sb = new StringBuilder(sub); // **** reverse all characters **** sb.reverse(); // **** append to answer **** ans.append(sb); } // **** reverse the first k characters and leave the others unchanged **** else { // **** extract the sub string **** String sub = s.substring(i * twoK); // **** k first characters **** StringBuilder sb = new StringBuilder(sub.substring(0, k)); // **** reverse all characters **** sb.reverse(); // **** append remaining characters **** sb.append(sub.substring(k)); // **** append to answer **** ans.append(sb); } // **** return answer **** return ans.toString(); }

We start by performing the initialization steps.

Before we proceed, the approach we are taking is to match the description with code. We are also using the StringBuilder class.

In the loop we take case of all the complete blocks of 2 * k characters.

Once we are done, we check if we are done. If so we return the string in the `ans` StringBuilder.

We now check if we have to reverse all the remaining characters. If so we do so and append the characters to the `ans` StringBuilder. If that is not the case, we reverse the first `k` characters and append the remaining.

Yes, you are correct, we could have further optimized the code (and get additional attempts at LeetCode). Both parts of the `if then else` statement reverse the first `k` characters, but only the `else` appends the remaining characters.

Please take a look at the comments section of this approach and note the execution time and space.

/** * Given a string s and an integer k, * reverse the first k characters for every 2k characters counting from the start of the string. * * If there are fewer than k characters left, reverse all of them. * * If there are less than 2k but greater than or equal to k characters, * then reverse the first k characters and left the other as original. * * Runtime: 1 ms, faster than 77.39% of Java online submissions. * Memory Usage: 39.1 MB, less than 48.59% of Java online submissions. * * 60 / 60 test cases passed. * Status: Accepted * Runtime: 1 ms * Memory Usage: 39.1 MB * * This implementation uses a char array. */ static String reverseStr(String s, int k) { // **** initialization **** char[] ca = s.toCharArray(); // **** loop once per block of 2 * k characters **** for (int i = 0; i < ca.length; i += 2 * k) { // **** compute range to reverse characters **** int begin = i; int end = Math.min(i + k - 1, ca.length - 1); // **** reverse characters **** while (begin < end) { char tmp = ca[begin]; ca[begin++] = ca[end]; ca[end--] = tmp; } } // **** return string of character array **** return String.valueOf(ca); }

This is the second approach using a character array instead of StringBuilders.

We start by generating a char[] `ca` with the characters in the input string.

We then enter a loop that processes `2 * k` characters per pass.

We need to calculate begin and end values for the range of characters we need to reverse. Once that is done we enter a while loop that reverses the characters in the specified range.

When all is set and done we return the contents of the character array converted to a string.

This code is a lot shorter and perhaps easier to follow or perhaps not. Go back and read the requirements for this problem. In addition please read the comments section of this implementation. It seems both implementations execute is about the same time. Perhaps we so revisit the idea that StringBuilders are **slower** and loops are **faster**. BTW, do not base future decisions on a single test case. Experiment and come up with your own conclusion.

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

Regards;

John

]]>As I mentioned a few posts ago, I attempted some daily problems posted by LeetCode. At this time I am somewhat busy so I have missed a few. Hopefully in a month or two I will have enough time to work on a problem a day; at least the easy and medium. The others may take a couple days.

Today I looked at LeetCode 162 Find Peak Element.

A peak element is an element that is strictly greater than its neighbors. Given an integer array nums, find a peak element, and return its index. If the array contains multiple peaks, return the index to any of the peaks. You may imagine that nums[-1] = nums[n] = -∞. You must write an algorithm that runs in O(log n) time. Constraints: o 1 <= nums.length <= 1000 o -231 <= nums[i] <= 231 - 1 o nums[i] != nums[i + 1] for all valid i.

This looks like a modified binary search would work. Binary search meets the time complexity requirement.

We will attempt to solve this problem using the Java programming language and the VSCode IDE on a Windows computer. If you are just interested in solving the problem directly, the best approach is to use the on-line IDE provided by LeetCode. Since I am using a Windows platform, we will need to generate a test scaffold that will collect the input parameters, assign them to variables, call the function in question and display results. Please note that the test code **IS NOT PART OF THE SOLUTION**.

public int findPeakElement(int[] nums) { }

The signature for the function of interest requires a single int[] array. We need to return the index of a peek value in the array.

1,2,3,1 main <<< nums: [1, 2, 3, 1] main <<< findPeakElement: 2 1,2,1,3,5,6,4 main <<< nums: [1, 2, 1, 3, 5, 6, 4] main <<< findPeakElement: 5 1,2,3,4,5,6,7 main <<< nums: [1, 2, 3, 4, 5, 6, 7] main <<< findPeakElement: 6 7,6,5,4,3,2,1 main <<< nums: [7, 6, 5, 4, 3, 2, 1] main <<< findPeakElement: 0

The first and only input line contains the integers for the `nums` array. Our test code reads the input, generates and then displays the array. This is done to verify that all is well so far.

At that point it seems that our test code calls the function of interest, computes the result and returns its value which is then displayed.

/** * Test code. * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read nums int[] array **** int[] nums = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< nums: " + Arrays.toString(nums)); // **** call function of interest and display result **** System.out.println("main <<< findPeakElement: " + findPeakElement(nums)); }

Our test code seems to match very close our description of the test cases. I do not have much to add at this point.

/** * A peak element is an element that is strictly greater than its neighbors. * Given an integer array nums, find a peak element, and return its index. * If the array contains multiple peaks, return the index to any of the peaks. * You may imagine that nums[-1] = nums[n] = -∞. * You must write an algorithm that runs in O(log n) time. * * 63 / 63 test cases passed. * Status: Accepted * Memory Usage: 40084000 * * Eexcution: O(log(n)) Space: O(3) == O(1) */ static int findPeakElement(int[] nums) { // **** initialization *** int left = 0; int right = nums.length - 1; // **** binary search **** while (left < right) { // **** compute mid **** int mid = left + (right - left) / 2; // **** go right (line slope is positive) **** if (nums[mid] < nums[mid + 1]) left = mid + 1; // **** go left (line slope is negative) **** else right = mid; } // **** return peek index (left == right) **** return left; // or right; }

We are using a modified binary search approach.

We start by initializing a couple variables.

The main loop implements a binary search. We start by computing the mid value element in the current range in the array.

We then make the decision to go right or left. Note the expression used for the test. If the mid value in the array range is less than the next value to the right, the line has a positive slope and since we are looking for a peek (highest point) we need to use the right section so we move the left to the right. Otherwise we have encountered a negative slope and we need to go left. This is done by moving the right limit to the left by assigning mid to right.

When the condition in the while loop becomes false, then we have encountered a peek which should be the value in the left or right variables as illustrated in the code.

Information on performance has been written in the comments section of the function of interest.

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

Regards;

John

]]>Spoke with my youngest son this morning. He was heading to his office. Since the pandemic started he has been working from home like most people in the world has including me. Since the pandemic appears to be under control in many countries, including the USA, schedules are quickly changing and most people will be **full time** at the office at their place of work. Some companies will allow more flexibility than before with schedules allowing employees to work one or two days from home. Keep in mind that most studies have determined that face contact and casual meetings are associated with more productivity. Interesting fact, because most of us thought that chatting by the water cooler was a **complete waste of time**!

Today I will attempt to solve LeetCode 373. Find K Pairs with Smallest Sums. Let’s take a look at the requirements for the problem:

You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define a pair (u, v) which consists of one element from the first array and one element from the second array. Return the k pairs (u1, v1), (u2, v2), ..., (uk, vk) with the smallest sums. Constraints: o 1 <= nums1.length, nums2.length <= 104 o -109 <= nums1[i], nums2[i] <= 109 o nums1 and nums2 both are sorted in ascending order. o 1 <= k <= 1000

We are given two sorted in ascending order arrays of integers and an integer `k`.

With such information at hand we need to generate a set of pairs with the first element from the first array and the second from the second one. The idea is that the set of pairs must be able to produce the smallest sums.

We will be attempting a solution using the Java programming language and the VSCode IDE on a Windows machine. Unless you have a compelling reason not to use the on-line IDE provided by LeetCode, you should solve it on-line. That way you will not have to generate test code to collect the input data, call the function of interest and display results.

public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) { }

The signature for the function of interest follows the requirements. We are provided two int[] arrays with sorted in ascending order values and an integer value that specifies the number of pairs we need to generate.

3 1,7,11 2,4,6 main <<< k: 3 main <<< nums1: [1, 7, 11] main <<< nums2: [2, 4, 6] main <<< pairs: [[1, 6], [1, 2], [1, 4]] 2 1,1,2 1,2,3 main <<< k: 2 main <<< nums1: [1, 1, 2] main <<< nums2: [1, 2, 3] main <<< pairs: [[1, 1], [1, 1]] 3 1,2 3 main <<< k: 3 main <<< nums1: [1, 2] main <<< nums2: [3] main <<< pairs: [[2, 3], [1, 3]] 5 1,2 1,3 main <<< k: 5 main <<< nums1: [1, 2] main <<< nums2: [1, 3] main <<< pairs: [[2, 3], [1, 3], [2, 1], [1, 1]] 3 1 2,4,5,9 main <<< k: 3 main <<< nums1: [1] main <<< nums2: [2, 4, 5, 9] main <<< pairs: [[1, 5], [1, 2], [1, 4]]

Our test code reads the first three input lines. The first line contains the value for `k`. The next line holds the values for the `nums1` array and the third line the vales for the `nums2` array of integers.

Our test code seems to display the input values. I like to do so to make sure all is well before passing the values as arguments to the function of interest.

It seems that our test scaffold calls the function of interest and then displays the results.

I was not sure if we had to return the pairs in any specified order. Initially I returned them in ascending order. As you can see in the requirements, there is no specific order for the pairs, just their values. When solving problems, make sure you capture all the information and interpret it in such a way not to perform additional tasks that take time to develop and execute.

/** * Test scaffold. * * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read k **** int k = Integer.parseInt(br.readLine().trim()); // **** read nums1 array **** int[] nums1 = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** read nums2 array **** int[] nums2 = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< k: " + k); System.out.println("main <<< nums1: " + Arrays.toString(nums1)); System.out.println("main <<< nums2: " + Arrays.toString(nums2)); // **** call function of interest and display result **** System.out.println("main <<< pairs: " + kSmallestPairs(nums1, nums2, k)); }

The test code seems to closely do what we discussed for the test cases. Not much to add at this time.

/** * You are given two integer arrays nums1 and nums2 * sorted in ascending order and an integer k. * * Define a pair (u, v) which consists of one element * from the first array and one element from the second array. * * Return the k pairs (u1, v1), (u2, v2), ..., (uk, vk) * with the smallest sums. * * Runtime: 14 ms, faster than 40.74% of Java online submissions. * Memory Usage: 39.6 MB, less than 80.78% of Java online submissions. */ static List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) { // **** create priority queue with comparator **** PriorityQueue<List<Integer>> pq = new PriorityQueue<>( k, (a, b) -> ((b.get(0) + b.get(1)) - (a.get(0) + a.get(1))) ); // **** populate priority queue O(n * m) **** int maxVal = Integer.MIN_VALUE; for (int i = 0; i < nums1.length; i++) { for (int j = 0; j < nums2.length; j++) { // **** add pair to priority queue ***** if (pq.size() < k) { // **** add pair to the priority queue **** pq.add(Arrays.asList(nums1[i], nums2[j])); // **** update max value (if needed) **** if (nums1[i] + nums2[j] > maxVal) maxVal = nums1[i] + nums2[j]; } else { // **** pair smaller than max value in priority queue **** if (nums1[i] + nums2[j] < maxVal) { // **** remove head (largest value in pq) **** pq.remove(); // **** add current pair **** pq.add(Arrays.asList(nums1[i], nums2[j])); // **** get pair at head of the priority queue (largest value in pq) **** List<Integer> pair = pq.peek(); // **** update max value **** maxVal = pair.get(0) + pair.get(1); } } } } // **** return list of pairs **** return new ArrayList<>(pq); }

It made sense to me to use a priority queue. That way our values will be ordered in ascending or descending order. Using such approach, our answer would be found in the **ending** or **beginning** pairs in the priority queue. This is dependent on the comparator that we implement.

I decided to implement a comparator as a separate class which contained the two array values and the sum. After a few passes I removed the comparator class and went with a lambda expression.

We start by creating a priority queue with a specific size and comparator. Our comparator takes two pairs and computes the difference of the sums of the associated pairs. The current comparator generates a priority queue with descending values. That way we will hold no more than `k` entries in the priority queue which should match the `k` pairs we need to return as a solution.

We then enter a loop in which we generate all combinations of pairs using one value from `nums1` and the other from `nums2`.

Note that we declare a `maxVal` integer variable that we use to help us maintain the smallest value pairs in the priority queue.

Of each pair we check if the size of the priority queue is smaller than `k`. If so, we add the current pair to the priority queue updating the max value if needed.

If we determine we have `k` elements in the priority queue, then we only want to add pairs to the priority queue whose sum is **less than** the `maxVal`. Note that we want to hold in the priority queue pairs that meet the requirements for the answer.

If the current pair meets expectations, we remove the pair at the head of the queue which is associated with the `maxVal`. We remove it from the priority queue, add the current pair, get the value of the pair at the head of the priority queue and update the `maxVal` variable.

After we are done with the loop, our priority queue should hold `k` or less elements. We generate an array list with the pairs in the priority queue and return them as our answer.

The execution time and memory usage is in the comments section of the function of interest.

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

Regards;

John

]]>Earlier this morning I watched the NewScientist DEBATE “**Information and the future of defense**” presented by **BAE Systems**. It was interesting. The panel had a moderator and if I recall four contributors with very good credentials in the topic at hand.

In my humble opinion, cyberwarfare is a very serious threat brought by bad actors which may represent organizations or states. Since they can be very damaging and are constantly morphing, AI should be employed to **determine** the threat in a very short time (e.g., milliseconds), **block** it, and **bring harm** to the actors. There is an old saying, if force does not solve your problem, you are not using enough. I will not go into more detail at this time, but chatting, discussing and signing agreements between good actors will never stop bad ones.

I decided to work on LeetCode 378 Kth Smallest Element in a Sorted Matrix problem. The problem is classified as Medium. It is quite interesting because after reading and assimilating the requirements, it looked like the solution could be a modified binary search. That is easier said than done.

Given an n x n matrix where each of the rows and columns are sorted in ascending order, return the kth smallest element in the matrix. Note that it is the kth smallest element in the sorted order, not the kth distinct element. Constraints: o n == matrix.length o n == matrix[i].length o 1 <= n <= 300 o -109 <= matrix[i][j] <= 109 o All the rows and columns of matrix are guaranteed to be sorted in non-decreasing order. o 1 <= k <= n ^ 2

We are given a square matrix of n * n and a number `k`. We need to return the kth smallest element in the matrix. If you need a few minutes to put your thoughts together, this is a good time to do so. I will wait until you are ready…

…OK now that you are back let’s take a look at the **signature** of the function of interest. We are going to solve the problem using the Java programming language and the VSCode IDE on a Windows computer.

public int kthSmallest(int[][] matrix, int k) { }

We are provided the sorted matrix and the value of the kth element we need to locate in the matrix.

8 1,5,9 10,11,13 12,13,15 main <<< k: 8 main <<< n: 3 main <<< matrix: [1, 5, 9] [10, 11, 13] [12, 13, 15] main <<< kthSmallest0: 13 main <<< kthSmallest: 13 1 -5 main <<< k: 1 main <<< n: 1 main <<< matrix: [-5] main <<< kthSmallest0: -5 main <<< kthSmallest: -5 3 10,20,30,40 15,25,35,45 24,29,37,48 32,33,39,50 main <<< k: 3 main <<< n: 4 main <<< matrix: [10, 20, 30, 40] [15, 25, 35, 45] [24, 29, 37, 48] [32, 33, 39, 50] main <<< kthSmallest0: 20 main <<< kthSmallest: 20

The first input line contains the value for `k`. The following few lines contain the contents of the matrix. Note that we are dealing with square matrices.

Our test code, which **IS NOT PART OF THE SOLUTION**, reads and display the value of `k`. It then figures the value of `n` and displays it on the screen.

The program then reads and displays the contents of the int[][] `matrix`.

At this point it seems that two implementations of the function of interest are called. Both seem to return the same results.

/** * Test scaffold * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new java.io.InputStreamReader(System.in)); // **** read k **** int k = Integer.parseInt(br.readLine().trim()); // **** read first line for matrix **** String[] line = br.readLine().trim().split(","); // **** extract value for n **** int n = line.length; // **** declare matrix **** int[][] matrix = new int[n][n]; matrix[0] = Arrays.stream(line).mapToInt(Integer::parseInt).toArray(); // **** populate the rest of the matrix **** for (int i = 1; i < n; i++) { matrix[i] = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); } // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< k: " + k); System.out.println("main <<< n: " + n); System.out.println("main <<< matrix:"); for (int i = 0; i < n; i++) System.out.println(Arrays.toString(matrix[i])); // ???? ???? // for (int i = 0; i < 17; i++) { // // ???? ???? // Random rand = new Random(); // // ???? generate mid ???? // int mid = matrix[0][0] + rand.nextInt(matrix[n - 1][n - 1] - matrix[0][0] + 1); // if (mid < matrix[0][0] || mid > matrix[n - 1][n - 1]) { // System.err.println("main <<< invalid mid: " + mid); System.exit(-1); // } // // ???? compute count of mid ???? // System.out.println("main <<< count(" + mid + "): " + count0(matrix, mid)); // } // **** call function of interest and display result **** System.out.println("main <<< kthSmallest0: " + kthSmallest0(matrix, k)); // **** call function of interest and display result **** System.out.println("main <<< kthSmallest: " + kthSmallest(matrix, k)); }

The code for the test scaffold seems to follow the description to process a test case.

Note that there is some code commented out. This was done to test the count0() function. We will learn more about the actual function when we go over its associated code.

/** * Given an n x n matrix where each of the rows and columns are sorted in ascending order, * return the kth smallest element in the matrix. * * Note that it is the kth smallest element in the sorted order, not the kth distinct element. * * 85 / 85 test cases passed. * Status: Accepted * Runtime: 0 ms * Memory Usage: 44.7 MB */ static int kthSmallest0 (int[][] matrix, int k) { // **** sanity check(s) **** if (k == 1) return matrix[0][0]; // **** initialization **** int n = matrix.length; // for ease of use int low = matrix[0][0]; int high = matrix[n - 1][n - 1]; // **** binary search **** while (low < high) { // **** compute mid value **** int mid = (high - low) / 2 + low; // **** update high or low value **** if (count0(matrix, mid) >= k) high = mid; else low = mid + 1; } // **** return low **** return low; }

This is the first implementation of the function of interest.

We start by performing a sanity check. This particular test checks for the case when ‘k’ is equal to one. It returns the lowest value in the `matrix` which should be at coordinates [0][0].

We then perform some initializations.

The while loop implements the binary search.

We first compute the mid value. Once we have it, we update the low or high value.

When all is said and done, we return the low value.

Hopefully you have noted the close resemblance of this code to a traditional binary search. If in doubt you can refer to the Binary Search in Java post in this blog.

/** * Count the # of values that is less than or equal to mid. * * Time: O(n ^ 2) - Space: O(1) */ static private int count0 (int[][] matrix, int mid) { // **** initialization **** int count = 0; int n = matrix.length; // for ease of use int col = n - 1; // **** traverse rows (top to bottom) - O(n)**** for (int row = 0; row < n; row++) { // **** traverse columns (right to left) - O(n) **** for ( ; col >= 0; col--) { // **** increment count (if needed) **** if (matrix[row][col] <= mid) { // **** increment count **** count += (col + 1); // **** exit inner loop **** break; } } } // **** return count **** return count; }

As we noted in the function of interest, we make a call to the count0() function to determine the way we proceed with the binary search.

We start by initializing a set of variables.

In the first loop we search the rows from top to bottom.

In the inner loop, we search the columns for the specified row right to left. This is done because each row is ordered in ascending order.

If the current value in the `matrix` is <= `mid` we increment the count of values we have so far and exit the inner loop.

When all is said and done, we return the count.

/** * Given an n x n matrix where each of the rows and columns are sorted in ascending order, * return the kth smallest element in the matrix. * * Note that it is the kth smallest element in the sorted order, not the kth distinct element. * * 85 / 85 test cases passed. * Status: Accepted * Runtime: 0 ms * Memory Usage: 44.7 MB */ static int kthSmallest (int[][] matrix, int k) { // **** sanity check(s) **** if (k == 1) return matrix[0][0]; // **** initialization **** int n = matrix.length; // for ease of use int low = matrix[0][0]; int high = matrix[n - 1][n - 1]; // **** perform binary search **** while (low <= high) { // **** compute mid **** int mid = low + (high - low) / 2; // **** update high or low **** if (check(matrix, mid, k, n)) high = mid - 1; else low = mid + 1; } // **** return low **** return low; }

This is the second implementation of the function of interest. It is quite similar to the first one. We start by performing a sanity check and then the initialization step. We then enter the loop which performs the binary search. We compute the value for the `mid` point. Based on the value returned by the check() function we update the low or the high value. When all is said and done we return the low value. This is quite similar to the previous implementation.

/** * Check to update high (return true) or low (return false) value. */ private static boolean check (int[][] matrix, int mid, int k, int n) { // **** initialization **** int col = n - 1; int row = 0; int num = 0; // **** move in the matrix updating the num **** while (col >= 0 && row < n) { if (matrix[col][row] <= mid) { num += (col + 1); row++; } else col--; } // **** return true (updte high) or false (update low) **** return num >= k; }

This function is used to determine in which direction we move after computing the `mid` value.

We start by performing the initialization step.

In the while loop, we move inside the `matrix` until the `num` value can be updated. At that time we return the Boolean value for the `num >= k` relation. This indicated the **direction** in which the binary search will continue.

Note that both functions have passed all the tests and the times and space are comparable.

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

Regards;

John

]]>Last night rain did wonders for lawns. They are starting to look very green and lush. It seems that on Friday we might get some additional rain which is good for farmers and lawns.

Over the long weekend my wife and I prepared bruschettas. Coming from Italian parents, while growing up, I consumed my fair share of this antipasto.

In the past few years we have prepared them now and then. This past long weekend my wife and I decided to prepare different flavored bruschettas before lunch. They were **simple** to prepare and we had them with beer, red and white wine and Prosecco. **Prosecco** is the champagne of Italy as **Cava** is the champagne of Spain. The change in names was **required** after the French **patented** the name champagne **only** to be used for sparkling wine produced in some parts of France.

You can use any bread, slice it and toast it until it gets a light golden brown color. We prefer to use **Ciabatta **or **Focaccia **bread. Yes, my family comes from the Ligurian region in Italy.

We get packages of four Ciabatta rolls at Trader Joe’s. We keep them in the fridge. With a serrated knife we slice them diagonally with a thickness of ½ inch or so. We put them in a convection oven at **450F** until they turn **light golden brown**. My mouth is starting to water thinking about how good these things are.

We also get at Trader Joe’s a variety of jars of **tapenades**. They are reasonably priced and the flavors are incredible.

Once the bread is done, we pull it out of the oven and moved the pieces to a serving platter. We get a couple jars of tapenade and two spoons that are used to get about a full spoon of tapenade on to a piece of bread. Open your mouth wide and place the bruschetta in it. If you try them, please leave me a comment. I think they taste extremely good!!!

Now let’s get into the main subject of this post. I have been looking at the daily problems in LeetCode. I was under the impression that you had to solve multiples each day. I was mistaken. You should only solve one per day. That is a lot more reasonable.

For this post I tackled LeetCode 89 Gray Code which brought me some memories. You can find more about Gray Code here. Let’s take a look at the problem description.

An n-bit gray code sequence is a sequence of 2^n integers where: o Every integer is in the inclusive range [0, 2^n - 1], o The first integer is 0, o An integer appears no more than once in the sequence, o The binary representation of every pair of adjacent integers differs by exactly one bit, and o The binary representation of the first and last integers differs by exactly one bit. Given an integer n, return any valid n-bit gray code sequence. Constraints: o 1 <= n <= 16

The description is correct but it matches just the “reflected binary Gray code”. If you are not satisfied with the Wikipedia article, I suggest reading **chapter 13, Gray Code in the book Hacker’s Delight Second Edition by Henry S. Warren Jr**.

We will solve this problem using the Java programming language and the VSCode IDE on a Windows computer. When you deal with bits and bytes, in general, I prefer to use C or C++, but for now Java is the go to language.

public List<Integer> grayCode(int n) { }

The signature for the function of interest is simple. We are given an integer in the specified range and we need to return in a list the complete set of reflected binary Gray codes. The LeetCode web site has two examples when n = 2 and n = 1.

1 main <<< n: 1 main <<< grayCode0: [0, 1] main <<< grayCode1: [0, 1] main <<< grayCode2: [0, 1] main <<< grayCode3: [0, 1] main <<< grayCode: [0, 1] 2 main <<< n: 2 main <<< grayCode0: [0, 1, 3, 2] main <<< grayCode1: [0, 1, 3, 2] main <<< grayCode2: [0, 1, 3, 2] main <<< grayCode3: [0, 1, 3, 2] main <<< grayCode: [0, 1, 3, 2] 3 main <<< n: 3 main <<< grayCode0: [0, 1, 3, 2, 6, 7, 5, 4] main <<< grayCode1: [0, 1, 3, 2, 6, 7, 5, 4] main <<< grayCode2: [0, 1, 3, 2, 6, 7, 5, 4] main <<< grayCode3: [0, 1, 3, 2, 6, 7, 5, 4] main <<< grayCode: [0, 1, 3, 2, 6, 7, 5, 4] 4 main <<< n: 4 main <<< grayCode0: [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8] main <<< grayCode1: [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8] main <<< grayCode2: [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8] main <<< grayCode3: [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8] main <<< grayCode: [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8]

The first is the input line. It contains the value for `n`. Our test code, which **IS NOT PART OF THE SOLUTION**, reads the input line and displays the value for `n`. At that point it seems to call five implementations which seem to return the same values.

Please take a few moments verifying that the values match the requirements.

/** * Test code. * @throws IOException */ public static void main(String[] args) throws IOException { // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read `n` **** int n = Integer.parseInt(br.readLine().trim()); // **** close buffered reader **** br.close(); // **** enforce constraints **** if (n < 1) n = 1; if (n > 16) n = 16; // ???? ???? System.out.println("main <<< n: " + n); // **** call function of interest and display results **** System.out.println("main <<< grayCode0: " + grayCode0(n)); // **** call function of interest and display results **** System.out.println("main <<< grayCode1: " + grayCode1(n)); // **** call function of interest and display results **** System.out.println("main <<< grayCode2: " + grayCode2(n)); // **** call function of interest and display results **** System.out.println("main <<< grayCode3: " + grayCode3(n)); // **** call function of interest and display results **** System.out.println("main <<< grayCode: " + grayCode(n)); }

Our test code reads the value for `n`, makes sure that it is within range, and displays it.

The code then calls five different implementations which we will look in detail shortly.

/** * Steps: * * o Let the list of (n-1)-bit Gray codes be L1. Create another list L2 which is reverse of L1. * o Modify the list L1 by prefixing a ‘0’ in all codes of L1. * o Modify the list L2 by prefixing a ‘1’ in all codes of L2. * o Concatenate L1 and L2. The concatenated list is required list of n-bit Gray codes. * * Runtime: 271 ms, faster than 5.04% of Java online submissions. * Memory Usage: 47.9 MB, less than 23.57% of Java online submissions. */ static List<Integer> grayCode0(int n) { // **** sanity check(s) **** if (n == 1) return Arrays.asList(new Integer[] {0, 1}); // **** initialization **** List<String> gcl = new ArrayList<String>(); List<Integer> gcli = new ArrayList<Integer>(); List<String> l1 = new ArrayList<String>(); List<String> l2 = new ArrayList<String>(); // **** loop once per set **** for (int i = 1; i < n; i++) { // **** populate l1 **** if (l1.size() == 0) { l1.add("0"); l1.add("1"); } else l1 = new ArrayList<String>(gcl); // **** populate l2 (reverse of l1) **** l2 = new ArrayList<String>(l1); Collections.reverse(l2); // **** modify l1 (prefixing `0`) **** for (int j = 0; j < l1.size(); j++) { // **** **** String str = l1.get(j); // **** replace this entry in l1 **** l1.remove(j); l1.add(j, "0" + str); } // **** modify l2 (prefixing `1`) **** for (int j = 0; j < l2.size(); j++) { // **** **** String str = l2.get(j); // **** replace this entry **** l2.remove(j); l2.add(j, "1" + str); } // **** concatenate l2 to l1 **** gcl = new ArrayList<String>(l1); gcl.addAll(l2); } // **** convert String to Integer **** for (int i = 0; i < gcl.size(); i++) gcli.add(i, Integer.parseInt(gcl.get(i), 2)); // **** return Integer list **** return gcli; }

The steps to generate the reflected binary Gray code are listed. You can get a true flavor for the use of the “**reflected**” word in their description.

We start with a sanity check.

We then initialize a set of array lists. They will be used to generate a list in binary and then convert it to integer before returning the results.

We enter the main loop. We will loop once per set.

We populate `l1`. If empty, we populate it with the seed values “0’ and “1”. If `l1` is not empty we use the gray codes from the previous pass.

We now populate `l2` with the reversed contents of `l1`. This operation doubles the number of entries per set.

We then set in the first list a prefix of “0” for each entry and in the second list we use a prefix of “1”. Take a look at the example for `n` = 2 and you should be able to follow these two steps.

We now concatenate the lists and add it to the `gcl` list.

The main loop repeats based on the value of `n`.

When we exit the main loop, we need to convert the Strings to binary values. Once the `gcli` list has been populated, we return from the function.

Slow but illustrates the individual steps.

/** * Same as previous pass but optimiing the steps inside the main loop. * * Runtime: 32 ms, faster than 11.86% of Java online submissions. * Memory Usage: 47 MB, less than 27.58% of Java online submissions. * * Time: O(2 * n) - Space: O(2 * n) */ static List<Integer> grayCode1(int n) { // ***** sanity check(s) **** if (n == 1) return Arrays.asList(new Integer[] {0, 1}); // **** initialization **** List<String> gcl = new ArrayList<String>(); List<Integer> gcli = new ArrayList<Integer>(); // **** prime gcl **** gcl.add("0"); gcl.add("1"); // **** copy gcl and append "0" to first and "1" to second half **** int i, j; for (i = 1; i < n; i++) { // **** append first part of gcl in reverse order **** for (j = gcl.size() - 1; j >= 0; j--) gcl.add(gcl.get(j)); // **** append "0" to first half of gcl **** for (j = 0; j < gcl.size() / 2; j++) gcl.set(j, "0" + gcl.get(j)); // **** append "1" to second half of gcl **** for ( ; j < gcl.size(); j++) gcl.set(j, "1" + gcl.get(j)); } // **** convert String to Integer **** for (i = 0; i < gcl.size(); i++) gcli.add(i, Integer.parseInt(gcl.get(i), 2)); // **** return Integer list **** return gcli; }

In this pass we will try to optimize the steps we performed in the previous implementation.

We start with performing a sanity check. If `n` == 1 we know that we need to return [0, 1].

We will only use two instead of four array lists. We will work with the binary strings on a single array list. We have to convert the Strings to Integers using as a target the second array list.

We start by priming the list with the values of “0” and “1”.

In the main loop we append to the list all the existing values in **reverse** order. To the **first** appended half we prefix each entry with a “0” and for the **second** half we prefix the entries with a “1”.

The loop repeats until we have all the entries we require.

At that time, we repeat the process we did in the previous implementation which is to generate a list with integer values. The list is then returned to the caller.

Note that we did the same operations as in the previous pass but we did them in a more efficient way. This is illustrated by the execution times listed in the comments sections in both implementations.

/** * Entry point for recursive function. * * Runtime: 33 ms, faster than 11.39% of Java online submissions. * Memory Usage: 46.8 MB, less than 28.25% of Java online submissions. * * Time: O(2 * n) - Space: O(2 * n) */ static List<Integer> grayCode2(int n) { // ***** sanity check(s) **** if (n == 1) return Arrays.asList(new Integer[] {0, 1}); // **** initialization **** List<Integer> gcli = new ArrayList<Integer>(); // **** start recursion **** ArrayList<String> gcl = grayCodeRec(n); // **** convert String to Integer **** for (int i = 0; i < gcl.size(); i++) gcli.add(i, Integer.parseInt(gcl.get(i), 2)); // **** return Integer list **** return gcli; }

We are now attempting a recursive approach. We start by performing a sanity check as before.

We initialize a list in which the String values will be collected.

A recursive call is invoked passing as argument the `n`. The function returns the list of Strings which we will convert to a list of integers as we did in the previous implementations.

/** * Recursive function. */ static ArrayList<String> grayCodeRec(int n) { // **** base case(s) **** if (n == 0) return new ArrayList<>(){ { add("0"); } }; if (n == 1) return new ArrayList<>(){ { add("0"); add("1"); } }; // **** initialization **** int j; // **** recursive call **** ArrayList<String> gcl = grayCodeRec(n - 1); // **** append first part of gcl in reverse order **** for (j = gcl.size() - 1; j >= 0; j--) gcl.add(gcl.get(j)); // **** append "0" to first half of gcl **** for (j = 0; j < gcl.size() / 2; j++) gcl.set(j, "0" + gcl.get(j)); // **** append "1" to second half of gcl **** for ( ; j < gcl.size(); j++) gcl.set(j, "1" + gcl.get(j)); // **** return gcl **** return gcl; }

This is the recursive call. We start by stating our base cases. The first addresses the case where n == 0 and the second when n == 1.

We then declare the variable that we will use to index the following three loops.

We make a recursive call to fill in the list with n – 1. Note that when all the recursive calls are made we will start with a list containing [“0”, “1”] which is where we started in all the previous implementations.

From then on we need to append, and prefix the lists.

The first loop appends the values in reverse order.

The second loop prefixes a “0” to the first half of the reversed list.

The third loop prefixes a “1” to the second half of the reversed list.

The updated list which has doubled its size is returned.

The process repeats until we get the results for the specified ‘n’.

Back into the entry call, we convert the String values in the returned list by the recursive call to a list of integers. The list of integers is returned to the caller.

Not much has been gained by using recursion as illustrated in the comments section of the entry function.

/** * Page 315 of Hacker's Delight Second Edition by Henry S.Warren, Jr. * * Runtime: 7 ms, faster than 33.02% of Java online submissions. * Memory Usage: 46.3 MB, less than 60.16% of Java online submissions. */ static ArrayList<Integer> grayCode3(int n) { // **** initialization **** ArrayList<Integer> gcli = new ArrayList<>(){ { add(0); add(1); } }; // **** sanity checks **** if (n == 1) return gcli; // **** loop generating the Gray codes O(2 ^ n - 1) **** for (int i = 2; i < (1 << n); i++) { // **** get previous gray code **** int gc = gcli.get(i - 1); // **** **** int l = Integer.lowestOneBit(gc); // **** parity **** int p = Integer.bitCount(gc); // **** proceed based on the parity bit of the **** if (p % 2 == 0) { // even parity gc ^= 1; // invert rightmost bit } else { // odd parity int mask = l; mask <<= 1; // shift left once gc ^= mask; } // **** add gray code to list **** gcli.add(gc); } // **** return gray code list **** return gcli; }

The approach in this function comes from the Hacker’s Delight book shown in the comments section.

We are now dealing with Integers instead of binary string representations. We start by initializing the list.

We then perform a sanity check and return our initialized list if needed.

We then enter a loop generating Gray codes.

First we recall the previous Gray code. In that Gray code we get the location of the lowest `1` bit followed by the parity of the Gray code.

If the parity of the Gray code is **even**, we invert the value of the rightmost bit; otherwise if the parity is odd we invert the value next to the lowest ‘1’ bit in the Gray code.

We then add the newly created Gray code to the list.

When done, we return the list of binary Gray codes.

Using this algorithm, we can generate the Gray codes faster than in all the previous implementations.

/** * Page 314 of Hacker's Delight Second Edition by Henry S.Warren, Jr. * * Runtime: 3 ms, faster than 96.35% of Java online submissions. * Memory Usage: 46.3 MB, less than 60.16% of Java online submissions. */ static ArrayList<Integer> grayCode(int n) { // **** initialization **** ArrayList<Integer> gcli = new ArrayList<Integer>(); // **** loop generating gray code values using formula **** for (int i = 0; i < (1 << n); i++) gcli.add(i ^ (i >> 1)); // **** return gray code list **** return gcli; }

On this last implementation, we will use a **different** approach that is also found in the Hacker’s Delight book shown in the comments section of the function.

We initialize an array list.

We then enter a loop that generates all the Gray codes associated with the specified value of `n`.

The values are inserted into the list. When done the list is returned.

Note that this is the fastest implementation that we will attempt in this post. If you check in LeetCode, there are a couple faster implementations. They both seem to be quite complex.

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

Regards;

John

]]>On a separate note, it appears that the UPS attached to one of my computers is starting to fail. One of the internal batteries does not seem to be charging as it should. I guess it is time to replace the unit. I believe I purchased the UPS about 10 years ago. OK, I just ordered a **replacement** on Amazon. The UPS should arrive on **Friday **this week.

My wife is calling me to go out grocery shopping. I **will** finish this post tomorrow morning…

… I am back. It is Saturday morning. After breakfast, my wife and I went out for a walk. The high temperature for the day will be in the low 90s F. Glad we are done walking. In a couple hours or so we will be heading out to get some groceries. After that we need to do some travel planning. It is already July 03. Shortly the temperature will drop and fall will be here.

In this post we will take a look at LeetCode 366 Find Leaves of Binary Tree which is part of the daily July coding challenge. During the next two months including July, I will try to solve as many problems as I can. Due to other commitments I will not be ready to give my full daily attention until the month of September 2021.

Given the root of a binary tree, collect a tree's nodes as if you were doing this: o Collect all the leaf nodes. o Remove all the leaf nodes. o Repeat until the tree is empty.

The requirements are somewhat **misleading**. The first step is to collect leaf nodes. The second step states that we should remove such nodes from the binary tree. We then need to repeat the two previous steps until the binary tree is empty.

/** * 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 List<List<Integer>> findLeaves(TreeNode root) { } }

We must use the TreeNode class provided by LeetCode. This class is typically used in binary tree problems.

The signature for the function of interest receives as argument the root of the binary tree and returns a List<List<Integer>> representing the leave nodes that have been deleted on each pass. It makes sense.

I will not be using the on-line IDE provided by LeetCode. I will be using the Java programming language and the VSCode IDE on a Windows computer. The reason is to have both the **test code** and **solution** in one package that I can run locally without having access to the Internet. Because of this we will generate some test code which **IS NOT PART OF THE SOLUTION**.

main <<< strArr.length: 1 main <<< strArr: [] main <<< arr: [null] main <<< bst.inOrder: main <<< bst.levelOrderTraversal: [] main <<< findLeaves: [] 1 main <<< strArr.length: 1 main <<< strArr: [1] main <<< arr: [1] main <<< bst.inOrder: 1 main <<< bst.levelOrderTraversal: [[1]] findLeaves <<< leaves: [1] main <<< findLeaves: [[1]] 1,2 main <<< strArr.length: 2 main <<< strArr: [1, 2] main <<< arr: [1, 2] main <<< bst.inOrder: 2 1 main <<< bst.levelOrderTraversal: [[1], [2]] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< leaves: [2] findLeaves <<< val: 1 findLeaves <<< leaves: [1] main <<< findLeaves: [[2], [1]] 1,2,3 main <<< strArr.length: 3 main <<< strArr: [1, 2, 3] main <<< arr: [1, 2, 3] main <<< bst.inOrder: 2 1 3 main <<< bst.levelOrderTraversal: [[1], [2, 3]] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 3 findLeaves <<< leaves: [2, 3] findLeaves <<< val: 1 findLeaves <<< leaves: [1] main <<< findLeaves: [[2, 3], [1]] 1,2,3,4,5 main <<< strArr.length: 5 main <<< strArr: [1, 2, 3, 4, 5] main <<< arr: [1, 2, 3, 4, 5] main <<< bst.inOrder: 4 2 5 1 3 main <<< bst.levelOrderTraversal: [[1], [2, 3], [4, 5]] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 4 findLeaves <<< val: 5 findLeaves <<< val: 3 findLeaves <<< leaves: [4, 5, 3] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< leaves: [2] findLeaves <<< val: 1 findLeaves <<< leaves: [1] main <<< findLeaves: [[4, 5, 3], [2], [1]] 1,2,3,null,4,null,null,5,null,null,null main <<< strArr.length: 11 main <<< strArr: [1, 2, 3, null, 4, null, null, 5, null, null, null] main <<< arr: [1, 2, 3, null, 4, null, null, 5, null, null, null] main <<< bst.inOrder: 2 5 4 1 3 main <<< bst.levelOrderTraversal: [[1], [2, 3], [4], [5]] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 4 findLeaves <<< val: 5 findLeaves <<< val: 3 findLeaves <<< leaves: [5, 3] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 4 findLeaves <<< leaves: [4] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< leaves: [2] findLeaves <<< val: 1 findLeaves <<< leaves: [1] main <<< findLeaves: [[5, 3], [4], [2], [1]] 1,2,3,4,5,6,7 main <<< strArr.length: 7 main <<< strArr: [1, 2, 3, 4, 5, 6, 7] main <<< arr: [1, 2, 3, 4, 5, 6, 7] main <<< bst.inOrder: 4 2 5 1 6 3 7 main <<< bst.levelOrderTraversal: [[1], [2, 3], [4, 5, 6, 7]] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 4 findLeaves <<< val: 5 findLeaves <<< val: 3 findLeaves <<< val: 6 findLeaves <<< val: 7 findLeaves <<< leaves: [4, 5, 6, 7] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 3 findLeaves <<< leaves: [2, 3] findLeaves <<< val: 1 findLeaves <<< leaves: [1] main <<< findLeaves: [[4, 5, 6, 7], [2, 3], [1]] 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 main <<< strArr.length: 15 main <<< strArr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] main <<< arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] main <<< bst.inOrder: 8 4 9 2 10 5 11 1 12 6 13 3 14 7 15 main <<< bst.levelOrderTraversal: [[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14, 15]] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 4 findLeaves <<< val: 8 findLeaves <<< val: 9 findLeaves <<< val: 5 findLeaves <<< val: 10 findLeaves <<< val: 11 findLeaves <<< val: 3 findLeaves <<< val: 6 findLeaves <<< val: 12 findLeaves <<< val: 13 findLeaves <<< val: 7 findLeaves <<< val: 14 findLeaves <<< val: 15 findLeaves <<< leaves: [8, 9, 10, 11, 12, 13, 14, 15] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 4 findLeaves <<< val: 5 findLeaves <<< val: 3 findLeaves <<< val: 6 findLeaves <<< val: 7 findLeaves <<< leaves: [4, 5, 6, 7] findLeaves <<< val: 1 findLeaves <<< val: 2 findLeaves <<< val: 3 findLeaves <<< leaves: [2, 3] findLeaves <<< val: 1 findLeaves <<< leaves: [1] main <<< findLeaves: [[8, 9, 10, 11, 12, 13, 14, 15], [4, 5, 6, 7], [2, 3], [1]] -64,12,18,-4,-53,null,76,null,-51,null,null,-93,3,null,-31,47,null,3,53,-81,33,4,null,-51,-44,-60,11,null,null,null,null,78,null,-35,-64,26,-81,-31,27,60,74,null,null,8,-38,47,12,-24,null,-59,-49,-11,-51,67,null,null,null,null,null,null,null,-67,null,-37,-19,10,-55,72,null,null,null,-70,17,-4,null,null,null,null,null,null,null,3,80,44,-88,-91,null,48,-90,-30,null,null,90,-34,37,null,null,73,-38,-31,-85,-31,-96,null,null,-18,67,34,72,null,-17,-77,null,56,-65,-88,-53,null,null,null,-33,86,null,81,-42,null,null,98,-40,70,-26,24,null,null,null,null,92,72,-27,null,null,null,null,null,null,-67,null,null,null,null,null,null,null,-54,-66,-36,null,-72,null,null,43,null,null,null,-92,-1,-98,null,null,null,null,null,null,null,39,-84,null,null,null,null,null,null,null,null,null,null,null,null,null,-93,null,null,null,98 main <<< strArr.length: 194 main <<< strArr: [-64, 12, 18, -4, -53, null, 76, null, -51, null, null, -93, 3, null, -31, 47, null, 3, 53, -81, 33, 4, null, -51, -44, -60, 11, null, null, null, null, 78, null, -35, -64, 26, -81, -31, 27, 60, 74, null, null, 8, -38, 47, 12, -24, null, -59, -49, -11, -51, 67, null, null, null, null, null, null, null, -67, null, -37, -19, 10, -55, 72, null, null, null, -70, 17, -4, null, null, null, null, null, null, null, 3, 80, 44, -88, -91, null, 48, -90, -30, null, null, 90, -34, 37, null, null, 73, -38, -31, -85, -31, -96, null, null, -18, 67, 34, 72, null, -17, -77, null, 56, -65, -88, -53, null, null, null, -33, 86, null, 81, -42, null, null, 98, -40, 70, -26, 24, null, null, null, null, 92, 72, -27, null, null, null, null, null, null, -67, null, null, null, null, null, null, null, -54, -66, -36, null, -72, null, null, 43, null, null, null, -92, -1, -98, null, null, null, null, null, null, null, 39, -84, null, null, null, null, null, null, null, null, null, null, null, null, null, -93, null, null, null, 98] main <<< arr: [-64, 12, 18, -4, -53, null, 76, null, -51, null, null, -93, 3, null, -31, 47, null, 3, 53, -81, 33, 4, null, -51, -44, -60, 11, null, null, null, null, 78, null, -35, -64, 26, -81, -31, 27, 60, 74, null, null, 8, -38, 47, 12, -24, null, -59, -49, -11, -51, 67, null, null, null, null, null, null, null, -67, null, -37, -19, 10, -55, 72, null, null, null, -70, 17, -4, null, null, null, null, null, null, null, 3, 80, 44, -88, -91, null, 48, -90, -30, null, null, 90, -34, 37, null, null, 73, -38, -31, -85, -31, -96, null, null, -18, 67, 34, 72, null, -17, -77, null, 56, -65, -88, -53, null, null, null, -33, 86, null, 81, -42, null, null, 98, -40, 70, -26, 24, null, null, null, null, 92, 72, -27, null, null, null, null, null, null, -67, null, null, null, null, null, null, null, -54, -66, -36, null, -72, null, null, 43, null, null, null, -92, -1, -98, null, null, null, null, null, null, null, 39, -84, null, null, null, null, null, null, null, null, null, null, null, null, null, -93, null, null, null, 98] main <<< bst.inOrder: -4 -51 -81 -31 33 12 -53 -64 18 78 4 47 -93 76 8 -35 -67 -38 -51 73 -33 3 -54 86 -66 -38 -37 -36 81 -31 -72 -42 80 -85 47 98 43 -31 -40 44 70 -92 -96 -93 -1 -26 -98 -19 -88 -64 24 -18 -91 67 10 12 34 92 48 72 72 -27 98 39 -55 -90 -17 3 -77 -30 72 -24 26 -44 -59 -81 -70 56 90 -84 -67 -65 -49 -88 -34 -53 17 37 3 -4 -11 -31 -51 -60 67 27 53 60 11 74 main <<< bst.levelOrderTraversal: [[-64], [12, 18], [-4, -53, 76], [-51, -93, 3], [-31, 47, 3, 53], [-81, 33, 4, -51, -44, -60, 11], [78, -35, -64, 26, -81, -31, 27, 60, 74], [8, -38, 47, 12, -24, -59, -49, -11, -51, 67], [-67, -37, -19, 10, -55, 72, -70, 17, -4], [3, 80, 44, -88, -91, 48, -90, -30, 90, -34, 37], [73, -38, -31, -85, -31, -96, -18, 67, 34, 72, -17, -77, 56, -65, -88, -53], [-33, 86, 81, -42, 98, -40, 70, -26, 24, 92, 72, -27, -67], [-54, -66, -36, -72, 43, -92, -1, -98, 39, -84], [-93, 98]] main <<< findLeaves: [[-81, 33, -53, 78, 8, -67, -33, -54, -66, -36, -72, -85, 43, -40, -92, -93, -98, -88, 24, 67, 92, 72, 98, -17, -77, -59, 56, -84, -88, -53, 37, -4, -51, 67, 60, 74], [-31, 4, -38, 73, 86, 81, -42, 98, 70, -1, -18, 34, 39, -90, -30, -67, -34, -11, 27, 11], [-51, 47, -35, -38, -31, -31, -26, -91, -27, 72, -65, 17, -31], [-4, -93, 3, 80, -96, 10, 72, -24, 90, -60], [12, -37, 44, 48, 26, -70, 53], [-19, -55, -49], [47, 12, -81], [-64, -44], [-51], [3], [3], [76], [18], [-64]]

There is a single input line for each test case. It represents the nodes in a binary tree.

Our test code reads the input line and seems to put the values in a string array. The length of the array is displayed followed by the actual contents of the array. The values seem to match.

Apparently our test code builds a binary tree and a BST in-order traversal is performed. The results are displayed.

A DFT is then performed and the results are displayed. This test case is the one used by LeetCode. You can see a diagram of the binary tree on the LeetCode web site.

Apparently our solution implements some type of DFS traversal to select the leave nodes. It seems that we can prune the sample binary tree in three passes.

When all is set and done the results are displayed.

Initially I went with an approach that removed the nodes and was based on DFS traversal. It passes 66 of 67 test cases. It failed the last test case. Not checking other test cases, my approach was dependent on not having nodes with the same value. Sadly, at least the **last** test case had enough **duplicate** values to cause my approach to fail.

/** * Test scaffold * !!! NOT PART OF SOLUTION !!! * * @throws IOException */ public static void main(String[] args) throws IOException { // **** initialization **** // HashSet<Integer> hs = new HashSet<Integer>(); // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** create String[] with values for the BST **** String[] strArr = br.readLine().trim().split(","); // **** close buffere reader **** br.close(); // ???? ???? System.out.println("main <<< strArr.length: " + strArr.length); System.out.println("main <<< strArr: " + Arrays.toString(strArr)); // **** generate an Integer[] to build the binary tree **** Integer[] arr = new Integer[strArr.length]; for (int i = 0; i < strArr.length; i++) { if (strArr[i].equals("null") || strArr[i].isEmpty()) arr[i] = null; else arr[i] = Integer.parseInt(strArr[i]); } // ???? ???? System.out.println("main <<< arr: " + Arrays.toString(arr)); // // **** check and count duplicate values **** // int duplicateValues = 0; // for (int i = 0; i < arr.length; i++) { // // **** skip null values **** // if (arr[i] == null) // continue; // // **** **** // if (hs.contains(arr[i]) == true) // System.out.println("main <<< arr[" + i + "]: " + arr[i] // + " duplicateValues: " + ++duplicateValues); // else // hs.add(arr[i]); // } // **** create and populate the binary tree **** BST bst = new BST(); bst.root = bst.populate(arr); // ???? ???? System.out.println("main <<< bst.inOrder: " + bst.inOrder(bst.root)); // ???? ???? System.out.print("main <<< bst.levelOrderTraversal: "); System.out.println(bst.levelOrderTraversal(bst.root).toString()); // **** compute and display result **** // System.out.println("main <<< findLeaves0: " + findLeaves0(bst.root)); // **** compute and display result **** System.out.println("main <<< findLeaves: " + findLeaves(bst.root)); }

The test code seems to match the description used to describe the test cases.

Note that at some point I added a hash map to **count duplicate values**. This was to verify that my original approach was **flawed** and only worked when the binary tree had unique values.

I apologize but the code for my first pass was lost. As soon as I determined that it was not going to work I moved into a second pass without saving the first attempt.

The function of interest is called at the end of the test code. The results are displayed.

The functions to create and display binary trees are included in separate files in the GitHub repository for this post. They are **NOT PART OF THE SOLUTION**.

There is a slower second pass that we will not cover at this time. If interested you can find the two associated functions in the GitHub repository associated with this post.

/** * Given the root of a binary tree, collect a tree's nodes as if you were doing this: * * o Collect all the leaf nodes. * o Remove all the leaf nodes. * o Repeat until the tree is empty. * * Runtime: O(n) Space: O(n) * * Runtime: 0 ms, faster than 100.00% of Java online submissions. * Memory Usage: 37.3 MB, less than 79.87% of Java online submissions. */ static List<List<Integer>> findLeaves(TreeNode root) { // **** initialization **** List<List<Integer>> lol = new ArrayList<>(); // **** loop until we have no more nodes to process **** while (root != null) { // **** new list of leaves **** List<Integer> leaves = new ArrayList<>(); // **** find leaf nodes on this pass **** root = findLeaves(root, leaves); // ???? ???? // System.out.println("findLeaves <<< leaves: " + leaves.toString()); // **** add list of leaf nodes to the list **** lol.add(leaves); } // **** return list of lists **** return lol; }

This is the function of interest.

We start by declaring an array list that will hold the lists of leaf node values.

We then perform a sanity check.

A recursive call is then invoked. In the second parameter we pass the array list.

After the recursion completes, we display the contents of the array list.

In the dfs function we are about to see, we are going to use an approach **similar** to the one we implemented in the Maximum Depth of a Binary Tree post. This might be a good time to take a quick look at the function of interest in that post. I will wait for you to return …

… OK, now that you are back we can proceed. This is the entry point for the function of interest.

We start by allocating an array list which will be used to store the end notes on each pass.

We then enter a loop that will end when the root of the tree becomes null.

In the loop we declare an array list that will be used to store the end leaf nodes of the current pass.

We then call the recursive function that will populate the leaves list and will update the root node when done.

The leaves are added to the `lol` list after each pass / loop.

/** * Recursive call. */ static TreeNode findLeaves(TreeNode root, List<Integer> leaves) { // **** base condition(s) **** if (root == null) return root; // ???? ???? // System.out.println("findLeaves <<< val: " + root.val); // **** found leaf node **** if (root.left == null && root.right == null) { // **** add it to list **** leaves.add(root.val); // **** **** return null; } // **** recursion on left and right sub-trees **** root.left = findLeaves(root.left, leaves); root.right = findLeaves(root.right, leaves); // **** return root **** return root; }

Let’s now get into the recursive call.

We have two base cases. The first is encountered when the root is null. This signals that we have processed and added to the leaves list the root of the binary tree.

The second case is when we encounter a leaf node that is not the root. This code takes care of all other leaf nodes. We add the value to the leaves list and return null indicating there are no more sub-trees to explore on this branch.

We then perform the recursive calls on the left and right sub trees.

When all is said and done we return the root of the binary tree.

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

Regards;

John

]]>After having breakfast, my wife and I went out on our daily walk. We run into several people we know. Perhaps people want to get in their workout before heading out for the long weekend. The fourth of July falls this Sunday, so the **observed** Independence Day (United States) is Monday July 05, 2021.

We do not have plans for the holiday. We will stick to our daily routine. I will try to get in one or two work-blocks each day in addition to do groceries and visit the Farmers Market in St. Paul. For one reason or another this year we have not shopped there yet.

In this post we will solve a very interesting problem involving islands on a 2D grid. LeetCode has several problems involving islands. I believe I have solved a few in this blog. The problem in question is LeetCode 1905 Count Sub Islands.

You are given two m x n binary matrices grid1 and grid2 containing only 0's (representing water) and 1's (representing land). An island is a group of 1's connected 4-directionally (horizontal or vertical). Any cells outside of the grid are considered water cells. An island in grid2 is considered a sub-island if there is an island in grid1 that contains all the cells that make up this island in grid2. Return the number of islands in grid2 that are considered sub-islands. Constraints: o m == grid1.length == grid2.length o n == grid1[i].length == grid2[i].length o 1 <= m, n <= 500 o grid1[i][j] and grid2[i][j] are either 0 or 1.

We are given two 2D matrices. Our task is to count the number of sub-islands in grid2. In my opinion, this is a simple but challenging task.

public int countSubIslands(int[][] grid1, int[][] grid2) { }

We will be attempting to solve this problem using the **Java** programming language and the **VSCode** IDE on a **Windows** computer. You may use different resources. In general it is simpler to use the on-line IDE provided by LeetCode. That way you will not be required to develop a test scaffold.

The **signature** for the function in question requires as arguments the two grids specified in the requirements of the problem.

5,5 1,1,1,0,0 0,1,1,1,1 0,0,0,0,0 1,0,0,0,0 1,1,0,1,1 1,1,1,0,0 0,0,1,1,1 0,1,0,0,0 1,0,1,1,0 0,1,0,1,0 main <<< m: 5 n: 5 main <<< grid1: [1, 1, 1, 0, 0] [0, 1, 1, 1, 1] [0, 0, 0, 0, 0] [1, 0, 0, 0, 0] [1, 1, 0, 1, 1] main <<< grid2: [1, 1, 1, 0, 0] [0, 0, 1, 1, 1] [0, 1, 0, 0, 0] [1, 0, 1, 1, 0] [0, 1, 0, 1, 0] <<< (0,0) count: 1 <<< (2,1) count: 1 <<< (3,0) count: 2 <<< (3,2) count: 2 <<< (4,1) count: 3 <<< g1: [1, 1, 1, 0, 0] [0, 1, 1, 1, 1] [0, 0, 0, 0, 0] [1, 0, 0, 0, 0] [1, 1, 0, 1, 1] <<< g2: [2, 2, 2, 0, 0] [0, 0, 2, 2, 2] [0, 2, 0, 0, 0] [2, 0, 2, 2, 0] [0, 2, 0, 2, 0] main <<< sub-islands: 3 5,5 1,0,1,0,1 1,1,1,1,1 0,0,0,0,0 1,1,1,1,1 1,0,1,0,1 0,0,0,0,0 1,1,1,1,1 0,1,0,1,0 0,1,0,1,0 1,0,0,0,1 main <<< m: 5 n: 5 main <<< grid1: [1, 0, 1, 0, 1] [1, 1, 1, 1, 1] [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] [1, 0, 1, 0, 1] main <<< grid2: [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] [0, 1, 0, 1, 0] [0, 1, 0, 1, 0] [1, 0, 0, 0, 1] <<< (1,0) count: 0 <<< (4,0) count: 1 <<< (4,4) count: 2 <<< g1: [1, 0, 1, 0, 1] [1, 1, 1, 1, 1] [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] [1, 0, 1, 0, 1] <<< g2: [0, 0, 0, 0, 0] [2, 2, 2, 2, 2] [0, 2, 0, 2, 0] [0, 2, 0, 2, 0] [2, 0, 0, 0, 2] main <<< sub-islands: 2

The first input line specifies the dimensions of the two grids. In both test cases we will be using 5×5 grids.

The following input lines describe the rows of the two grids. The rows for grid1 come first followed by the rows for grid2.

Our test code seems to display the number of rows followed by the number of columns.

The contents of grid1 are displayed followed by the contents of grid2.

The following lines display the position of a cell in an island followed by a count. It seems that the first match or sub-island was detected at coordinates (0, 0). The second sub-island was detected at coordinates (3, 0) and the third and last sub-island was detected at coordinates (4, 1). Please take a few moments to map the sub-islands in grid2 to the islands in grid1 and verify that the results so far make sense. I will wait…

…OK, let’s move forward.

Our code seems to display the contents of gri1 followed by the contents of grid2. Note that grid1 has not been disturbed. In grid2 the values for the cells have been changed from 1 to 2. This was probably done to make sure each cell was processed just once. This will become clear when we look at the actual code.

The last line of the test case displays the number of sub-islands found. For additional information and clarification, please refer to the problem definition. LeetCode has provided colorful grids.

/** * Test scaffold * @throws IOException */ public static void main(String[] args) throws IOException { // **** initialization **** int m = 0; int n = 0; int[][] grid1 = null; int[][] grid2 = null; // **** open buffered reader **** BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // **** read m & n **** String[] mn = br.readLine().trim().split(","); m = Integer.parseInt(mn[1]); n = Integer.parseInt(mn[0]); // **** read grid 1 **** grid1 = new int[m][n]; for (int i = 0; i < m; i++) { int[] row = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); grid1[i] = row; } // **** read grid 2 **** grid2 = new int[m][n]; for (int i = 0; i < grid1.length; i++) { int[] row = Arrays.stream(br.readLine().trim().split(",")) .mapToInt(Integer::parseInt) .toArray(); grid2[i] = row; } // **** close buffered reader **** br.close(); // ???? ???? System.out.println("main <<< m: " + m + " n: " + n); System.out.println("main <<< grid1:"); displayGrid(grid1); System.out.println("main <<< grid2:"); displayGrid(grid2); // **** call function of interest and display result **** // System.out.println("main <<< sub-islands: " + countSubIslands0(grid1, grid2)); // **** call function of interest and display result **** System.out.println("main <<< sub-islands: " + countSubIslands(grid1, grid2)); }

This is the test code that we wrote to collect the input data, call the function in questions and display the result. Please note that this **IS NOT PART OF THE SOLUTION**.

I am not going to say much because the descriptions of the test case seem to match closely the test code. Note that we have implemented **two** solutions. The first has been commented out. We will not go deep into the associated functions since as you will be able to see in the comments section of the code, the function passes the tests but could be optimized.

The code has been left commented out, because both implementations change the input grids. If you wish to run the first implementation, comment out the second. If you do not, the results will be incorrect.

/** * Display grid. * Utility function. * NOT PART OF THE SOLUTION */ static void displayGrid(int[][] grid) { for (int r = 0; r < grid.length; r++) { System.out.println(Arrays.toString(grid[r])); } }

This is a utility function. We use it to display the contents of the grids.

// **** global variables (for ease of use) **** static int n; static int m; static int[][] g1; static int[][] g2; static boolean valid = false;

We will be using in both approaches a set of global variables. We could avoid them by passing additional arguments to the different functions. The `valid` variable is **only** used in the first implementation.

/** * You are given two m x n binary matrices grid1 and grid2 * containing only 0's (representing water) and 1's (representing land). * An island is a group of 1's connected 4-directionally (horizontal or vertical). * Any cells outside of the grid are considered water cells. * * An island in grid2 is considered a sub-island * if there is an island in grid1 that contains all the cells that make up this island in grid2. * * Return the number of islands in grid2 that are considered sub-islands. * * Runtime: 33 ms, faster than 51.53% of Java online submissions. * Memory Usage: 75.7 MB, less than 81.59% of Java online submissions. */ static int countSubIslands0(int[][] grid1, int[][] grid2) { // **** initialization **** int count = 0; int color = 2; // island color // **** for ease of use **** n = grid1.length; // # of rows m = grid1[0].length; // # of columns g1 = grid1; g2 = grid2; // **** color island(s) in grid1 O(n * m) **** for (int r = 0; r < n; r++) { for (int c = 0; c < m; c++) { if (grid1[r] == 1) colorIsland(r, c, color++); } } // **** traverse the grids O(n * m) **** for (int r = 0; r < n; r++) { for (int c = 0; c < m; c++) { // **** **** if (g1[r] != 0 && g2[r] == 1) { // **** valid sub-island **** valid = true; // **** clear island from grid2 **** clearIsland(r, c, g1[r]); // **** increment sub-island count (if needed) **** if (valid) count++; } } } // **** return sub-island count **** return count; }

The comment shows the performance of the function. It seems that we should be able to generate a simpler and faster implementation. Due to this fact, we will briefly touch this implementation. In this implementation we touch both grids. Not only that, we perform two different DFS traversals, one on each grid. We start by performing some initializations. We loop through the first grid and color each island with different values. Since the grid starts by implementing all islands with a value of 1, we start by setting the land cell(s) in the first island with 2 and incrementing it on successive islands. The second loop looks for the first cell in an island in grid1 that matches land in the second grid. We flag that we have found a valid sub-island so we need to change the color of the sub-island in grid2 so we do not process the same cells more than once. We then increment the count of sub-islands. When all is said and done, we return the number of sub-islands found in grid2.

/** * Check if cell (r,c) is in bounds (inside the grid). * Utility function. */ static boolean inBounds(int r, int c) { return (r >= 0 && r < n && c >= 0 && c < m); }

This function is used to determine if the coordinates of a cell are inside the grids. We could have saved the use of the function call by just writing the code in-line.

/** * Color the island starting at the specifed cell in grid1 * with the specified color. */ static void colorIsland(int r, int c, int color) { // **** sanity check(s) **** if (inBounds(r, c) == false || g1[r] != 1) return; // **** cell belongs to island with specified color **** g1[r] = color; // **** recursively color all adjacent cells with specified color **** colorIsland(r + 1, c, color); // recurse down colorIsland(r - 1, c, color); // recurse up colorIsland(r, c + 1, color); // recurse right colorIsland(r, c - 1, color); // recurse left }

This function is used to color each island with the specified color. This is needed to check that we are processing a single island at a time. Note that it implements a DFS approach.

/** * Remove (change to water) from grid2 the specified island * starting at the specified cell. */ static void clearIsland(int r, int c, int color) { // **** sanity check(s) **** if (inBounds(r, c) == false || g2[r] == 0) return; // **** check if cell not in island in grid1 **** if (g1[r] != color) valid = false; // **** flag this cell as being processed (mark it as water) **** g2[r] = 0; // **** check adjacent cells recursively **** clearIsland(r + 1, c, color); // recurse down clearIsland(r - 1, c, color); // recurse up clearIsland(r, c + 1, color); // recurse right clearIsland(r, c - 1, color); // recurse left }

This function is used to change back to `water` the cells of an island that we have processed. This function also implements a DFS approach.

So let’s see if we can operate on a single grid. That should shave some execution time.

/** * You are given two m x n binary matrices grid1 and grid2 * containing only 0's (representing water) and 1's (representing land). * An island is a group of 1's connected 4-directionally (horizontal or vertical). * Any cells outside of the grid are considered water cells. * * An island in grid2 is considered a sub-island * if there is an island in grid1 that contains all the cells that make up this island in grid2. * * Return the number of islands in grid2 that are considered sub-islands. * * Runtime: 17 ms, faster than 98.51% of Java online submissions. * Memory Usage: 76.6 MB, less than 80.59% of Java online submissions. */ static int countSubIslands(int[][] grid1, int[][] grid2) { // **** for ease of use **** n = grid1.length; // # of rows m = grid1[0].length; // # of columns g1 = grid1; g2 = grid2; // **** initialization **** int count = 0; // **** DFS on each cell in grid2 (if on land) O(m * n) **** for (int r = 0; r < n; r++) { for (int c = 0; c < m; c++) { // **** unvisited land in g2 **** if (g2[r] == 1) { // **** increment count (if island in g1) **** count += dfs(r, c); // ???? ???? System.out.println("<<< (" + r + "," + c + ") count: " + count); } } } // ???? ???? System.out.println("<<< g1:"); displayGrid(g1); System.out.println("<<< g2:"); displayGrid(g2); // **** return count of sub-islands **** return count; }

We start by setting some variables.

We now enter a loop in which we are looking for a cell in an island in grid2. When found, we increment the sub-island count by traversing all the cells in the sub-island.

When all is done, the count represents the number of found sub-islands.

/** * DFS approach. */ static int dfs(int r, int c) { // **** base case(s) **** if (r < 0 || c < 0 || r >= n || c >= m || g2[r] == 2) return 1; if (g2[r] == 0) return 1; // **** set this cell to 2 (was 1) **** g2[r] = 2; // **** recursive calls in four directions (if g1 cell is part of an island) **** return g1[r] // land on g1 & dfs(r + 1, c) // recursive call down & dfs(r - 1, c) // recursive call up & dfs(r, c + 1) // recursive call right & dfs(r, c - 1); // recursive call left }

This recursive call implements the DFS algorithm. We start by testing a couple of end cases. Note that both return a value of 1.

We then set the value for the cell to 2 so we avoid visiting the same cell multiple times.

Finally we check the corresponding cell in grid1 and perform a set of recursive calls in each direction. When done, each sub-island will have cells of value 2.

Please take a look at the initial and final contents of the grid2. The values in grid1 have not been altered.

Also note the execution time of this approach. You can find it in the comments section of the second implementation.

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

Regards;

John

]]>