# Longest Increasing Path in a Matrix

- slug: longest-increasing-path-in-a-matrix
- difficulty: Hard

Given an `m x n` integers `matrix`, return _the length of the longest increasing path in_ `matrix`.

From each cell, you can either move in four directions: left, right, up, or down. You **may not** move **diagonally** or move **outside the boundary** (i.e., wrap-around is not allowed).

**Example 1:**

**Input:** matrix = \[\[9,9,4\],\[6,6,8\],\[2,1,1\]\]
**Output:** 4
**Explanation:** The longest increasing path is `[1, 2, 6, 9]`.

**Example 2:**

**Input:** matrix = \[\[3,4,5\],\[3,2,6\],\[2,2,1\]\]
**Output:** 4
**Explanation:** The longest increasing path is `[3, 4, 5, 6]`. Moving diagonally is not allowed.

**Example 3:**

**Input:** matrix = \[\[1\]\]
**Output:** 1

**Constraints:**

*   `m == matrix.length`
*   `n == matrix[i].length`
*   `1 <= m, n <= 200`
*   `0 <= matrix[i][j] <= 231 - 1`

## A solution in Java

```java
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};

public int dfs(int[][] matrix, int[][] memo, int i, int j) {
    if (memo[i][j] != 0) return memo[i][j];

    int max_len = 0;
    for (int k = 0; k < 4; ++k) {
        int ni = i + dx[k], nj = j + dy[k];
        if (ni >= 0 && ni < matrix.length && nj >= 0 && nj < matrix[0].length && matrix[ni][nj] > matrix[i][j]) {
            max_len = Math.max(max_len, dfs(matrix, memo, ni, nj));
        }
    }

    memo[i][j] = max_len + 1;
    return memo[i][j];
}

public int longestIncreasingPath(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;

    int[][] memo = new int[m][n];

    int ans = 0;
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
            ans = Math.max(ans, dfs(matrix, memo, i, j));

    return ans;
}
```
We use a depth-first search (DFS) to explore the grid. The basic idea is to start from each cell and move in the four directions allowed (left, right, up, and down). We can only move to a neighboring cell if the value in that cell is greater than the current cell (increasing path).

We use memoization to store the longest increasing path starting from each cell to avoid redundant calculations. For each cell `(i, j)`, we call the DFS function and compute the longest increasing path starting from that cell by exploring the neighboring cells in all four directions.

Finally, we return the longest increasing path found in the grid.

The time complexity of this algorithm is O(m * n) as we visit each cell once, where m is the number of rows and n is the number of columns in the given matrix.

## A solution in C++

```cpp
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};

int dfs(vector<vector<int>>& matrix, vector<vector<int>>& memo, int i, int j) {
    if (memo[i][j] != 0) return memo[i][j];

    int max_len = 0;
    for (int k = 0; k < 4; ++k) {
        int ni = i + dx[k], nj = j + dy[k];
        if (ni >= 0 && ni < matrix.size() && nj >= 0 && nj < matrix[0].size() && matrix[ni][nj] > matrix[i][j]) {
            max_len = max(max_len, dfs(matrix, memo, ni, nj));
        }
    }

    memo[i][j] = max_len + 1;
    return memo[i][j];
}

int longestIncreasingPath(vector<vector<int>>& matrix) {
    int m = matrix.size(), n = matrix[0].size();

    vector<vector<int>> memo(m, vector<int>(n, 0));

    int ans = 0;
    for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
            ans = max(ans, dfs(matrix, memo, i, j));

    return ans;
}
```
We use a depth-first search (DFS) to explore the grid. The basic idea is to start from each cell and move in the four directions allowed (left, right, up, and down). We can only move to a neighboring cell if the value in that cell is greater than the current cell (increasing path).

We use memoization to store the longest increasing path starting from each cell to avoid redundant calculations. For each cell `(i, j)`, we call the DFS function and compute the longest increasing path starting from that cell by exploring the neighboring cells in all four directions.

Finally, we return the longest increasing path found in the grid.

The time complexity of this algorithm is O(m * n) as we visit each cell once, where m is the number of rows and n is the number of columns in the given matrix.

## A solution in Python

```python
dx = (0, 0, 1, -1)
dy = (1, -1, 0, 0)

def dfs(matrix, memo, i, j):
    if memo[i][j] != 0:
        return memo[i][j]

    max_len = 0
    for k in range(4):
        ni, nj = i + dx[k], j + dy[k]
        if (0 <= ni < len(matrix) and 0 <= nj < len(matrix[0]) and matrix[ni][nj] > matrix[i][j]):
            max_len = max(max_len, dfs(matrix, memo, ni, nj))

    memo[i][j] = max_len + 1
    return memo[i][j]

def longest_increasing_path(matrix):
    m, n = len(matrix), len(matrix[0])

    memo = [[0] * n for _ in range(m)]

    ans = 0
    for i in range(m):
        for j in range(n):
            ans = max(ans, dfs(matrix, memo, i, j))

    return ans
```
We use a depth-first search (DFS) to explore the grid. The basic idea is to start from each cell and move in the four directions allowed (left, right, up, and down). We can only move to a neighboring cell if the value in that cell is greater than the current cell (increasing path).

We use memoization to store the longest increasing path starting from each cell to avoid redundant calculations. For each cell `(i, j)`, we call the DFS function and compute the longest increasing path starting from that cell by exploring the neighboring cells in all four directions.

Finally, we return the longest increasing path found in the grid.

The time complexity of this algorithm is O(m * n) as we visit each cell once, where m is the number of rows and n is the number of columns in the given matrix.

## A solution in Javascript

```javascript
const dx = [0, 0, 1, -1];
const dy = [1, -1, 0, 0];

function dfs(matrix, memo, i, j) {
    if (memo[i][j] !== 0) return memo[i][j];

    let max_len = 0;
    for (let k = 0; k < 4; ++k) {
        const ni = i + dx[k], nj = j + dy[k];
        if (ni >= 0 && ni < matrix.length && nj >= 0 && nj < matrix[0].length && matrix[ni][nj] > matrix[i][j]) {
            max_len = Math.max(max_len, dfs(matrix, memo, ni, nj));
        }
    }

    memo[i][j] = max_len + 1;
    return memo[i][j];
}

function longestIncreasingPath(matrix) {
    const m = matrix.length, n = matrix[0].length;

    const memo = Array.from({length: m}, () => Array.from({length: n}, () => 0));

    let ans = 0;
    for (let i = 0; i < m; ++i) {
        for (let j = 0; j < n; ++j) {
            ans = Math.max(ans, dfs(matrix, memo, i, j));
        }
    }

    return ans;
}
```
We use a depth-first search (DFS) to explore the grid. The basic idea is to start from each cell and move in the four directions allowed (left, right, up, and down). We can only move to a neighboring cell if the value in that cell is greater than the current cell (increasing path).

We use memoization to store the longest increasing path starting from each cell to avoid redundant calculations. For each cell `(i, j)`, we call the DFS function and compute the longest increasing path starting from that cell by exploring the neighboring cells in all four directions.

Finally, we return the longest increasing path found in the grid.

The time complexity of this algorithm is O(m * n) as we visit each cell once, where m is the number of rows and n is the number of columns in the given matrix.


# Patching Array

- slug: patching-array
- difficulty: Hard

Given a sorted integer array `nums` and an integer `n`, add/patch elements to the array such that any number in the range `[1, n]` inclusive can be formed by the sum of some elements in the array.

Return _the minimum number of patches required_.

**Example 1:**

**Input:** nums = \[1,3\], n = 6
**Output:** 1
Explanation:
Combinations of nums are \[1\], \[3\], \[1,3\], which form possible sums of: 1, 3, 4.
Now if we add/patch 2 to nums, the combinations are: \[1\], \[2\], \[3\], \[1,3\], \[2,3\], \[1,2,3\].
Possible sums are 1, 2, 3, 4, 5, 6, which now covers the range \[1, 6\].
So we only need 1 patch.

**Example 2:**

**Input:** nums = \[1,5,10\], n = 20
**Output:** 2
Explanation: The two patches can be \[2, 4\].

**Example 3:**

**Input:** nums = \[1,2,2\], n = 5
**Output:** 0

**Constraints:**

*   `1 <= nums.length <= 1000`
*   `1 <= nums[i] <= 104`
*   `nums` is sorted in **ascending order**.
*   `1 <= n <= 231 - 1`

## A solution in Java

```java
public int minPatches(int[] nums, int n) {
    long maxSum = 0;
    int patches = 0, i = 0;

    while (maxSum < n) {
        if (i < nums.length && nums[i] <= maxSum + 1) {
            maxSum += nums[i];
            i++;
        } else {
            maxSum += maxSum + 1;
            patches++;
        }
    }
    return patches;
}
```
The algorithm maintains a variable `maxSum` which stores the maximum sum that can be obtained using the current elements in the nums array. Initially, `maxSum` is set to 0, and we iterate through the array until `maxSum` is greater than or equal to the given `n`.

During the iteration, if the current element (nums[i]) is less than or equal to `maxSum + 1`, we can extend our sum range by updating `maxSum += nums[i]`. This means that the current element can be used to form a sum in the target range.

However, if the current nums[i] is greater than `maxSum + 1`, a patch would be needed. To minimize the number of patches, the best element to add would be `maxSum + 1`, as it extends the current maxSum twice as much without gaps. So, we update `maxSum += maxSum + 1`, and increment the patches count.

We repeat this process until `maxSum` reaches or exceeds the given `n`. Finally, we return the number of patches used.

## A solution in C++

```cpp
int minPatches(vector<int>& nums, int n) {
    long long maxSum = 0;
    int patches = 0, i = 0;

    while (maxSum < n) {
        if (i < nums.size() && nums[i] <= maxSum + 1) {
            maxSum += nums[i];
            i++;
        } else {
            maxSum += maxSum + 1;
            patches++;
        }
    }
    return patches;
}
```
The algorithm maintains a variable `maxSum` which stores the maximum sum that can be obtained using the current elements in the nums array. Initially, `maxSum` is set to 0, and we iterate through the array until `maxSum` is greater than or equal to the given `n`.

During the iteration, if the current element (nums[i]) is less than or equal to `maxSum + 1`, we can extend our sum range by updating `maxSum += nums[i]`. This means that the current element can be used to form a sum in the target range.

However, if the current nums[i] is greater than `maxSum + 1`, a patch would be needed. To minimize the number of patches, the best element to add would be `maxSum + 1`, as it extends the current maxSum twice as much without gaps. So, we update `maxSum += maxSum + 1`, and increment the patches count.

We repeat this process until `maxSum` reaches or exceeds the given `n`. Finally, we return the number of patches used.

## A solution in Python

```python
def minPatches(nums, n):
    max_sum = 0
    patches = 0
    i = 0

    while max_sum < n:
        if i < len(nums) and nums[i] <= max_sum + 1:
            max_sum += nums[i]
            i += 1
        else:
            max_sum += max_sum + 1
            patches += 1

    return patches
```
The algorithm maintains a variable `maxSum` which stores the maximum sum that can be obtained using the current elements in the nums array. Initially, `maxSum` is set to 0, and we iterate through the array until `maxSum` is greater than or equal to the given `n`.

