Longest Increasing Continuous subsequence I & II

Longest Increasing Continuous subsequence I 

Give you an integer array (index from 0 to n-1, where n is the size of this array),find the longest increasing continuous subsequence in this array. (The definition of the longest increasing continuous subsequence here can be from right to left or from left to right)

Example

For [5, 4, 2, 1, 3], the LICS is [5, 4, 2, 1], return 4.

For [5, 1, 2, 3, 4], the LICS is [1, 2, 3, 4], return 4.

Note

O(n) time and O(1) extra space.

Solution:

Longest Increasing Continuous subsequence II

Give you an integer matrix (with row size n, column size m),find the longest increasing continuous subsequence in this matrix. (The definition of the longest increasing continuous subsequence here can start at any row or column and go up/down/right/left any direction).

Example

Given a matrix:

<code>[
  [1 ,2 ,3 ,4 ,5],
  [16,17,24,23,6],
  [15,18,25,22,7],
  [14,19,20,21,8],
  [13,12,11,10,9]
]
</code>

return 25

Challenge

O(nm) time and memory.

Solution:

FacebookTwitterGoogle+Share

Longest Increasing Subsequence

Longest Increasing Subsequence

Given a sequence of integers, find the longest increasing subsequence (LIS).

You code should return the length of the LIS.

Example

For [5, 4, 1, 2, 3], the LIS  is [1, 2, 3], return 3

For [4, 2, 4, 5, 3, 7], the LIS is [4, 4, 5, 7], return 4

Challenge

Time complexity O(n^2) or O(nlogn)

Clarification

What’s the definition of longest increasing subsequence?

* The longest increasing subsequence problem is to find a subsequence of a given sequence in which the subsequence’s elements are in sorted order, lowest to highest, and in which the subsequence is as long as possible. This subsequence is not necessarily contiguous, or unique.

* https://en.wikipedia.org/wiki/Longest_common_subsequence_problem

Solution: DP O(n^2)

lis[i]表示结尾为nums[i]的子序列的最长长度。

所以lis[i] =for all the j<i && nums[j]<=nums[i] max(lis[j])+1

其实还有可以优化为lis[i] = lis[j] +1 where nums[j] 是比nums[i]小的数里最大的一个

public int longestIncreasingSubsequence(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    //lis[i] means the lis of nums[0-i] which i is the last element selected in the lis
    int[] lis = new int[nums.length];
    int maxLis = 0;
    for (int i = 0; i < nums.length; i++) {
        lis[i] = 1;
        for (int j = 0; j < i; j++) {
            if (nums[i] >= nums[j]) {
                lis[i] = Math.max(lis[j] + 1, lis[i]);
            }
        }
        maxLis = Math.max(maxLis, lis[i]);
    }
    return maxLis;
}

Best Time to Buy and Sell Stock III

Best Time to Buy and Sell Stock III

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Example

Given an example [4,4,6,1,1,4,2,5], return 6.

Note

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Solution: O(n) Time 先参考 Best Time to Buy and Sell Stock I 的做法

这次的不同点在与要做两次交易,所以我们要找一个分界点,分别算左右两边的一次交易利润和的最大值。所以我们可以做Best Time to Buy and Sell Stock I两次

第一次是从左到右扫[0-i]区间,也就是先确定buyDay,然后根据每个prices[i]来确定sellDay。

第二次是从右到左扫[i-end]区间,也就是先确定sellDay,然后根据每个prices[i]来确定buyDay。

所以这两个循环时间都是O(n)

最后我们要用一个循环来设分界点,然后取

max(maxProfitEndedAt[i]+maxProfitStartedFrom[i+1])

注意这个max不是最终答案,假设只有两天[1,2] 这种情况算下来是0,因为分界点两边都是1天不足以完成交易,而且有可能的情况是第一天买入,最后一天买进,这种情况按两次交易算也无法取到最大值,所以最后结果还要考虑到只做一次交易的情况,因为没有规定必须做两次交易

Math.max(maxWithTwoTransaction, maxProfitEndedAt[length - 1]);