During the iteration, if the current element (nums[i]) is less than or equal to `maxSum + 1`, we can extend our sum range by updating `maxSum += nums[i]`. This means that the current element can be used to form a sum in the target range.

However, if the current nums[i] is greater than `maxSum + 1`, a patch would be needed. To minimize the number of patches, the best element to add would be `maxSum + 1`, as it extends the current maxSum twice as much without gaps. So, we update `maxSum += maxSum + 1`, and increment the patches count.

We repeat this process until `maxSum` reaches or exceeds the given `n`. Finally, we return the number of patches used.

## A solution in Javascript

```javascript
function minPatches(nums, n) {
    let maxSum = 0;
    let patches = 0;
    let i = 0;

    while (maxSum < n) {
        if (i < nums.length && nums[i] <= maxSum + 1) {
            maxSum += nums[i];
            i++;
        } else {
            maxSum += maxSum + 1;
            patches++;
        }
    }
    return patches;
}
```
The algorithm maintains a variable `maxSum` which stores the maximum sum that can be obtained using the current elements in the nums array. Initially, `maxSum` is set to 0, and we iterate through the array until `maxSum` is greater than or equal to the given `n`.

During the iteration, if the current element (nums[i]) is less than or equal to `maxSum + 1`, we can extend our sum range by updating `maxSum += nums[i]`. This means that the current element can be used to form a sum in the target range.

However, if the current nums[i] is greater than `maxSum + 1`, a patch would be needed. To minimize the number of patches, the best element to add would be `maxSum + 1`, as it extends the current maxSum twice as much without gaps. So, we update `maxSum += maxSum + 1`, and increment the patches count.

We repeat this process until `maxSum` reaches or exceeds the given `n`. Finally, we return the number of patches used.


# Verify Preorder Serialization of a Binary Tree

- slug: verify-preorder-serialization-of-a-binary-tree
- difficulty: Medium

One way to serialize a binary tree is to use **preorder traversal**. When we encounter a non-null node, we record the node's value. If it is a null node, we record using a sentinel value such as `'#'`.

For example, the above binary tree can be serialized to the string `"9,3,4,#,#,1,#,#,2,#,6,#,# "`, where `'#'` represents a null node.

Given a string of comma-separated values `preorder`, return `true` if it is a correct preorder traversal serialization of a binary tree.

It is **guaranteed** that each comma-separated value in the string must be either an integer or a character `'#'` representing null pointer.

You may assume that the input format is always valid.

*   For example, it could never contain two consecutive commas, such as `"1,,3 "`.

**Note:** You are not allowed to reconstruct the tree.

**Example 1:**

**Input:** preorder = "9,3,4,#,#,1,#,#,2,#,6,#,#"
**Output:** true

**Example 2:**

**Input:** preorder = "1,#"
**Output:** false

**Example 3:**

**Input:** preorder = "9,#,#,1"
**Output:** false

**Constraints:**

*   `1 <= preorder.length <= 104`
*   `preorder` consist of integers in the range `[0, 100]` and `'#'` separated by commas `','`.

## A solution in Java

```java
public boolean isValidSerialization(String preorder) {
    String[] nodes = preorder.split(",");
    int node_count = 1;

    for (String node : nodes) {
        node_count--;

        if (node_count < 0) return false;
        if (!node.equals("#")) node_count += 2;
    }

    return node_count == 0;
}
```
The algorithm iterates through each node in the given preorder traversal string, decrementing a node count for each iteration. If the node count falls below 0, it returns false, as it means the input is not a valid preorder traversal serialization of a binary tree. If the current node is not a null node (represented by '#'), we increment the node count by 2, as each non-null node would have two children. Once the iteration is complete, we check if the node count is 0, indicating a valid serialization, and return the result accordingly.

## A solution in C++

```cpp
#include <string>
#include <sstream>
#include <vector>

bool isValidSerialization(const std::string &preorder) {
    std::istringstream iss(preorder);
    std::string token;
    int node_count = 1;

    while (std::getline(iss, token, ',')) {
        --node_count;
        if (node_count < 0) return false;
        if (token != "#") node_count += 2;
    }

    return node_count == 0;
}
```
The algorithm iterates through each node in the given preorder traversal string, decrementing a node count for each iteration. If the node count falls below 0, it returns false, as it means the input is not a valid preorder traversal serialization of a binary tree. If the current node is not a null node (represented by '#'), we increment the node count by 2, as each non-null node would have two children. Once the iteration is complete, we check if the node count is 0, indicating a valid serialization, and return the result accordingly.

## A solution in Python

```python
def is_valid_serialization(preorder: str) -> bool:
    nodes = preorder.split(',')
    node_count = 1

    for node in nodes:
        node_count -= 1
        if node_count < 0:
            return False
        if node != '#':
            node_count += 2

    return node_count == 0
```
The algorithm iterates through each node in the given preorder traversal string, decrementing a node count for each iteration. If the node count falls below 0, it returns false, as it means the input is not a valid preorder traversal serialization of a binary tree. If the current node is not a null node (represented by '#'), we increment the node count by 2, as each non-null node would have two children. Once the iteration is complete, we check if the node count is 0, indicating a valid serialization, and return the result accordingly.

## A solution in Javascript

```javascript
function isValidSerialization(preorder) {
    const nodes = preorder.split(',');
    let node_count = 1;

    for (let node of nodes) {
        node_count--;

        if (node_count < 0) return false;
        if (node !== '#') node_count += 2;
    }

    return node_count === 0;
}
```
The algorithm iterates through each node in the given preorder traversal string, decrementing a node count for each iteration. If the node count falls below 0, it returns false, as it means the input is not a valid preorder traversal serialization of a binary tree. If the current node is not a null node (represented by '#'), we increment the node count by 2, as each non-null node would have two children. Once the iteration is complete, we check if the node count is 0, indicating a valid serialization, and return the result accordingly.


# Reconstruct Itinerary

- slug: reconstruct-itinerary
- difficulty: Hard

You are given a list of airline `tickets` where `tickets[i] = [fromi, toi]` represent the departure and the arrival airports of one flight. Reconstruct the itinerary in order and return it.

All of the tickets belong to a man who departs from `"JFK "`, thus, the itinerary must begin with `"JFK "`. If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string.

*   For example, the itinerary `[ "JFK ", "LGA "]` has a smaller lexical order than `[ "JFK ", "LGB "]`.

You may assume all tickets form at least one valid itinerary. You must use all the tickets once and only once.

**Example 1:**

**Input:** tickets = \[\[ "MUC ", "LHR "\],\[ "JFK ", "MUC "\],\[ "SFO ", "SJC "\],\[ "LHR ", "SFO "\]\]
**Output:** \[ "JFK ", "MUC ", "LHR ", "SFO ", "SJC "\]

**Example 2:**

**Input:** tickets = \[\[ "JFK ", "SFO "\],\[ "JFK ", "ATL "\],\[ "SFO ", "ATL "\],\[ "ATL ", "JFK "\],\[ "ATL ", "SFO "\]\]
**Output:** \[ "JFK ", "ATL ", "JFK ", "SFO ", "ATL ", "SFO "\]
**Explanation:** Another possible reconstruction is \[ "JFK ", "SFO ", "ATL ", "JFK ", "ATL ", "SFO "\] but it is larger in lexical order.

**Constraints:**

*   `1 <= tickets.length <= 300`
*   `tickets[i].length == 2`
*   `fromi.length == 3`
*   `toi.length == 3`
*   `fromi` and `toi` consist of uppercase English letters.
*   `fromi != toi`

## A solution in Java

```java
import java.util.*;

class Solution {
    public List<String> findItinerary(List<List<String>> tickets) {
        HashMap<String, PriorityQueue<String>> flights = new HashMap<>();
        LinkedList<String> itinerary = new LinkedList<>();

        for (List<String> ticket : tickets) {
            flights.putIfAbsent(ticket.get(0), new PriorityQueue<>());
            flights.get(ticket.get(0)).offer(ticket.get(1));
        }

        Stack<String> dfs = new Stack<>();
        dfs.push("JFK");

        while (!dfs.isEmpty()) {
            String nextAirport = dfs.peek();
            if (flights.get(nextAirport) == null || flights.get(nextAirport).isEmpty()) {
                itinerary.addFirst(dfs.pop());
            } else {
                dfs.push(flights.get(nextAirport).poll());
            }
        }

        return itinerary;
    }
}
```
1. Create a data structure to store our itinerary as well as the flights to be taken.
2. Iterate over the given list of airline tickets and create a mapping of departure airports to possible arrival airports in sorted order.
3. Implement a depth-first search (DFS) helper function to traverse through the airports following the mentioned constraints.
4. Start the DFS traversal with the departure airport "JFK."
5. After the traversal is complete, reverse the reconstructed itinerary and return it as the result.

## A solution in C++

```cpp
#include <vector>
#include <string>
#include <unordered_map>
#include <set>

using namespace std;

vector<string> findItinerary(vector<vector<string>>& tickets) {
    unordered_map<string, multiset<string>> flights;
    vector<string> itinerary;

    for (const auto& ticket : tickets)
        flights[ticket[0]].insert(ticket[1]);

    stack<string> dfs;
    dfs.push("JFK");

    while (!dfs.empty()) {
        string next_airport = dfs.top();
        if (flights[next_airport].empty()) {
            itinerary.push_back(next_airport);
            dfs.pop();
        } else {
            dfs.push(*flights[next_airport].begin());
            flights[next_airport].erase(flights[next_airport].begin());
        }
    }

    reverse(itinerary.begin(), itinerary.end());
    return itinerary;
}
```
1. Create a data structure to store our itinerary as well as the flights to be taken.
2. Iterate over the given list of airline tickets and create a mapping of departure airports to possible arrival airports in sorted order.
3. Implement a depth-first search (DFS) helper function to traverse through the airports following the mentioned constraints.
4. Start the DFS traversal with the departure airport "JFK."
5. After the traversal is complete, reverse the reconstructed itinerary and return it as the result.

## A solution in Python

```python
from collections import defaultdict

def findItinerary(tickets):
    flights = defaultdict(list)
    itinerary = []

    for ticket in tickets:
        flights[ticket[0]].append(ticket[1])
        flights[ticket[0]].sort(reverse=True)

    def dfs(airport):
        while flights[airport]:
            dfs(flights[airport].pop())
        itinerary.append(airport)

    dfs("JFK")

    return itinerary[::-1]
```
1. Create a data structure to store our itinerary as well as the flights to be taken.
2. Iterate over the given list of airline tickets and create a mapping of departure airports to possible arrival airports in sorted order.
3. Implement a depth-first search (DFS) helper function to traverse through the airports following the mentioned constraints.
4. Start the DFS traversal with the departure airport "JFK."
5. After the traversal is complete, reverse the reconstructed itinerary and return it as the result.

## A solution in Javascript

```javascript
function findItinerary(tickets) {
    const flights = {};
    const itinerary = [];

    for (const ticket of tickets) {
        if (!flights[ticket[0]]) {
            flights[ticket[0]] = [];
        }
        flights[ticket[0]].push(ticket[1]);
        flights[ticket[0]].sort((a, b) => b.localeCompare(a));
    }

    function dfs(airport) {
        while (flights[airport] && flights[airport].length > 0) {
            dfs(flights[airport].pop());
        }
        itinerary.push(airport);
    }

    dfs("JFK");

    return itinerary.reverse();
}
```
1. Create a data structure to store our itinerary as well as the flights to be taken.
2. Iterate over the given list of airline tickets and create a mapping of departure airports to possible arrival airports in sorted order.
3. Implement a depth-first search (DFS) helper function to traverse through the airports following the mentioned constraints.
4. Start the DFS traversal with the departure airport "JFK."
5. After the traversal is complete, reverse the reconstructed itinerary and return it as the result.


# Largest BST Subtree

- slug: largest-bst-subtree
- difficulty: Medium

Given the root of a binary tree, find the largest subtree, which is also a Binary Search Tree (BST), where the largest means subtree has the largest number of nodes.

A **Binary Search Tree (BST)** is a tree in which all the nodes follow the below-mentioned properties:

*   The left subtree values are less than the value of their parent (root) node's value.
*   The right subtree values are greater than the value of their parent (root) node's value.

**Note:** A subtree must include all of its descendants.

**Example 1:**

**Input:** root = \[10,5,15,1,8,null,7\]
**Output:** 3
**Explanation:** The Largest BST Subtree in this case is the highlighted one. The return value is the subtree's size, which is 3.

**Example 2:**

**Input:** root = \[4,2,7,2,3,5,null,2,null,null,null,null,null,1\]
**Output:** 2

**Constraints:**

*   The number of nodes in the tree is in the range `[0, 104]`.
*   `-104 <= Node.val <= 104`

**Follow up:** Can you figure out ways to solve it with `O(n)` time complexity?

## A solution in Java

```java
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
}

class TreeInfo {
    boolean isBST;
    int size;
    int minValue;
    int maxValue;

    TreeInfo(boolean isBST, int size, int minValue, int maxValue) {
        this.isBST = isBST;
        this.size = size;
        this.minValue = minValue;
        this.maxValue = maxValue;
    }
}

public int largestBSTSubtree(TreeNode root) {
    return largestBSTUtil(root, new int[1]).size;
}

private TreeInfo largestBSTUtil(TreeNode root, int[] largestBST) {
    if (root == null) return new TreeInfo(true, 0, Integer.MAX_VALUE, Integer.MIN_VALUE);

    TreeInfo left = largestBSTUtil(root.left, largestBST);
    TreeInfo right = largestBSTUtil(root.right, largestBST);

    if (left.isBST && root.val > left.maxValue && right.isBST && root.val <right.minValue) {
        largestBST[0] = Math.max(largestBST[0], left.size + 1 + right.size);
        return new TreeInfo(true, left.size + 1 + right.size, Math.min(root.val, left.minValue), Math.max(root.val, right.maxValue));
    }

    return new TreeInfo(false, -1, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
```
The algorithm leverages the postorder traversal of the binary tree. We create a `TreeInfo` structure to store the information related to BST. It has `isBST` as a boolean to check if the tree is BST or not, `size` to store the size of the largest BST subtree, `minValue`, and `maxValue` to represent the minimum and maximum values in the subtree.

The utility function `largestBSTUtil` recursively traverses the binary tree in post order. When there is no root node, it returns a `TreeInfo` structure with default values. The function then checks the left and right subtrees.

The function checks if both the left and right subtrees are BST, and if the current root node's value is greater than the maximum value of its left subtree and less than the minimum value of its right subtree. If this condition is true, it sets `largestBST` as the maximum of its previous value and the sum of the current node's left and right subtree sizes plus one (the current node).

The function then returns a new `TreeInfo` structure with an updated `size`, `minValue`, and `maxValue` that combines information from both subtrees.

The base function `largestBSTSubtree` initializes an array `largestBST` at zero and calls the `largestBSTUtil` function. After the traversal, the `largestBST` will contain the size of the largest BST subtree found in the input binary tree. We return this value.

## A solution in C++

```cpp
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
};

struct TreeInfo {
    bool isBST;
    int size;
    int minValue;
    int maxValue;
};

TreeInfo largestBSTUtil(TreeNode* root, int &largestBST) {
    if (!root) return {true, 0, INT_MAX, INT_MIN};

    TreeInfo left = largestBSTUtil(root->left, largestBST);
    TreeInfo right = largestBSTUtil(root->right, largestBST);

    if (left.isBST && root->val > left.maxValue && right.isBST && root->val < right.minValue) {
        largestBST = max(largestBST, left.size + 1 + right.size);
        return {true, left.size + 1 + right.size, min(root->val, left.minValue), max(root->val, right.maxValue)};
    }

    return {false, -1, INT_MAX, INT_MIN};
}

int largestBSTSubtree(TreeNode* root) {
    int largestBST = 0;
    largestBSTUtil(root, largestBST);
    return largestBST;
}
```
The algorithm leverages the postorder traversal of the binary tree. We create a `TreeInfo` structure to store the information related to BST. It has `isBST` as a boolean to check if the tree is BST or not, `size` to store the size of the largest BST subtree, `minValue`, and `maxValue` to represent the minimum and maximum values in the subtree.

The utility function `largestBSTUtil` recursively traverses the binary tree in post order. When there is no root node, it returns a `TreeInfo` structure with default values. The function then checks the left and right subtrees.

The function checks if both the left and right subtrees are BST, and if the current root node's value is greater than the maximum value of its left subtree and less than the minimum value of its right subtree. If this condition is true, it sets `largestBST` as the maximum of its previous value and the sum of the current node's left and right subtree sizes plus one (the current node).

The function then returns a new `TreeInfo` structure with an updated `size`, `minValue`, and `maxValue` that combines information from both subtrees.

The base function `largestBSTSubtree` initializes an array `largestBST` at zero and calls the `largestBSTUtil` function. After the traversal, the `largestBST` will contain the size of the largest BST subtree found in the input binary tree. We return this value.

## A solution in Python

```python
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def largestBSTSubtree(root: TreeNode) -> int:
    def largestBSTUtil(root):
        if not root:
            return True, 0, float('inf'), float('-inf')

        left = largestBSTUtil(root.left)
        right = largestBSTUtil(root.right)

        if left[0] and root.val > left[3] and right[0] and root.val < right[2]:
            largestBST[0] = max(largestBST[0], left[1] + 1 + right[1])
            return True, left[1] + 1 + right[1], min(root.val, left[2]), max(root.val, right[3])

        return False, -1, float('inf'), float('-inf')

    largestBST = [0]
    largestBSTUtil(root)

    return largestBST[0]
```
The algorithm leverages the postorder traversal of the binary tree. We create a `TreeInfo` structure to store the information related to BST. It has `isBST` as a boolean to check if the tree is BST or not, `size` to store the size of the largest BST subtree, `minValue`, and `maxValue` to represent the minimum and maximum values in the subtree.

The utility function `largestBSTUtil` recursively traverses the binary tree in post order. When there is no root node, it returns a `TreeInfo` structure with default values. The function then checks the left and right subtrees.

The function checks if both the left and right subtrees are BST, and if the current root node's value is greater than the maximum value of its left subtree and less than the minimum value of its right subtree. If this condition is true, it sets `largestBST` as the maximum of its previous value and the sum of the current node's left and right subtree sizes plus one (the current node).

The function then returns a new `TreeInfo` structure with an updated `size`, `minValue`, and `maxValue` that combines information from both subtrees.

The base function `largestBSTSubtree` initializes an array `largestBST` at zero and calls the `largestBSTUtil` function. After the traversal, the `largestBST` will contain the size of the largest BST subtree found in the input binary tree. We return this value.

## A solution in Javascript

```javascript
class TreeNode {
    constructor(val, left, right) {
        this.val = (val===undefined ? 0 : val);
        this.left = (left===undefined ? null : left);
        this.right = (right===undefined ? null : right);
    }
}

function largestBSTSubtree(root) {
    function largestBSTUtil(root, largestBST) {
        if (!root) return [true, 0, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER];

        let left = largestBSTUtil(root.left, largestBST);
        let right = largestBSTUtil(root.right, largestBST);

        if (left[0] && root.val > left[3] && right[0] && root.val < right[2]) {
            largestBST[0] = Math.max(largestBST[0], left[1] + 1 + right[1]);
            return [true, left[1] + 1 + right[1], Math.min(root.val, left[2]), Math.max(root.val, right[3])];
        }

        return [false, -1, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER];
    }

    let largestBST = [0];
    largestBSTUtil(root, largestBST);

    return largestBST[0];
}
```
The algorithm leverages the postorder traversal of the binary tree. We create a `TreeInfo` structure to store the information related to BST. It has `isBST` as a boolean to check if the tree is BST or not, `size` to store the size of the largest BST subtree, `minValue`, and `maxValue` to represent the minimum and maximum values in the subtree.

The utility function `largestBSTUtil` recursively traverses the binary tree in post order. When there is no root node, it returns a `TreeInfo` structure with default values. The function then checks the left and right subtrees.

The function checks if both the left and right subtrees are BST, and if the current root node's value is greater than the maximum value of its left subtree and less than the minimum value of its right subtree. If this condition is true, it sets `largestBST` as the maximum of its previous value and the sum of the current node's left and right subtree sizes plus one (the current node).

The function then returns a new `TreeInfo` structure with an updated `size`, `minValue`, and `maxValue` that combines information from both subtrees.

The base function `largestBSTSubtree` initializes an array `largestBST` at zero and calls the `largestBSTUtil` function. After the traversal, the `largestBST` will contain the size of the largest BST subtree found in the input binary tree. We return this value.


# Increasing Triplet Subsequence

- slug: increasing-triplet-subsequence
- difficulty: Medium

Given an integer array `nums`, return `true` _if there exists a triple of indices_ `(i, j, k)` _such that_ `i < j < k` _and_ `nums[i] < nums[j] < nums[k]`. If no such indices exists, return `false`.

**Example 1:**

**Input:** nums = \[1,2,3,4,5\]
**Output:** true
**Explanation:** Any triplet where i < j < k is valid.

**Example 2:**

**Input:** nums = \[5,4,3,2,1\]
**Output:** false
**Explanation:** No triplet exists.

**Example 3:**

**Input:** nums = \[2,1,5,0,4,6\]
**Output:** true
**Explanation:** The triplet (3, 4, 5) is valid because nums\[3\] == 0 < nums\[4\] == 4 < nums\[5\] == 6.

**Constraints:**

*   `1 <= nums.length <= 5 * 105`
*   `-231 <= nums[i] <= 231 - 1`

**Follow up:** Could you implement a solution that runs in `O(n)` time complexity and `O(1)` space complexity?

## A solution in Java