这一部分的时间复杂度也是O(n)

所以整体的时间复杂度就是O(n)

public int maxProfit(int[] prices) {
    if (prices == null || prices.length <= 1) {
        return 0;
    }
    int length = prices.length;
    int[] maxProfitEndedAt = new int[length];
    int[] maxProfitStartedFrom = new int[length];

    //get the max profit from 0 to i with one transaction
    maxProfitEndedAt[0] = 0;
    int buyDay = 0;
    for (int i = 1; i < length; i++) {
        maxProfitEndedAt[i] = Math.max(maxProfitEndedAt[i - 1], prices[i] - prices[buyDay]);
        if (prices[i] < prices[buyDay]) {
            buyDay = i;
        }
    }
    //get the max profit form i to length-1 with one transaction
    maxProfitStartedFrom[length - 1] = 0;
    int sellDay = length - 1;
    for (int i = length - 2; i >= 0; i--) {
        maxProfitStartedFrom[i] = Math.max(maxProfitStartedFrom[i + 1], prices[sellDay] - prices[i]);
        if (prices[i] > prices[sellDay]) {
            sellDay = i;
        }
    }
    //to have two transactions
    //pick a pivot day i, to get the max(maxProfitEndedAt[i]+maxProfitStartedFrom[i+1])
    int maxWithTwoTransaction = 0;
    for (int i = 0; i < length - 2; i++) {
        maxWithTwoTransaction = Math.max(maxWithTwoTransaction,
                                         maxProfitEndedAt[i] + maxProfitStartedFrom[i + 1]);
    }
    return Math.max(maxWithTwoTransaction, maxProfitEndedAt[length - 1]);
}

 

Maximal Square

Maximal Square

Given a 2D binary matrix filled with 0’s and 1’s, find the largest square containing all 1’s and return its area.

Example

For example, given the following matrix:

<code>1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
</code>

Return 4.

Solution: DP side[i][j] 表示以(i,j)这个点为右下角可以组成的最大square的边长

由下图可知,大正方形由三个小正方形和(i,j)这个点这个点决定

maximum square草图

side[i][j] = min(side[i-1][j-1], side[i][j-1], side[i-1][j]) +1 //when matrix[i][j]==1

= 0 //when matrix[i][j] = 0

public int maxSquare(int[][] matrix) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
        return 0;
    }
    //side[i][j] = the length of the max square which (i,j) is the at right bottom corner.
    int[][] side = new int[matrix.length][matrix[0].length];
    int max = 0;
    for (int i = 0; i < matrix.length; i++) {
        side[i][0] = matrix[i][0];
        max = Math.max(max, side[i][0]);
    }
    for (int j = 0; j < matrix[0].length; j++) {
        side[0][j] = matrix[0][j];
        max = Math.max(max, side[0][j]);
    }

    for (int i = 1; i < matrix.length; i++) {
        for (int j = 1; j < matrix[i].length; j++) {
            if (matrix[i][j] == 0) {
                side[i][j] = 0;
            } else {
                side[i][j] = Math.min(side[i - 1][j], Math.min(side[i][j - 1], side[i - 1][j - 1])) + 1;
                max = Math.max(max, side[i][j]);
            }
        }
    }
    return max * max;
}

House Robber

House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected andit will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example

Given [3, 8, 4], return 8.

Challenge

O(n) time and O(1) memory.

Solution: max[i]表示从0-i个房子里,要抢房子i的最大值

max[i] = Math.max(max[i – 2] + A[i], max[i – 1]);

public long houseRobber(int[] A) {
    if (A == null || A.length == 0) {
        return 0;
    }
    if (A.length == 1) {
        return A[0];
    }
    if (A.length == 2) {
        return Math.max(A[0], A[1]);
    }

    //max[i] = for [0-i], the max value if rob house i
    long[] max = new long[A.length];
    max[0] = A[0];
    max[1] = A[1];
    for (int i = 2; i < A.length; i++) {
        max[i] = Math.max(max[i - 2] + A[i], max[i - 1]);
    }
    return Math.max(max[A.length - 1], max[A.length - 2]);
}

Find the Connected Component in the Undirected Graph

Find the Connected Component in the Undirected Graph

Find the number connected component in the undirected graph. Each node in the graph contains a label and a list of its neighbors. (a connected component (or just component) of an undirected graph is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in the supergraph.)

Example

Given graph:

A------B  C
 \     |  | 
  \    |  |
   \   |  |
    \  |  |
      D   E

Return {A,B,D}, {C,E}. Since there are two connected component which is {A,B,D}, {C,E}

Solution: BFS

public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) {
    List<List<Integer>> result = new ArrayList<>();
    if (nodes == null || nodes.size() == 0) {
        return result;
    }
    Queue<UndirectedGraphNode> q = new LinkedList<>();
    q.offer(nodes.get(0));
    List<Integer> component = new ArrayList<>();
    Set<UndirectedGraphNode> visited = new HashSet<>();
    while (!nodes.isEmpty()) {
        if (q.isEmpty()) {
            Collections.sort(component);
            result.add(new ArrayList<>(component));
            q.offer(nodes.get(0));
            component = new ArrayList<>();
        } else {
            UndirectedGraphNode curr = q.poll();
            if (!visited.contains(curr)) {
                visited.add(curr);
                nodes.remove(curr);
                component.add(curr.label);
                for (UndirectedGraphNode neighbor : curr.neighbors) {
                    q.offer(neighbor);
                }
            }
        }
    }
    if (!component.isEmpty()) {
        result.add(component);
    }
    return result;
}

Count of Smaller Number I && II

Count of Smaller Number I 

Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) and an query list. For each query, give you an integer, return the number of element in the array that are smaller that the given integer.

Example

For array [1,2,7,8,5], and queries [1,8,5], return [0,4,2]

Note

We suggest you finish problem Segment Tree Build and Segment Tree Query II first.

Challenge

Could you use three ways to do it.

  1. Just loop
  2. Sort and binary search
  3. Build Segment Tree and Search.

Solution:  Version 1. sort + binary search O(nlogn) Time
//Version 2. Segment Tree. not as efficient as version 1.
//check <Count of Smaller Number II>

public ArrayList<Integer> countOfSmallerNumber(int[] A, int[] queries) {
    //Version 1. sort + binary search
    //Version 2. Segment Tree. not as efficient as version 1.
    //check <Count of Smaller Number before itself>
    ArrayList<Integer> result = new ArrayList<>();
    if (A == null || queries == null) {
        return result;
    }
    if (A.length == 0) {
        for (int i = 0; i < queries.length; i++) {
            result.add(0);
        }
        return result;
    }
    Arrays.sort(A);
    for (int i = 0; i < queries.length; i++) {
        int target = queries[i];
        //find the last element A[i]<target, i+1 is the number
        int start = 0;
        int end = A.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (A[end] < target) {
            result.add(end + 1);
        } else if (A[start] < target) {
            result.add(start + 1);
        } else {
            result.add(0);
        }
    }
    return result;
}

Count of Smaller Number II

Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) . For each elementAi in the array, count the number of element before this element Ai is smaller than it and return count number array.

Example

For array [1,2,7,8,5], return [0,1,2,3,2]

Note

We suggest you finish problem Segment Tree Build, Segment Tree Query II and Count of Smaller Number before itself I first.

Solution: Segment Tree. 这道题不能用sort+binary search的方法了因为顺序不能改变。

建一个0-10000的值型线段树。然后每个叶子节点代表这个值的count。所以在A[i]之前比它小的数的个数就是那个0~A[i]-1这个node 的count值。之后每次Query以后把A[i]那个leaf node count++, 相应回溯更新它所有父亲节点。

时间复杂度是O(m + nlogm) m是值的区间,n是query个数。

这个算法是否最优需要分情况,如果值特别分散且query数特别小,那还不如每次query用O(n)的时间count它之前的比她小的数。

public class SegmentTreeNode {
    int start, end;
    int count;
    SegmentTreeNode left;
    SegmentTreeNode right;
    public SegmentTreeNode(int start, int end) {
        this.start = start;
        this.end = end;
        this.count = 0;
    }
}