```java
public boolean findTriplets(int[] nums) {
    int first = Integer.MAX_VALUE;
    int second = Integer.MAX_VALUE;

    for (int num : nums) {
        if (num <= first) {
            first = num;
        } else if (num <= second) {
            second = num;
        } else {
            return true;
        }
    }
    return false;
}
```
We use a greedy algorithm here. We keep track of two variables: `first` and `second`. `first` is initialized to the maximum integer value supported by the language. Similarly, `second` is initialized to the max integer value.

We iterate through the given array `nums`. If the current value is less than or equal to `first`, then we update `first` to be the current value. This helps us find the smallest value in the array. If the current value is not less than `first` but is less than or equal to `second`, we update `second` to be the current value. This helps us find the second smallest value, whose position is strictly greater than the position of `first`.

Now, during iteration, if we find any value that is greater than `second`, then we return `true`, as we have found three indices such that they satisfy the given condition.

If we iterate through the entire array and don't find a value that is greater than `second`, we return `false`. This means that we couldn't find a triplet satisfying the given condition.

This greedy algorithm runs in O(n) time complexity, as we iterate through the array once.

## A solution in C++

```cpp
bool findTriplets(vector<int>& nums) {
    int first = INT_MAX, second = INT_MAX;

    for (int num : nums) {
        if (num <= first) {
            first = num;
        } else if (num <= second) {
            second = num;
        } else {
            return true;
        }
    }
    return false;
}
```
We use a greedy algorithm here. We keep track of two variables: `first` and `second`. `first` is initialized to the maximum integer value supported by the language. Similarly, `second` is initialized to the max integer value.

We iterate through the given array `nums`. If the current value is less than or equal to `first`, then we update `first` to be the current value. This helps us find the smallest value in the array. If the current value is not less than `first` but is less than or equal to `second`, we update `second` to be the current value. This helps us find the second smallest value, whose position is strictly greater than the position of `first`.

Now, during iteration, if we find any value that is greater than `second`, then we return `true`, as we have found three indices such that they satisfy the given condition.

If we iterate through the entire array and don't find a value that is greater than `second`, we return `false`. This means that we couldn't find a triplet satisfying the given condition.

This greedy algorithm runs in O(n) time complexity, as we iterate through the array once.

## A solution in Python

```python
def findTriplets(nums: List[int]) -> bool:
    first = float('inf')
    second = float('inf')

    for num in nums:
        if num <= first:
            first = num
        elif num <= second:
            second = num
        else:
            return True
    return False
```
We use a greedy algorithm here. We keep track of two variables: `first` and `second`. `first` is initialized to the maximum integer value supported by the language. Similarly, `second` is initialized to the max integer value.

We iterate through the given array `nums`. If the current value is less than or equal to `first`, then we update `first` to be the current value. This helps us find the smallest value in the array. If the current value is not less than `first` but is less than or equal to `second`, we update `second` to be the current value. This helps us find the second smallest value, whose position is strictly greater than the position of `first`.

Now, during iteration, if we find any value that is greater than `second`, then we return `true`, as we have found three indices such that they satisfy the given condition.

If we iterate through the entire array and don't find a value that is greater than `second`, we return `false`. This means that we couldn't find a triplet satisfying the given condition.

This greedy algorithm runs in O(n) time complexity, as we iterate through the array once.

## A solution in Javascript

```javascript
function findTriplets(nums) {
    let first = Number.MAX_VALUE;
    let second = Number.MAX_VALUE;

    for (let num of nums) {
        if (num <= first) {
            first = num;
        } else if (num <= second) {
            second = num;
        } else {
            return true;
        }
    }
    return false;
}
```
We use a greedy algorithm here. We keep track of two variables: `first` and `second`. `first` is initialized to the maximum integer value supported by the language. Similarly, `second` is initialized to the max integer value.

We iterate through the given array `nums`. If the current value is less than or equal to `first`, then we update `first` to be the current value. This helps us find the smallest value in the array. If the current value is not less than `first` but is less than or equal to `second`, we update `second` to be the current value. This helps us find the second smallest value, whose position is strictly greater than the position of `first`.

Now, during iteration, if we find any value that is greater than `second`, then we return `true`, as we have found three indices such that they satisfy the given condition.

If we iterate through the entire array and don't find a value that is greater than `second`, we return `false`. This means that we couldn't find a triplet satisfying the given condition.

This greedy algorithm runs in O(n) time complexity, as we iterate through the array once.


# Self Crossing

- slug: self-crossing
- difficulty: Hard

You are given an array of integers `distance`.

You start at the point `(0, 0)` on an **X-Y plane,** and you move `distance[0]` meters to the north, then `distance[1]` meters to the west, `distance[2]` meters to the south, `distance[3]` meters to the east, and so on. In other words, after each move, your direction changes counter-clockwise.

Return `true` _if your path crosses itself or_ `false` _if it does not_.

**Example 1:**

**Input:** distance = \[2,1,1,2\]
**Output:** true
**Explanation:** The path crosses itself at the point (0, 1).

**Example 2:**

**Input:** distance = \[1,2,3,4\]
**Output:** false
**Explanation:** The path does not cross itself at any point.

**Example 3:**

**Input:** distance = \[1,1,1,2,1\]
**Output:** true
**Explanation:** The path crosses itself at the point (0, 0).

**Constraints:**

*   `1 <= distance.length <= 105`
*   `1 <= distance[i] <= 105`

## A solution in Java

```java
public boolean isPathCrossing(int[] distance) {
    Set<Pair<Integer, Integer>> visited = new HashSet<>();
    int x = 0, y = 0;
    visited.add(new Pair<>(x, y));
    int[] dx = {0, -1, 0, 1};
    int[] dy = {1, 0, -1, 0};

    for (int i = 0; i < distance.length; i++) {
        int direction = i % 4;
        for (int j = 0; j < distance[i]; j++) {
            x += dx[direction];
            y += dy[direction];
            Pair<Integer, Integer> point = new Pair<>(x, y);
            if (!visited.add(point)) {
                return true;
            }
        }
    }
    return false;
}
```
- Create a visited set containing the initial starting point `(0, 0)`
- Create variables x, y initialized as 0, which represent the current position
- Create two arrays dx, and dy to represent the change in the x and y directions for each step depending on the current direction (north, west, south, east).
- Use a nested loop to iterate over each step in given distances
  - Calculate the current direction using modulo operation
  - Move x and y based on the current direction using dx and dy
  - Check if the resulting point is in the visited set. If so, return True
  - Otherwise, add the point to the visited set
- After iterating through all steps, return False if no crossing has been detected

## A solution in C++

```cpp
bool isPathCrossing(const std::vector<int>& distance) {
    std::unordered_set<std::pair<int, int>, pair_hash> visited;
    int x = 0, y = 0;
    visited.insert({x, y});
    int dx[] = {0, -1, 0, 1};
    int dy[] = {1, 0, -1, 0};

    for (int i = 0; i < distance.size(); ++i) {
        int direction = i % 4;
        for (int j = 0; j < distance[i]; ++j) {
            x += dx[direction];
            y += dy[direction];
            if (!visited.insert({x, y}).second) {
                return true;
            }
        }
    }
    return false;
}
```
- Create a visited set containing the initial starting point `(0, 0)`
- Create variables x, y initialized as 0, which represent the current position
- Create two arrays dx, and dy to represent the change in the x and y directions for each step depending on the current direction (north, west, south, east).
- Use a nested loop to iterate over each step in given distances
  - Calculate the current direction using modulo operation
  - Move x and y based on the current direction using dx and dy
  - Check if the resulting point is in the visited set. If so, return True
  - Otherwise, add the point to the visited set
- After iterating through all steps, return False if no crossing has been detected

## A solution in Python

```python
def is_path_crossing(distance):
    visited = set([(0, 0)])
    x, y = 0, 0
    directions = [(0, 1), (-1, 0), (0, -1), (1, 0)]

    for i, d in enumerate(distance):
        dx, dy = directions[i % 4]
        for _ in range(d):
            x += dx
            y += dy
            if (x, y) in visited:
                return True
            visited.add((x, y))
    return False
```
- Create a visited set containing the initial starting point `(0, 0)`
- Create variables x, y initialized as 0, which represent the current position
- Create two arrays dx, and dy to represent the change in the x and y directions for each step depending on the current direction (north, west, south, east).
- Use a nested loop to iterate over each step in given distances
  - Calculate the current direction using modulo operation
  - Move x and y based on the current direction using dx and dy
  - Check if the resulting point is in the visited set. If so, return True
  - Otherwise, add the point to the visited set
- After iterating through all steps, return False if no crossing has been detected

## A solution in Javascript

```javascript
function isPathCrossing(distance) {
    let visited = new Set(["0,0"]);
    let x = 0, y = 0;
    const dx = [0, -1, 0, 1];
    const dy = [1, 0, -1, 0];

    for (let i = 0; i < distance.length; i++) {
        let direction = i % 4;
        for (let j = 0; j < distance[i]; j++) {
            x += dx[direction];
            y += dy[direction];
            let point = `${x},${y}`;
            if (visited.has(point)) {
                return true;
            }
            visited.add(point);
        }
    }
    return false;
}
```
- Create a visited set containing the initial starting point `(0, 0)`
- Create variables x, y initialized as 0, which represent the current position
- Create two arrays dx, and dy to represent the change in the x and y directions for each step depending on the current direction (north, west, south, east).
- Use a nested loop to iterate over each step in given distances
  - Calculate the current direction using modulo operation
  - Move x and y based on the current direction using dx and dy
  - Check if the resulting point is in the visited set. If so, return True
  - Otherwise, add the point to the visited set
- After iterating through all steps, return False if no crossing has been detected


# Palindrome Pairs

- slug: palindrome-pairs
- difficulty: Hard

You are given a **0-indexed** array of **unique** strings `words`.

A **palindrome pair** is a pair of integers `(i, j)` such that:

*   `0 <= i, j < words.length`,
*   `i != j`, and
*   `words[i] + words[j]` (the concatenation of the two strings) is a palindrome.

Return _an array of all the **palindrome pairs** of_ `words`.

**Example 1:**

**Input:** words = \[ "abcd ", "dcba ", "lls ", "s ", "sssll "\]
**Output:** \[\[0,1\],\[1,0\],\[3,2\],\[2,4\]\]
**Explanation:** The palindromes are \[ "abcddcba ", "dcbaabcd ", "slls ", "llssssll "\]

**Example 2:**

**Input:** words = \[ "bat ", "tab ", "cat "\]
**Output:** \[\[0,1\],\[1,0\]\]
**Explanation:** The palindromes are \[ "battab ", "tabbat "\]

**Example 3:**

**Input:** words = \[ "a ", " "\]
**Output:** \[\[0,1\],\[1,0\]\]
**Explanation:** The palindromes are \[ "a ", "a "\]

**Constraints:**

*   `1 <= words.length <= 5000`
*   `0 <= words[i].length <= 300`
*   `words[i]` consists of lowercase English letters.

## A solution in Java