public ArrayList<Integer> countOfSmallerNumberII(int[] A) {
    ArrayList<Integer> result = new ArrayList<>();
    if (A == null || A.length == 0) {
        return result;
    }
    SegmentTreeNode node = buildSegmentTree(0, 10000);
    for (int i = 0; i < A.length; i++) {
        if (A[i] == 0) {
            result.add(0);
        } else {
            result.add(query(node, 0, A[i] - 1));
        }
        update(node, A[i]);
    }
    return result;
}

public SegmentTreeNode buildSegmentTree(int start, int end) {
    SegmentTreeNode node = new SegmentTreeNode(start, end);
    if (start < end) {
        int mid = start + (end - start) / 2;
        node.left = buildSegmentTree(start, mid);
        node.right = buildSegmentTree(mid + 1, end);
        node.count = node.left.count + node.right.count;
    }
    return node;
}

public int query(SegmentTreeNode node, int start, int end) {
    if (node.start == start && node.end == end) {
        return node.count;
    }
    int leftCount = 0, rightCount = 0;
    int mid = node.start + (node.end - node.start) / 2;
    if (start <= mid) {
        if (end <= mid) {
            leftCount = query(node.left, start, end);
        } else {
            leftCount = query(node.left, start, mid);
        }
    }
    if (end > mid) {
        if (start > mid) {
            rightCount = query(node.right, start, end);
        } else {
            rightCount = query(node.right, mid + 1, end);
        }
    }
    return leftCount + rightCount;
}

public void update(SegmentTreeNode node, int index) {
    if (node.start == index && node.end == index) {
        node.count = node.count + 1;
    } else {
        int mid = node.start + (node.end - node.start) / 2;
        if (node.start <= index && index <= mid) {
            update(node.left, index);
        } else if (mid < index && index <= node.end) {
            update(node.right, index);
        }
        node.count = node.left.count + node.right.count; //or node.count = node.count+1;
    }
}

Interval Sum I && II

Interval Sum I

Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the sum number between index start and end in the given array, return the result list.

Example

For array [1,2,7,8,5], and queries [(0,4),(1,2),(2,4)], return [23,9,20]

Note

We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first.

Challenge

O(logN) time for each query

Solution: Version 1. prefix sum,
(Version 2. Segment tree: check Interval Sum II)

public ArrayList<Long> intervalSum(int[] A,
                                   ArrayList<Interval> queries) {
    //Version 1. prefix sum,
    //Version 2. Segment tree: check Interval Sum II
    ArrayList<Long> result = new ArrayList<>();
    if (A == null || queries == null || A.length == 0 || queries.size() == 0) {
        return result;
    }
    Long[] prefixSum = new Long[A.length];
    Long sum = 0L;
    for (int i = 0; i < A.length; i++) {
        sum += A[i];
        prefixSum[i] = sum;
    }
    for (Interval interval : queries) {
        if (interval.start == 0) {
            result.add(prefixSum[interval.end]);
        } else {
            result.add(prefixSum[interval.end] - prefixSum[interval.start - 1]);
        }

    }
    return result;
}

Interval Sum II

Given an integer array in the construct method, implement two methods query(start, end) andmodify(index, value):

  • For query(start, end), return the sum from index start to index end in the given array.
  • For modify(index, value), modify the number in the given index to value

Example

Given array A = [1,2,7,8,5].

  • query(0, 2), return 10.
  • modify(0, 4), change A[0] from 1 to 4.
  • query(0, 1), return 6.
  • modify(2, 1), change A[2] from 7 to 1.
  • query(2, 4), return 14.

Note

We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first.

Challenge

O(logN) time for query and modify.

Solution: Segment Tree.

建立:O(n) 时间

查询:O(logN) 时间

更改其中一个元素: O(logN)

public class Solution {
    SegmentTreeNode root;

    public class SegmentTreeNode {
        int start, end;
        int sum;
        SegmentTreeNode left;
        SegmentTreeNode right;
        public SegmentTreeNode(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }
    public Solution(int[] A) {
        if (A == null || A.length == 0) {
            return;
        }
        this.root = buildSegmentTree(A, 0, A.length - 1);
    }

    private SegmentTreeNode buildSegmentTree(int[] A, int start, int end) {
        SegmentTreeNode node = new SegmentTreeNode(start, end);
        if (start == end) {
            node.sum = A[start];
        } else {
            int mid = start + (end - start) / 2;
            node.left = buildSegmentTree(A, start, mid);
            node.right = buildSegmentTree(A, mid + 1, end);
            node.sum = node.left.sum + node.right.sum;
        }
        return node;
    }

    public long query(int start, int end) {
        if (start < this.root.start || end > this.root.end) {
            return -1;
        }
        return query(this.root, start, end);
    }

    private long query(SegmentTreeNode root, int start, int end) {
        if (root.start == start && root.end == end) {
            return root.sum;
        }
        int mid = root.start + (root.end - root.start) / 2;
        long leftSum = 0L, rightSum = 0L;
        if (start <= mid) {
            if (end <= mid) {
                leftSum = query(root.left, start, end);
            } else {
                leftSum = query(root.left, start, mid);
            }
        }
        if (end > mid) {
            if (start > mid) {
                rightSum = query(root.right, start, end);
            } else {
                rightSum = query(root.right, mid + 1, end);
            }
        }
        return leftSum + rightSum;
    }

    public void modify(int index, int value) {
        modify(this.root, index, value);
    }

    public void modify(SegmentTreeNode root, int index, int value) {
        if (root.start == index && root.end == index) {
            root.sum = value;
        } else {
            int mid = root.start + (root.end - root.start) / 2;
            if (index <= mid) {
                modify(root.left, index, value);
            } else {
                modify(root.right, index, value);
            }
            root.sum = root.left.sum + root.right.sum;
        }
    }
}

Maximum Product Subarray

Maximum Product Subarray

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

Example

For example, given the array [2,3,-2,4], the contiguous subarray [2,3] has the largest product = 6.

Solution: One Pass O(n) time.

So for each index i, max(i) = the maximum product ending at i, so we have

max(i) = max(max(i-1)*nums[i], nums[i]) //nums[i]>=0

since you can either append from the previous one, or just start a new contiguous subarray.

Be careful on negative nums, if nums[i] < 0, the equation now becomes:

max(i) = max(min(i-1)*nums[i], nums[i]) //nums[i]<0

public int maxProduct(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    int maxProduct = Integer.MIN_VALUE;
    int preMax = 1;
    int preMin = 1;
    for (int i = 0; i < nums.length; i++) {
        int max, min;
        if (nums[i] >= 0) {
            max = Math.max(preMax * nums[i], nums[i]);
            min = Math.min(preMin * nums[i], nums[i]);
        } else {
            max = Math.max(preMin * nums[i], nums[i]);
            min = Math.min(preMax * nums[i], nums[i]);
        }
        maxProduct = Math.max(maxProduct, max);
        preMax = max;
        preMin = min;
    }
    return maxProduct;
}

Segment Tree

Segment Tree(线段树)

性质:

  1. 二叉树
  2. 线段树的每个节点表示的是一段区间和最大值(或最小值)
  3. 节点的左子树 = 父亲区间分裂后的左区间

节点的右子树 = 父亲区间分裂后的右区间

查询:O(logN) 时间

  1. 查询的区间 和 线段树节点表示的区间 相等 -》直接返回节点的最大值
  2. 查询的区间 被 线段树节点表示的区间 包含 -》递归向下搜索左右子树
  3. 查询的区间 和 线段树节点表示的区间 不相交 -》停止搜索子树
  4. 查询的区间 和 线段树节点表示的区间 相交不相等 -》分裂查找左右子树 eg.max[1,3] = max([1,1], [2,3])

建立:O(n) 时间