```java
import java.util.ArrayList;
import java.util.List;

public List<List<Integer>> palindromePairs(String[] words) {
    List<List<Integer>> result = new ArrayList<>();

    for (int i = 0; i < words.length; i++) {
        for (int j = 0; j < words.length; j++) {
            if (i == j) continue;
            String concat = words[i] + words[j];

            boolean isPalindrome = true;
            for (int left = 0, right = concat.length() - 1; left < right; left++, right--) {
                if (concat.charAt(left) != concat.charAt(right)) {
                    isPalindrome = false;
                    break;
                }
            }

            if (isPalindrome) {
                List<Integer> pair = new ArrayList<>();
                pair.add(i);
                pair.add(j);
                result.add(pair);
            }
        }
    }

    return result;
}
```
The algorithm starts by defining a nested function `isPalindrome` to check if a given string is a palindrome. This function will be used later in the code.

Then, a `result` list (vector in C++) is created to store the index pairs of the palindrome concatenations.

Two nested loops are used to iterate through all the pairs of words in the input list. If the two words are the same (i.e., the indices `i` and `j` are equal), the current iteration is ignored (with a `continue` statement). This ensures that we only process distinct pairs of words.

Next, we concatenate the two words, and check if the resulting string is a palindrome using the `isPalindrome` function. If it is, we add the indices as a pair to the `result` list.

Once the two loops complete, the `result` list is returned, containing all the unique index pairs (i, j) where the concatenation of words[i] and words[j] is a palindrome.

## A solution in C++

```cpp
#include <vector>
#include <string>

std::vector<std::pair<int, int>> palindromePairs(const std::vector<std::string> &words) {
    std::vector<std::pair<int, int>> result;

    for (size_t i = 0; i < words.size(); i++) {
        for (size_t j = 0; j < words.size(); j++) {
            if (i == j) continue;
            std::string concat = words[i] + words[j];

            bool is_palindrome = true;
            for (size_t left = 0, right = concat.size() - 1; left < right; left++, right--) {
                if (concat[left] != concat[right]) {
                    is_palindrome = false;
                    break;
                }
            }

            if (is_palindrome) {
                result.push_back(std::make_pair(i, j));
            }
        }
    }

    return result;
}
```
The algorithm starts by defining a nested function `isPalindrome` to check if a given string is a palindrome. This function will be used later in the code.

Then, a `result` list (vector in C++) is created to store the index pairs of the palindrome concatenations.

Two nested loops are used to iterate through all the pairs of words in the input list. If the two words are the same (i.e., the indices `i` and `j` are equal), the current iteration is ignored (with a `continue` statement). This ensures that we only process distinct pairs of words.

Next, we concatenate the two words, and check if the resulting string is a palindrome using the `isPalindrome` function. If it is, we add the indices as a pair to the `result` list.

Once the two loops complete, the `result` list is returned, containing all the unique index pairs (i, j) where the concatenation of words[i] and words[j] is a palindrome.

## A solution in Python

```python
def palindrome_pairs(words):
    def is_palindrome(s):
        return s == s[::-1]

    result = []
    for i in range(len(words)):
        for j in range(len(words)):
            if i == j:
                continue
            concat = words[i] + words[j]
            if is_palindrome(concat):
                result.append([i, j])
    return result
```
The algorithm starts by defining a nested function `isPalindrome` to check if a given string is a palindrome. This function will be used later in the code.

Then, a `result` list (vector in C++) is created to store the index pairs of the palindrome concatenations.

Two nested loops are used to iterate through all the pairs of words in the input list. If the two words are the same (i.e., the indices `i` and `j` are equal), the current iteration is ignored (with a `continue` statement). This ensures that we only process distinct pairs of words.

Next, we concatenate the two words, and check if the resulting string is a palindrome using the `isPalindrome` function. If it is, we add the indices as a pair to the `result` list.

Once the two loops complete, the `result` list is returned, containing all the unique index pairs (i, j) where the concatenation of words[i] and words[j] is a palindrome.

## A solution in Javascript

```javascript
function palindromePairs(words) {
    function isPalindrome(s) {
        return s === s.split('').reverse().join('');
    }

    const result = [];
    for (let i = 0; i < words.length; i++) {
        for (let j = 0; j < words.length; j++) {
            if (i === j) continue;
            const concat = words[i] + words[j];
            if (isPalindrome(concat)) {
                result.push([i, j]);
            }
        }
    }

    return result;
}
```
The algorithm starts by defining a nested function `isPalindrome` to check if a given string is a palindrome. This function will be used later in the code.

Then, a `result` list (vector in C++) is created to store the index pairs of the palindrome concatenations.

Two nested loops are used to iterate through all the pairs of words in the input list. If the two words are the same (i.e., the indices `i` and `j` are equal), the current iteration is ignored (with a `continue` statement). This ensures that we only process distinct pairs of words.

Next, we concatenate the two words, and check if the resulting string is a palindrome using the `isPalindrome` function. If it is, we add the indices as a pair to the `result` list.

Once the two loops complete, the `result` list is returned, containing all the unique index pairs (i, j) where the concatenation of words[i] and words[j] is a palindrome.


# House Robber III

- slug: house-robber-iii
- difficulty: Medium

The thief has found himself a new place for his thievery again. There is only one entrance to this area, called `root`.

Besides the `root`, each house has one and only one parent house. After a tour, the smart thief realized that all houses in this place form a binary tree. It will automatically contact the police if **two directly-linked houses were broken into on the same night**.

Given the `root` of the binary tree, return _the maximum amount of money the thief can rob **without alerting the police**_.

**Example 1:**

**Input:** root = \[3,2,3,null,3,null,1\]
**Output:** 7
**Explanation:** Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.

**Example 2:**

**Input:** root = \[3,4,5,1,3,null,1\]
**Output:** 9
**Explanation:** Maximum amount of money the thief can rob = 4 + 5 = 9.

**Constraints:**

*   The number of nodes in the tree is in the range `[1, 104]`.
*   `0 <= Node.val <= 104`

## A solution in Java

```java
public int rob(TreeNode root) {
    int[] result = robHelper(root);
    return Math.max(result[0], result[1]);
}

private int[] robHelper(TreeNode node) {
    if (node == null) return new int[2];

    int[] left = robHelper(node.left);
    int[] right = robHelper(node.right);
    int[] result = new int[2];

    result[0] = node.val + left[1] + right[1];
    result[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
    return result;
}
```
The algorithm uses a dynamic programming approach to find the optimal sum for each subtree. The helper function has two main cases for each node:

1. Include the current node value in the sum (includeNode): If the current node is included in the sum, its children nodes cannot be included. Therefore, we add the current node value to the sum of the node's grandchildren.

2. Exclude the current node value in the sum (excludeNode): If the current node is not included in the sum, its children can be included. We add the maximum of the children nodes' sum including or excluding the children in the sum.

The helper function is called recursively for each child node until the leaves are reached. For each node, the helper function returns two values indicating the maximum sum including and excluding the current node. At the end of the recursion, we return the maximum of these two values for the root node as the result.

The time complexity of this algorithm is O(n), where n is the number of nodes in the tree.

## A solution in C++

```cpp
int robHelper(TreeNode* node, int& includeNode, int& excludeNode) {
    if (!node) return 0;

    int left_include, left_exclude, right_include, right_exclude;

    robHelper(node->left, left_include, left_exclude);
    robHelper(node->right, right_include, right_exclude);

    includeNode = node->val + left_exclude + right_exclude;
    excludeNode = max(left_include, left_exclude) + max(right_include, right_exclude);
    return max(includeNode, excludeNode);
}

int rob(TreeNode* root) {
    int includeNode = 0, excludeNode = 0;
    return robHelper(root, includeNode, excludeNode);
}
```
The algorithm uses a dynamic programming approach to find the optimal sum for each subtree. The helper function has two main cases for each node:

1. Include the current node value in the sum (includeNode): If the current node is included in the sum, its children nodes cannot be included. Therefore, we add the current node value to the sum of the node's grandchildren.

2. Exclude the current node value in the sum (excludeNode): If the current node is not included in the sum, its children can be included. We add the maximum of the children nodes' sum including or excluding the children in the sum.

The helper function is called recursively for each child node until the leaves are reached. For each node, the helper function returns two values indicating the maximum sum including and excluding the current node. At the end of the recursion, we return the maximum of these two values for the root node as the result.

The time complexity of this algorithm is O(n), where n is the number of nodes in the tree.

## A solution in Python

```python
def rob(self, root: TreeNode) -> int:
    def rob_helper(node):
        if not node:
            return 0, 0

        left_with_node, left_without_node = rob_helper(node.left)
        right_with_node, right_without_node = rob_helper(node.right)

        with_node = node.val + left_without_node + right_without_node
        without_node = max(left_with_node, left_without_node) + max(right_with_node, right_without_node)
        return with_node, without_node

    return max(rob_helper(root))
```
The algorithm uses a dynamic programming approach to find the optimal sum for each subtree. The helper function has two main cases for each node:

1. Include the current node value in the sum (includeNode): If the current node is included in the sum, its children nodes cannot be included. Therefore, we add the current node value to the sum of the node's grandchildren.

2. Exclude the current node value in the sum (excludeNode): If the current node is not included in the sum, its children can be included. We add the maximum of the children nodes' sum including or excluding the children in the sum.

The helper function is called recursively for each child node until the leaves are reached. For each node, the helper function returns two values indicating the maximum sum including and excluding the current node. At the end of the recursion, we return the maximum of these two values for the root node as the result.

The time complexity of this algorithm is O(n), where n is the number of nodes in the tree.

## A solution in Javascript

```javascript
var rob = function(root) {
    const [withNode, withoutNode] = robHelper(root);
    return Math.max(withNode, withoutNode);
};

function robHelper(node) {
    if (!node) return [0, 0];

    const [leftWithNode, leftWithoutNode] = robHelper(node.left);
    const [rightWithNode, rightWithoutNode] = robHelper(node.right);

    const withNode = node.val + leftWithoutNode + rightWithoutNode;
    const withoutNode = Math.max(leftWithNode, leftWithoutNode) + Math.max(rightWithNode, rightWithoutNode);
    return [withNode, withoutNode];
}
```
The algorithm uses a dynamic programming approach to find the optimal sum for each subtree. The helper function has two main cases for each node:

1. Include the current node value in the sum (includeNode): If the current node is included in the sum, its children nodes cannot be included. Therefore, we add the current node value to the sum of the node's grandchildren.

2. Exclude the current node value in the sum (excludeNode): If the current node is not included in the sum, its children can be included. We add the maximum of the children nodes' sum including or excluding the children in the sum.

The helper function is called recursively for each child node until the leaves are reached. For each node, the helper function returns two values indicating the maximum sum including and excluding the current node. At the end of the recursion, we return the maximum of these two values for the root node as the result.

The time complexity of this algorithm is O(n), where n is the number of nodes in the tree.


# Counting Bits

- slug: counting-bits
- difficulty: Easy

Given an integer `n`, return _an array_ `ans` _of length_ `n + 1` _such that for each_ `i` (`0 <= i <= n`)_,_ `ans[i]` _is the **number of**_ `1`_**'s** in the binary representation of_ `i`.

**Example 1:**

**Input:** n = 2
**Output:** \[0,1,1\]
**Explanation:**
0 --> 0
1 --> 1
2 --> 10

**Example 2:**