  1. 自上而下递归分裂
  2. 自下而上回溯更新

Segment Tree Query

For an integer array (index from 0 to n-1, where n is the size of this array), in the corresponding SegmentTree, each node stores an extra attribute max to denote the maximum number in the interval of the array (index from start to end).

Design a query method with three parameters root, start and end, find the maximum number in the interval [start, end] by the given root of segment tree.

Example

For array [1, 4, 2, 3], the corresponding Segment Tree is:

<code>                  [0, 3, max=4]
                 /             \
          [0,1,max=4]        [2,3,max=3]
          /         \        /         \
   [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3]
</code>

query(root, 1, 1), return 4

query(root, 1, 2), return 4

query(root, 2, 3), return 3

query(root, 0, 2), return 4

Note

It is much easier to understand this problem if you finished Segment Tree Build first.

public int query(SegmentTreeNode root, int start, int end) {
    if (root == null) {
        return -1;
    }
    if (root.start == start && root.end == end) {
        return root.max;
    }
    int mid = root.start + (root.end - root.start) / 2;
    int leftMax = Integer.MIN_VALUE;
    int rightMax = Integer.MIN_VALUE;
    if (start <= mid) {
        if (end <= mid) {
            leftMax = query(root.left, start, end);
        } else {
            leftMax = query(root.left, start, mid);
        }
    }
    if (end > mid) {
        if (start >= mid) {
            rightMax = query(root.right, start, end);
        } else {
            rightMax = query(root.right, mid + 1, end);
        }
    }
    return Math.max(leftMax, rightMax);
}

Segment Tree Query II

For an array, we can build a SegmentTree for it, each node stores an extra attributecount to denote the number of elements in the the array which value is between interval start and end. (The array may not fully filled by elements)

Design a query method with three parameters root, start and end, find the number of elements in the in array’s interval [start, end] by the given root of value SegmentTree.

Example

For array [0, 2, 3], the corresponding value Segment Tree is:

<code>                     [0, 3, count=3]
                     /             \
          [0,1,count=1]             [2,3,count=2]
          /         \               /            \
   [0,0,count=1] [1,1,count=0] [2,2,count=1], [3,3,count=1]
</code>

query(1, 1), return 0

query(1, 2), return 1

query(2, 3), return 2

query(0, 2), return 2

Note

It is much easier to understand this problem if you finished Segment Tree Build and Segment Tree Query first.

public int query(SegmentTreeNode root, int start, int end) {
    if (root == null || start > root.end || end < root.start) {
        return 0;
    }
    if (root.start == start && root.end == end) {
        return root.count;
    }
    int mid = root.start + (root.end - root.start) / 2;
    int leftCount = 0, rightCount = 0;
    if (start <= mid) {
        if (end <= mid) {
            leftCount = query(root.left, Math.max(start, root.start), end);
        } else {
            leftCount = query(root.left, Math.max(start, root.start), mid);
        }
    }
    if (end >= mid) {
        if (start >= mid + 1) {
            rightCount = query(root.right, start, Math.min(end, root.end));
        } else {
            rightCount = query(root.right, mid + 1, Math.min(end, root.end));
        }
    }
    return leftCount + rightCount;
}

Segment Tree Modify

For a Maximum Segment Tree, which each node has an extra value max to store the maximum value in this node’s interval.

Implement a modify function with three parameter root, index and value to change the node’s value with [start, end] = [index, index] to the new given value. Make sure after this change, every node in segment tree still has the max attribute with the correct value.

Example

For segment tree:

<code>                      [1, 4, max=3]
                    /                \
        [1, 2, max=2]                [3, 4, max=3]
       /              \             /             \
[1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3]
</code>

if call modify(root, 2, 4), we can get:

<code>                      [1, 4, max=4]
                    /                \
        [1, 2, max=4]                [3, 4, max=3]
       /              \             /             \
[1, 1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3]
</code>

or call modify(root, 4, 0), we can get:

<code>                      [1, 4, max=2]
                    /                \
        [1, 2, max=2]                [3, 4, max=0]
       /              \             /             \
[1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=0]
</code>
Note

We suggest you finish problem Segment Tree Build and Segment Tree Query first.

Challenge

Do it in O(h) time, h is the height of the segment tree.