**Input:** n = 5
**Output:** \[0,1,1,2,1,2\]
**Explanation:**
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

**Constraints:**

*   `0 <= n <= 105`

**Follow up:**

*   It is very easy to come up with a solution with a runtime of `O(n log n)`. Can you do it in linear time `O(n)` and possibly in a single pass?
*   Can you do it without using any built-in function (i.e., like `__builtin_popcount` in C++)?

## A solution in Java

```java
public int[] countBits(int n) {
    int[] ans = new int[n + 1];
    for (int i = 1; i <= n; ++i) {
        ans[i] = ans[i & (i - 1)] + 1;
    }
    return ans;
}
```
The algorithm leverages a key observation: `(i & (i - 1))` has the same binary representation as `i`, but with the least significant `1` bit removed. This leads to a fast and efficient way to count the number of `1` bits in the binary representation of each number in the range `[0, n]`.

For each number `i` in the range `[1, n]`, we calculate `ans[i]` based on `ans[i & (i - 1)]`. Since `ans[i & (i - 1)]` is the count of `1` bits in the binary representation of `(i & (i - 1))`, adding `1` to that count will give us the count of `1` bits in the binary representation of `i`.

The algorithm iterates through the range `[1, n]` and fills the output array `ans` accordingly. The final array `ans` contains the count of `1` bits in the binary representation of each number in the range `[0, n]`.

The time complexity of this algorithm is O(n) as it iterates through the range `[1, n]` only once, and the space complexity is O(n + 1) since it creates an array of length `n + 1` to store the results.

## A solution in C++

```cpp
#include <vector>
using namespace std;

vector<int> countBits(int n) {
    vector<int> ans(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        ans[i] = ans[i & (i - 1)] + 1;
    }
    return ans;
}
```
The algorithm leverages a key observation: `(i & (i - 1))` has the same binary representation as `i`, but with the least significant `1` bit removed. This leads to a fast and efficient way to count the number of `1` bits in the binary representation of each number in the range `[0, n]`.

For each number `i` in the range `[1, n]`, we calculate `ans[i]` based on `ans[i & (i - 1)]`. Since `ans[i & (i - 1)]` is the count of `1` bits in the binary representation of `(i & (i - 1))`, adding `1` to that count will give us the count of `1` bits in the binary representation of `i`.

The algorithm iterates through the range `[1, n]` and fills the output array `ans` accordingly. The final array `ans` contains the count of `1` bits in the binary representation of each number in the range `[0, n]`.

The time complexity of this algorithm is O(n) as it iterates through the range `[1, n]` only once, and the space complexity is O(n + 1) since it creates an array of length `n + 1` to store the results.

## A solution in Python

```python
def countBits(n):
    ans = [0] * (n + 1)
    for i in range(1, n + 1):
        ans[i] = ans[i & (i - 1)] + 1
    return ans
```
The algorithm leverages a key observation: `(i & (i - 1))` has the same binary representation as `i`, but with the least significant `1` bit removed. This leads to a fast and efficient way to count the number of `1` bits in the binary representation of each number in the range `[0, n]`.

For each number `i` in the range `[1, n]`, we calculate `ans[i]` based on `ans[i & (i - 1)]`. Since `ans[i & (i - 1)]` is the count of `1` bits in the binary representation of `(i & (i - 1))`, adding `1` to that count will give us the count of `1` bits in the binary representation of `i`.

The algorithm iterates through the range `[1, n]` and fills the output array `ans` accordingly. The final array `ans` contains the count of `1` bits in the binary representation of each number in the range `[0, n]`.

The time complexity of this algorithm is O(n) as it iterates through the range `[1, n]` only once, and the space complexity is O(n + 1) since it creates an array of length `n + 1` to store the results.

## A solution in Javascript

```javascript
function countBits(n) {
    const ans = new Array(n + 1).fill(0);
    for (let i = 1; i <= n; ++i) {
        ans[i] = ans[i & (i - 1)] + 1;
    }
    return ans;
}
```
The algorithm leverages a key observation: `(i & (i - 1))` has the same binary representation as `i`, but with the least significant `1` bit removed. This leads to a fast and efficient way to count the number of `1` bits in the binary representation of each number in the range `[0, n]`.

For each number `i` in the range `[1, n]`, we calculate `ans[i]` based on `ans[i & (i - 1)]`. Since `ans[i & (i - 1)]` is the count of `1` bits in the binary representation of `(i & (i - 1))`, adding `1` to that count will give us the count of `1` bits in the binary representation of `i`.

The algorithm iterates through the range `[1, n]` and fills the output array `ans` accordingly. The final array `ans` contains the count of `1` bits in the binary representation of each number in the range `[0, n]`.

The time complexity of this algorithm is O(n) as it iterates through the range `[1, n]` only once, and the space complexity is O(n + 1) since it creates an array of length `n + 1` to store the results.


# Nested List Weight Sum

- slug: nested-list-weight-sum
- difficulty: Medium

You are given a nested list of integers `nestedList`. Each element is either an integer or a list whose elements may also be integers or other lists.

The **depth** of an integer is the number of lists that it is inside of. For example, the nested list `[1,[2,2],[[3],2],1]` has each integer's value set to its **depth**.

Return _the sum of each integer in_ `nestedList` _multiplied by its **depth**_.

**Example 1:**

**Input:** nestedList = \[\[1,1\],2,\[1,1\]\]
**Output:** 10
**Explanation:** Four 1's at depth 2, one 2 at depth 1. 1\*2 + 1\*2 + 2\*1 + 1\*2 + 1\*2 = 10.

**Example 2:**

**Input:** nestedList = \[1,\[4,\[6\]\]\]
**Output:** 27
**Explanation:** One 1 at depth 1, one 4 at depth 2, and one 6 at depth 3. 1\*1 + 4\*2 + 6\*3 = 27.

**Example 3:**

**Input:** nestedList = \[0\]
**Output:** 0

**Constraints:**

*   `1 <= nestedList.length <= 50`
*   The values of the integers in the nested list is in the range `[-100, 100]`.
*   The maximum **depth** of any integer is less than or equal to `50`.

## A solution in Java

```java
import java.util.List;

public int depthSum(List<?> nestedList) {
    return depthSumHelper(nestedList, 1);
}

private int depthSumHelper(List<?> nestedList, int depth) {
    int sum = 0;
    for (Object element : nestedList) {
        if (element instanceof Integer) {
            sum += (int) element * depth;
        } else {
            sum += depthSumHelper((List<?>) element, depth + 1);
        }
    }
    return sum;
}
```
The algorithm utilizes a helper function that takes the nested list and the current depth. It loops through each element in the nested list. If the element is an integer, it adds the product of the integer and the depth to the sum. If the element is a list, it recursively calls the helper function with the element and a higher depth. This continues until all elements are processed, and the sum is returned.

## A solution in C++

```cpp
#include <vector>
#include <variant>

using NestedList = std::vector<std::variant<int, std::vector<std::variant<int, std::vector<int>>>>>;

int depthSumHelper(NestedList nestedList, int depth) {
    int sum = 0;
    for (auto& element : nestedList) {
        if (std::holds_alternative<int>(element)) {
            sum += std::get<int>(element) * depth;
        } else {
            sum += depthSumHelper(std::get<std::vector<std::variant<int, std::vector<int>>>>(element), depth + 1);
        }
    }
    return sum;
}

int depthSum(NestedList nestedList) {
    return depthSumHelper(nestedList, 1);
}
```
The algorithm utilizes a helper function that takes the nested list and the current depth. It loops through each element in the nested list. If the element is an integer, it adds the product of the integer and the depth to the sum. If the element is a list, it recursively calls the helper function with the element and a higher depth. This continues until all elements are processed, and the sum is returned.

## A solution in Python

```python
from typing import List, Union

def depth_sum(nested_list: List[Union[int, List]]) -> int:
    def depth_sum_helper(nested_list: List[Union[int, List]], depth: int) -> int:
        return sum(element * depth if isinstance(element, int) else depth_sum_helper(element, depth + 1) for element in nested_list)

    return depth_sum_helper(nested_list, 1)
```
The algorithm utilizes a helper function that takes the nested list and the current depth. It loops through each element in the nested list. If the element is an integer, it adds the product of the integer and the depth to the sum. If the element is a list, it recursively calls the helper function with the element and a higher depth. This continues until all elements are processed, and the sum is returned.

## A solution in Javascript

```javascript
function depthSum(nestedList) {
    return depthSumHelper(nestedList, 1);
}

function depthSumHelper(nestedList, depth) {
    let sum = 0;
    for (let element of nestedList) {
        if (typeof element === "number") {
            sum += element * depth;
        } else {
            sum += depthSumHelper(element, depth + 1);
        }
    }
    return sum;
}
```
The algorithm utilizes a helper function that takes the nested list and the current depth. It loops through each element in the nested list. If the element is an integer, it adds the product of the integer and the depth to the sum. If the element is a list, it recursively calls the helper function with the element and a higher depth. This continues until all elements are processed, and the sum is returned.


# Longest Substring with At Most K Distinct Characters

- slug: longest-substring-with-at-most-k-distinct-characters
- difficulty: Medium

Given a string `s` and an integer `k`, return _the length of the longest_ _substring_ _of_ `s` _that contains at most_ `k` _**distinct** characters_.

**Example 1:**

**Input:** s =  "eceba ", k = 2
**Output:** 3
**Explanation:** The substring is  "ece " with length 3.

**Example 2:**

**Input:** s =  "aa ", k = 1
**Output:** 2
**Explanation:** The substring is  "aa " with length 2.

**Constraints:**

*   `1 <= s.length <= 5 * 104`
*   `0 <= k <= 50`

## A solution in Java

```java
import java.util.HashMap;

public int lengthOfLongestSubstringKDistinct(String s, int k) {
    HashMap<Character, Integer> charCount = new HashMap<>();
    int left = 0, maxLength = 0;

    for (int right = 0; right < s.length(); ++right) {
        charCount.put(s.charAt(right), charCount.getOrDefault(s.charAt(right), 0) + 1);

        while (charCount.size() > k) {
            charCount.put(s.charAt(left), charCount.get(s.charAt(left)) - 1);
            if (charCount.get(s.charAt(left)) == 0) {
                charCount.remove(s.charAt(left));
            }
            left++;
        }

        maxLength = Math.max(maxLength, right - left + 1);
    }
    return maxLength;
}
```
1. Create a hashmap to store character counts within the sliding window.
2. Initialize `left` and `maxLength` to zero.
3. Iterate through the string with a for loop:
    a. Increment the count of the current character in the hashmap.
    b. If there are more than `k` distinct characters in the hashmap, move `left` to the right to shrink the window and update the count in the hashmap (remove the entry if the count is zero).
    c. Update `maxLength` with the length of the current window right - left + 1.
4. Return `maxLength`.

## A solution in C++

```cpp
#include <unordered_map>
#include <algorithm>

int lengthOfLongestSubstringKDistinct(std::string s, int k) {
    std::unordered_map<char, int> charCount;
    int left = 0, maxLength = 0;

    for (int right = 0; right < s.size(); ++right) {
        charCount[s[right]]++;

        while (charCount.size() > k) {
            charCount[s[left]]--;
            if (charCount[s[left]] == 0) {
                charCount.erase(s[left]);
            }
            left++;
        }

        maxLength = std::max(maxLength, right - left + 1);
    }
    return maxLength;
}
```
1. Create a hashmap to store character counts within the sliding window.
2. Initialize `left` and `maxLength` to zero.
3. Iterate through the string with a for loop:
    a. Increment the count of the current character in the hashmap.
    b. If there are more than `k` distinct characters in the hashmap, move `left` to the right to shrink the window and update the count in the hashmap (remove the entry if the count is zero).
    c. Update `maxLength` with the length of the current window right - left + 1.
4. Return `maxLength`.

## A solution in Python

```python
def length_of_longest_substring_k_distinct(s: str, k: int) -> int:
    char_count = {}
    left = 0
    max_length = 0

    for right, char in enumerate(s):
        char_count[char] = char_count.get(char, 0) + 1

        while len(char_count) > k:
            char_count[s[left]] -= 1
            if char_count[s[left]] == 0:
                del char_count[s[left]]
            left += 1

        max_length = max(max_length, right - left + 1)

    return max_length
```
1. Create a hashmap to store character counts within the sliding window.
2. Initialize `left` and `maxLength` to zero.
3. Iterate through the string with a for loop:
    a. Increment the count of the current character in the hashmap.
    b. If there are more than `k` distinct characters in the hashmap, move `left` to the right to shrink the window and update the count in the hashmap (remove the entry if the count is zero).
    c. Update `maxLength` with the length of the current window right - left + 1.
4. Return `maxLength`.

## A solution in Javascript

```javascript
function lengthOfLongestSubstringKDistinct(s, k) {
    const charCount = new Map();
    let left = 0;
    let maxLength = 0;

    for (let right = 0; right < s.length; ++right) {
        charCount.set(s[right], (charCount.get(s[right]) || 0) + 1);

        while (charCount.size > k) {
            charCount.set(s[left], charCount.get(s[left]) - 1);
            if (charCount.get(s[left]) === 0) {
                charCount.delete(s[left]);
            }
            left++;
        }

        maxLength = Math.max(maxLength, right - left + 1);
    }
    return maxLength;
}
```
1. Create a hashmap to store character counts within the sliding window.
2. Initialize `left` and `maxLength` to zero.
3. Iterate through the string with a for loop:
    a. Increment the count of the current character in the hashmap.
    b. If there are more than `k` distinct characters in the hashmap, move `left` to the right to shrink the window and update the count in the hashmap (remove the entry if the count is zero).
    c. Update `maxLength` with the length of the current window right - left + 1.
4. Return `maxLength`.


# Flatten Nested List Iterator

- slug: flatten-nested-list-iterator
- difficulty: Medium

You are given a nested list of integers `nestedList`. Each element is either an integer or a list whose elements may also be integers or other lists. Implement an iterator to flatten it.

Implement the `NestedIterator` class:

*   `NestedIterator(List nestedList)` Initializes the iterator with the nested list `nestedList`.
*   `int next()` Returns the next integer in the nested list.
*   `boolean hasNext()` Returns `true` if there are still some integers in the nested list and `false` otherwise.

Your code will be tested with the following pseudocode:

initialize iterator with nestedList
res = \[\]
while iterator.hasNext()
    append iterator.next() to the end of res
return res

If `res` matches the expected flattened list, then your code will be judged as correct.

**Example 1:**

**Input:** nestedList = \[\[1,1\],2,\[1,1\]\]
**Output:** \[1,1,2,1,1\]
**Explanation:** By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: \[1,1,2,1,1\].

**Example 2:**

**Input:** nestedList = \[1,\[4,\[6\]\]\]
**Output:** \[1,4,6\]
**Explanation:** By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: \[1,4,6\].

**Constraints:**

*   `1 <= nestedList.length <= 500`
*   The values of the integers in the nested list is in the range `[-106, 106]`.

## A solution in Java

```java
import java.util.*;

public class NestedIterator implements Iterator<Integer> {
    List<Integer> flatList;
    int index;

    public NestedIterator(List<NestedInteger> nestedList) {
        flatList = new ArrayList<Integer>();
        flatten(nestedList);
        index = 0;
    }

    private void flatten(List<NestedInteger> nestedList) {
        for (NestedInteger ni : nestedList) {
            if (ni.isInteger()) {
                flatList.add(ni.getInteger());
            } else {
                flatten(ni.getList());
            }
        }
    }

    @Override
    public Integer next() {
        return flatList.get(index++);
    }

    @Override
    public boolean hasNext() {
        return index < flatList.size();
    }
}
```
1. Create a NestedIterator class.
2. In the class, create a flatten function that accepts a nested list and a "flat list" as arguments.
3. For each element in the nested list,
   a. If the element is an integer, add it to the flat list.
   b. Otherwise, call the flatten function recursively on the element and the flat list.
4. In the constructor of the NestedIterator class,
   a. Initialize an empty flat list and an index variable.
   b. Call the flatten function with the given nested list and the initialized flat list.
5. The next method returns the value of the flat list at the current index and increments the index.
6. The hasNext method returns true if the index is less than the length of the flat list, indicating more elements are present in the iterator.

## A solution in C++

```cpp
class NestedIterator {
public:
    vector<int> flatList;
    int index;

    void flatten(vector<NestedInteger> &nestedList) {
        for (NestedInteger &ni : nestedList) {
            if (ni.isInteger()) {
                flatList.push_back(ni.getInteger());
            } else {
                flatten(ni.getList());
            }
        }
    }

    NestedIterator(vector<NestedInteger> &nestedList) {
        flatten(nestedList);
        index = 0;
    }

    int next() {
        return flatList[index++];
    }

    bool hasNext() {
        return index < flatList.size();
    }
};
```
1. Create a NestedIterator class.
2. In the class, create a flatten function that accepts a nested list and a "flat list" as arguments.
3. For each element in the nested list,
   a. If the element is an integer, add it to the flat list.
   b. Otherwise, call the flatten function recursively on the element and the flat list.
4. In the constructor of the NestedIterator class,
   a. Initialize an empty flat list and an index variable.
   b. Call the flatten function with the given nested list and the initialized flat list.
5. The next method returns the value of the flat list at the current index and increments the index.
6. The hasNext method returns true if the index is less than the length of the flat list, indicating more elements are present in the iterator.

## A solution in Python

```python
class NestedIterator:
    def __init__(self, nestedList):
        def flatten(nestedList, flatList):
            for item in nestedList:
                if isinstance(item, int):
                    flatList.append(item)
                else:
                    flatten(item, flatList)
        self.flatList = []
        flatten(nestedList, self.flatList)
        self.index = 0

    def next(self):
        result = self.flatList[self.index]
        self.index += 1
        return result

    def hasNext(self):
        return self.index < len(self.flatList)
```
1. Create a NestedIterator class.
2. In the class, create a flatten function that accepts a nested list and a "flat list" as arguments.
3. For each element in the nested list,
   a. If the element is an integer, add it to the flat list.
   b. Otherwise, call the flatten function recursively on the element and the flat list.
4. In the constructor of the NestedIterator class,
   a. Initialize an empty flat list and an index variable.
   b. Call the flatten function with the given nested list and the initialized flat list.
5. The next method returns the value of the flat list at the current index and increments the index.
6. The hasNext method returns true if the index is less than the length of the flat list, indicating more elements are present in the iterator.

## A solution in Javascript

```javascript
class NestedIterator {
    constructor(nestedList) {
        this.flatList = [];
        this.flatten(nestedList, this.flatList);
        this.index = 0;
    }

    flatten(nestedList, flatList) {
        for (let item of nestedList) {
            if (Number.isInteger(item)) {
                flatList.push(item);
            } else {
                this.flatten(item, flatList);
            }
        }
    }

    next() {
        return this.flatList[this.index++];
    }

    hasNext() {
        return this.index < this.flatList.length;
    }
}
```
1. Create a NestedIterator class.
2. In the class, create a flatten function that accepts a nested list and a "flat list" as arguments.
3. For each element in the nested list,
   a. If the element is an integer, add it to the flat list.
   b. Otherwise, call the flatten function recursively on the element and the flat list.
4. In the constructor of the NestedIterator class,
   a. Initialize an empty flat list and an index variable.
   b. Call the flatten function with the given nested list and the initialized flat list.
5. The next method returns the value of the flat list at the current index and increments the index.
6. The hasNext method returns true if the index is less than the length of the flat list, indicating more elements are present in the iterator.


# Power of Four

- slug: power-of-four
- difficulty: Easy

Given an integer `n`, return _`true` if it is a power of four. Otherwise, return `false`_.

An integer `n` is a power of four, if there exists an integer `x` such that `n == 4x`.

**Example 1:**

**Input:** n = 16
**Output:** true

**Example 2:**

**Input:** n = 5
**Output:** false

**Example 3:**

**Input:** n = 1
**Output:** true

**Constraints:**

*   `-231 <= n <= 231 - 1`

**Follow up:** Could you solve it without loops/recursion?

## A solution in Java

```java
public boolean isPowerOfFour(int n) {
    if (n <= 0) return false;
    while (n % 4 == 0) {
        n /= 4;
    }
    return n == 1;
}
```
The algorithm checks if a given integer `n` is a power of 4 or not. If `n` is less than or equal to 0, it immediately returns `false`. Otherwise, it enters a loop that checks if `n` is divisible by 4. If it is, it keeps dividing the number by 4 until it is no longer divisible by 4. After the loop, the algorithm checks whether the final remainder is 1 or not. If the remainder is 1, the number was initially a power of 4, and it returns `true`. Otherwise, it returns `false`.

## A solution in C++

```cpp
bool isPowerOfFour(int n) {
    if (n <= 0) return false;
    while (n % 4 == 0) {
        n /= 4;
    }
    return n == 1;
}
```
The algorithm checks if a given integer `n` is a power of 4 or not. If `n` is less than or equal to 0, it immediately returns `false`. Otherwise, it enters a loop that checks if `n` is divisible by 4. If it is, it keeps dividing the number by 4 until it is no longer divisible by 4. After the loop, the algorithm checks whether the final remainder is 1 or not. If the remainder is 1, the number was initially a power of 4, and it returns `true`. Otherwise, it returns `false`.

## A solution in Python

```python
def isPowerOfFour(n: int) -> bool:
    if n <= 0:
        return False
    while n % 4 == 0:
        n //= 4
    return n == 1
```
The algorithm checks if a given integer `n` is a power of 4 or not. If `n` is less than or equal to 0, it immediately returns `false`. Otherwise, it enters a loop that checks if `n` is divisible by 4. If it is, it keeps dividing the number by 4 until it is no longer divisible by 4. After the loop, the algorithm checks whether the final remainder is 1 or not. If the remainder is 1, the number was initially a power of 4, and it returns `true`. Otherwise, it returns `false`.

## A solution in Javascript

```javascript
function isPowerOfFour(n) {
    if (n <= 0) return false;
    while (n % 4 === 0) {
        n = Math.floor(n / 4);
    }
    return n === 1;
}
```
The algorithm checks if a given integer `n` is a power of 4 or not. If `n` is less than or equal to 0, it immediately returns `false`. Otherwise, it enters a loop that checks if `n` is divisible by 4. If it is, it keeps dividing the number by 4 until it is no longer divisible by 4. After the loop, the algorithm checks whether the final remainder is 1 or not. If the remainder is 1, the number was initially a power of 4, and it returns `true`. Otherwise, it returns `false`.


# Integer Break

- slug: integer-break
- difficulty: Medium

Given an integer `n`, break it into the sum of `k` **positive integers**, where `k >= 2`, and maximize the product of those integers.

Return _the maximum product you can get_.

**Example 1:**

**Input:** n = 2
**Output:** 1
**Explanation:** 2 = 1 + 1, 1 \* 1 = 1.

**Example 2:**

**Input:** n = 10
**Output:** 36
**Explanation:** 10 = 3 + 3 + 4, 3 \* 3 \* 4 = 36.

**Constraints:**

*   `2 <= n <= 58`

## A solution in Java

```java
public int integerBreak(int n) {
    if (n <= 3) return n - 1;
    int product = 1;
    while (n > 4) {
        product *= 3;
        n -= 3;
    }
    return product * n;
}
```
The algorithm is based on the fact that the optimal factorization for the integer n consists of as many 3s as possible, and maybe a 2 or a 4. If we have a factor composed of five 1s, we can always break it into two 2s and a 3, which has a larger product (2 * 2 * 3 > 1 * 1 * 1 * 1 * 1). This means that the factors included in the final product will never be smaller than 2.

The solution is to repeatedly subtract 3 from the given number `n` and multiply the product by 3. If we end up with `n` being 2, 3, or 4 after this process, we multiply the product by `n` and return it.

This solution runs in O(n) time complexity.

## A solution in C++

```cpp
int integerBreak(int n) {
    if (n <= 3) return n - 1;
    int product = 1;
    while (n > 4) {
        product *= 3;
        n -= 3;
    }
    return product * n;
}
```
The algorithm is based on the fact that the optimal factorization for the integer n consists of as many 3s as possible, and maybe a 2 or a 4. If we have a factor composed of five 1s, we can always break it into two 2s and a 3, which has a larger product (2 * 2 * 3 > 1 * 1 * 1 * 1 * 1). This means that the factors included in the final product will never be smaller than 2.

The solution is to repeatedly subtract 3 from the given number `n` and multiply the product by 3. If we end up with `n` being 2, 3, or 4 after this process, we multiply the product by `n` and return it.

This solution runs in O(n) time complexity.

## A solution in Python

```python
def integer_break(n):
    if n <= 3:
        return n - 1
    product = 1
    while n > 4:
        product *= 3
        n -= 3
    return product * n
```
The algorithm is based on the fact that the optimal factorization for the integer n consists of as many 3s as possible, and maybe a 2 or a 4. If we have a factor composed of five 1s, we can always break it into two 2s and a 3, which has a larger product (2 * 2 * 3 > 1 * 1 * 1 * 1 * 1). This means that the factors included in the final product will never be smaller than 2.

The solution is to repeatedly subtract 3 from the given number `n` and multiply the product by 3. If we end up with `n` being 2, 3, or 4 after this process, we multiply the product by `n` and return it.

This solution runs in O(n) time complexity.

## A solution in Javascript

```javascript
function integerBreak(n) {
    if (n <= 3) return n - 1;
    let product = 1;
    while (n > 4) {
        product *= 3;
        n -= 3;
    }
    return product * n;
}
```
The algorithm is based on the fact that the optimal factorization for the integer n consists of as many 3s as possible, and maybe a 2 or a 4. If we have a factor composed of five 1s, we can always break it into two 2s and a 3, which has a larger product (2 * 2 * 3 > 1 * 1 * 1 * 1 * 1). This means that the factors included in the final product will never be smaller than 2.

The solution is to repeatedly subtract 3 from the given number `n` and multiply the product by 3. If we end up with `n` being 2, 3, or 4 after this process, we multiply the product by `n` and return it.

This solution runs in O(n) time complexity.


# Reverse String

- slug: reverse-string
- difficulty: Easy

Write a function that reverses a string. The input string is given as an array of characters `s`.

You must do this by modifying the input array [in-place](https://en.wikipedia.org/wiki/In-place_algorithm) with `O(1)` extra memory.

**Example 1:**

**Input:** s = \["h","e","l","l","o"\]
**Output:** \["o","l","l","e","h"\]

**Example 2:**

**Input:** s = \["H","a","n","n","a","h"\]
**Output:** \["h","a","n","n","a","H"\]

**Constraints:**

*   `1 <= s.length <= 105`
*   `s[i]` is a [printable ascii character](https://en.wikipedia.org/wiki/ASCII#Printable_characters).

## A solution in Java

```java
public void reverseString(char[] s) {
    int left = 0, right = s.length - 1;
    while (left < right) {
        char temp = s[left];
        s[left++] = s[right];
        s[right--] = temp;
    }
}
```
The algorithm follows a two-pointer approach. Initialize two pointers, `left` and `right`, at the beginning and end of the input array. Using a while loop, run until the `left` pointer is greater than or equal to the `right` pointer. In the loop, swap the elements at the `left` and `right` pointers. Increment the `left` pointer and decrement the `right` pointer. Repeat this process until the loop terminates, which means the entire string has been reversed.

## A solution in C++

```cpp
void reverseString(vector<char>& s) {
    int left = 0, right = s.size() - 1;
    while (left < right) {
        swap(s[left++], s[right--]);
    }
}
```
The algorithm follows a two-pointer approach. Initialize two pointers, `left` and `right`, at the beginning and end of the input array. Using a while loop, run until the `left` pointer is greater than or equal to the `right` pointer. In the loop, swap the elements at the `left` and `right` pointers. Increment the `left` pointer and decrement the `right` pointer. Repeat this process until the loop terminates, which means the entire string has been reversed.

## A solution in Python

```python
def reverseString(s: List[str]) -> None:
    left, right = 0, len(s) - 1
    while left < right:
        s[left], s[right] = s[right], s[left]
        left, right = left + 1, right - 1
```
The algorithm follows a two-pointer approach. Initialize two pointers, `left` and `right`, at the beginning and end of the input array. Using a while loop, run until the `left` pointer is greater than or equal to the `right` pointer. In the loop, swap the elements at the `left` and `right` pointers. Increment the `left` pointer and decrement the `right` pointer. Repeat this process until the loop terminates, which means the entire string has been reversed.

## A solution in Javascript

```javascript
function reverseString(s) {
    let left = 0, right = s.length - 1;
    while (left < right) {
        [s[left], s[right]] = [s[right], s[left]];
        left++;
        right--;
    }
}
```
The algorithm follows a two-pointer approach. Initialize two pointers, `left` and `right`, at the beginning and end of the input array. Using a while loop, run until the `left` pointer is greater than or equal to the `right` pointer. In the loop, swap the elements at the `left` and `right` pointers. Increment the `left` pointer and decrement the `right` pointer. Repeat this process until the loop terminates, which means the entire string has been reversed.


# Reverse Vowels of a String

- slug: reverse-vowels-of-a-string
- difficulty: Easy

Given a string `s`, reverse only all the vowels in the string and return it.

The vowels are `'a'`, `'e'`, `'i'`, `'o'`, and `'u'`, and they can appear in both lower and upper cases, more than once.

**Example 1:**

**Input:** s = "hello"
**Output:** "holle"

**Example 2:**

**Input:** s = "leetcode"
**Output:** "leotcede"

**Constraints:**

*   `1 <= s.length <= 3 * 105`
*   `s` consist of **printable ASCII** characters.

## A solution in Java

```java
import java.util.HashSet;
import java.util.Set;

public String reverseVowels(String s) {
    int i = 0, j = s.length() - 1;
    char[] result = new char[s.length()];
    Set<Character> vowels = new HashSet<>();
    for (char c : "aeiouAEIOU".toCharArray()) {
        vowels.add(c);
    }
    while (i <= j) {
        char ci = s.charAt(i);
        char cj = s.charAt(j);
        if (!vowels.contains(ci)) {
            result[i++] = ci;
        } else if (!vowels.contains(cj)) {
            result[j--] = cj;
        } else {
            result[i++] = cj;
            result[j--] = ci;
        }
    }
    return new String(result);
}
```
The algorithm uses two-pointer technique to iterate over the string from both the start and the end. It then checks if the characters at the pointers are vowels or not. If the characters are not vowels, the pointers move accordingly. If both characters are vowels, they are swapped and the pointers move forward. This process continues until the pointers meet in the middle of the string or cross each other. Finally, the modified string is returned.

## A solution in C++

```cpp
#include <algorithm>
#include <string>

std::string reverseVowels(std::string s) {
    int i = 0, j = s.size() - 1;
    while (i < j) {
        if (!isVowel(s[i])) {
            i++;
        } else if (!isVowel(s[j])) {
            j--;
        } else {
            std::swap(s[i], s[j]);
            i++;
            j--;
        }
    }
    return s;
}

bool isVowel(char c) {
    c = std::tolower(c);
    return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
```
The algorithm uses two-pointer technique to iterate over the string from both the start and the end. It then checks if the characters at the pointers are vowels or not. If the characters are not vowels, the pointers move accordingly. If both characters are vowels, they are swapped and the pointers move forward. This process continues until the pointers meet in the middle of the string or cross each other. Finally, the modified string is returned.

## A solution in Python

```python
def reverseVowels(s: str) -> str:
    vowels = set('aeiouAEIOU')
    s_list = list(s)
    i, j = 0, len(s) - 1
    while i < j:
        if s_list[i] not in vowels:
            i += 1
        elif s_list[j] not in vowels:
            j -= 1
        else:
            s_list[i], s_list[j] = s_list[j], s_list[i]
            i += 1
            j -= 1
    return ''.join(s_list)
```
The algorithm uses two-pointer technique to iterate over the string from both the start and the end. It then checks if the characters at the pointers are vowels or not. If the characters are not vowels, the pointers move accordingly. If both characters are vowels, they are swapped and the pointers move forward. This process continues until the pointers meet in the middle of the string or cross each other. Finally, the modified string is returned.

## A solution in Javascript

```javascript
function reverseVowels(s) {
    const vowels = new Set('aeiouAEIOU');
    let chars = Array.from(s);
    let i = 0, j = s.length - 1;
    while (i < j) {
        if (!vowels.has(chars[i])) {
            i++;
        } else if (!vowels.has(chars[j])) {
            j--;
        } else {
            [chars[i], chars[j]] = [chars[j], chars[i]];
            i++;
            j--;
        }
    }
    return chars.join('');
}
```
The algorithm uses two-pointer technique to iterate over the string from both the start and the end. It then checks if the characters at the pointers are vowels or not. If the characters are not vowels, the pointers move accordingly. If both characters are vowels, they are swapped and the pointers move forward. This process continues until the pointers meet in the middle of the string or cross each other. Finally, the modified string is returned.