Friday, March 23, 2018

LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal and LeetCode 106 as follow up

LeetCode 105

Yifeng Zeng

Description

Given preorder and inorder traversal of a tree, construct the binary tree.
Note: You may assume that duplicates do not exist in the tree.
For example, given
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Return the following binary tree:
    3
   / \
  9  20
    /  \
   15   7

Idea Report

We can come up an example so it's easier to explain, I will just use the above example. For the preorder array, the first number [3] is always the root. Then we can find this root number [3] in the inorder array. The left part of [3] is [9], this is the whole left children of root [3], the right part of [3] in inorder array is [15,20,7], these are the whole right children of root [3]. By counting how many left childre, and right children of root [3], we would know where those children are in the preorder array. Which the index is 1 [9], and 3,4,5 [20,15,7] in the preorder array. Because the first index of the subarray is the child root, we can know index 1[nodex 9] is our left child, index 3[node 20] is our right child. By passing the start and end index of the inorder array, and the child root index in the preorder array, we can recursivly build up this tree.
Code
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return helper(preorder, inorder, 0, 0, inorder.length - 1);
    }

    private TreeNode helper(int[] preorder, int[] inorder,
                            int preIndex, int inStart, int inEnd) {
        if (inStart > inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[preIndex]);
        int index = inStart;
        while (index <= inEnd) {
            if (inorder[index] == preorder[preIndex]) {
                break;
            }
            index++;
        }

        root.left = helper(preorder, inorder, preIndex + 1, inStart, index - 1);
        root.right = helper(preorder, inorder, preIndex
                            + (index - inStart) + 1, index + 1, inEnd);
        return root;
    }
}

Summary

  • Divide and conquer, build up the root at the current level, and recursively get the left child and right child.
  • Sometimes using a helper() function with the start and end index may do the trick.
  • Follow up is the same idea just a different order (from nums.length - 1 to 0).

Follow up

LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
Basically the same as LC 105, because postorder reversed is the root-right-left preorder, so just start from the end of the int[] postorder array, and postIndex - 1 is the right child index, and calculate the left child index in the postorder array.
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return helper(inorder, postorder, postorder.length - 1,
                      0, inorder.length - 1);
    }

    private TreeNode helper(int[] inorder, int[] postorder,
                            int postIndex, int inStart, int inEnd) {
        if (inStart > inEnd || postIndex < 0) {
            return null;
        }

        TreeNode root = new TreeNode(postorder[postIndex]);
        int index = inStart;
        while (index <= inEnd) {
            if (inorder[index] == postorder[postIndex]) {
                break;
            }
            index++;
        }
        root.right = helper(inorder, postorder, postIndex - 1,
                            index + 1, inEnd);
        root.left = helper(inorder, postorder, postIndex - (inEnd - index) - 1,
                           inStart, index - 1);
        return root;
    }
}

How to increase the Java stack size?


https://stackoverflow.com/questions/3700459/how-to-increase-the-java-stack-size

Quote:
"
$ javac TT.java
$ java -Xss4m TT
"

“How to Increase the Java Stack Size?” Stack Overflow, stackoverflow.com/questions/3700459/how-to-increase-the-java-stack-size.

Monday, March 19, 2018

LeetCode 25. Reverse Nodes in k-Group

LeetCode 25

Yifeng Zeng

Description

Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
You may not alter the values in the nodes, only nodes itself may be changed.
Only constant memory is allowed.
For example,
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5

Idea Report

We can reduce the problem into a series of reverse linked list sub problem. First we get k nodes, reverse them and get the next k nodes, reverse them, and get the next k nodes and so on. So to reverse k nodes, we can have a seperate function (reverseK()) to implement it. But how can we find where it starts for the next k nodes? We can simply return it using the reverseK() funciton. So the input the of the reverseK() function is the previous node of the current k nodes to reverse (reversing k nodes need the previos node). And we store the node that should be the previous node of next k nodes, we return it after reverse the current k nodes. If there is less than k nodes, we simply don't need reverse what's left, and simply return a null to stop the outer loop and stop reversing.
Code
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null || head.next == null || k <= 1) {
            return head;
        }

        ListNode dummy = new ListNode(0);
        dummy.next = head;
        head = dummy;

        while (head != null) {
            head = reverseK(head, k);
        }

        return dummy.next;
    }

    private ListNode reverseK(ListNode head, int k) {
        ListNode n1 = head.next; // node 1
        ListNode nk = head; // node k
        for (int i = 0; i < k; i++) {
            nk = nk.next;
            if (nk == null) {
                return null;
            }
        }
        ListNode nkn = nk.next; // node k's next

        ListNode prev = nkn; // previous for reverse
        ListNode cur = n1; // current for reverse
        while (cur != nkn) {
            ListNode temp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = temp;
        }

        head.next = nk;

        return n1;
    }
}

Summary

  • Reduce a big problem into a sequence of sub problems.

LeetCode 166. Fraction to Recurring Decimal

LeetCode 166

Yifeng Zeng

Description

Given two integers representing the numerator and denominator of a fraction, return the fraction in string format.
If the fractional part is repeating, enclose the repeating part in parentheses.
For example,
Given numerator = 1, denominator = 2, return "0.5".
Given numerator = 2, denominator = 1, return "2".
Given numerator = 2, denominator = 3, return "0.(6)".

Idea Report

This is a step by step problem. If the quotient is less that 0, we append a '-' to the begining. And we append the integral part then we handle the decimal part. To handle the recurring decimal, we need store each remainder we encountered, if we encouter the same remainder again, that means we have the recurring decimal. When storing each remainder, we also need to store where it starts, just in order to put the left parentheses if we need to.
Code
class Solution {

    public String fractionToDecimal(int numerator, int denominator) {
        if (denominator == 0) {
            return "INF";
        }
        if (numerator == 0) {
            return "0";
        }

        StringBuilder sb = new StringBuilder();
        if ((numerator < 0) ^ (denominator < 0)) {
            sb.append("-");
        }

        long a = Math.abs((long) numerator);
        long b = Math.abs((long) denominator);
        sb.append(a / b);
        a = a % b;
        if (a == 0) {
            return sb.toString();
        }

        sb.append(".");
        Map<String, Integer> map = new HashMap<>();
        map.put(a + "", sb.length());
        a *= 10;

        while (a != 0) {
            long quotient = a / b;
            long remainder = a % b;
            sb.append(quotient + "");
            if (map.containsKey(remainder + "")) {
                sb.insert(map.get(remainder + ""), "(");
                sb.append(")");
                break;
            }
            map.put(remainder + "", sb.length());
            a = remainder * 10;
        }


        return sb.toString();
    }
}

Summary

  • First define the steps, and then code those step by step.

Basic Calculator Template

Basic Calculator Template

class Solution {
    public int calculate(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        Deque<Integer> nums = new ArrayDeque<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isDigit(ch)) {
                int n = 0;
                while (i < s.length() && Character.isDigit(s.charAt(i))) {
                    n = n * 10 + s.charAt(i++) - '0';
                }
                nums.push(n);
                i--;
            } else if (ch == '+' || ch == '-') {
                while (!stack.isEmpty() && stack.peek() != '(') {
                    pop(stack, nums);
                }
                stack.push(ch);
            } else if (ch == '*' || ch == '/') {
                while (!stack.isEmpty() && stack.peek() != '('
                       && stack.peek() != '+' && stack.peek() != '-') {
                    pop(stack, nums);
                }
                stack.push(ch);
            } else if (ch == ')') {
                while (stack.peek() != '(') {
                    pop(stack, nums);
                }
                stack.pop();
            } else if  (ch == '(') {
                stack.push(ch);
            }
        }

        while (!stack.isEmpty()) {
            pop(stack, nums);
        }
        return nums.pop();
    }

    private void pop(Deque<Character> stack, Deque<Integer> nums) {
        int n1 = nums.pop();
        int n2 = nums.pop();
        char op = stack.pop();
        if (op == '+') {
            nums.push(n2 + n1);
        } else if (op == '-') {
            nums.push(n2 - n1);
        } else if (op == '*') {
            nums.push(n2 * n1);
        } else if (op == '/') {
            nums.push(n2 / n1);
        }
    }
}

LeetCode 394. Decode String

LeetCode 394

Yifeng Zeng

Description

Given an encoded string, return it's decoded string.
The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer.
You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc.
Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 2[4].
Examples:
s = "3[a]2[bc]", return "aaabcbc".
s = "3[a2[c]]", return "accaccacc".
s = "2[abc]3[cd]ef", return "abcabccdcdcdef".

Idea Report

This is a very straight forward step by step problem. We can check each character (ch), and treat differernt kinds of characters differerntly. We may want two stacks to store our information, one "stack" to store our parentheses and the the letters, another "nums" to store out numbers.
  • If ch is a digit, we have a while loop to record this number because it may not be just single digit.
  • If ch is a letter, we just store it for future use.
  • If ch is '[', we store it for future use. When we meet a ']', this '[' is our signal to stop.
  • If ch is ']', we pop out all the letters until we meet a "[". And then we duplicate n times of these poped out letters, n is from the "nums" stack where we store the how many time a string should be duplicated.
In the end, all the letters has correct duplcations in the "stack" without any parentheses, so we just pop everything out reverse it and return.
Code
class Solution {
    public String decodeString(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        Deque<Integer> nums = new ArrayDeque<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isDigit(ch)) {
                int n = 0;
                while (Character.isDigit(s.charAt(i))) {
                    n = n * 10 + s.charAt(i) - '0';
                    i++;
                }
                nums.push(n);
                i--;
            } else if (ch == '[') {
                stack.push(ch);
            } else if (ch ==']') {
                pop(stack, nums.isEmpty() ? 1 : nums.pop());
            } else if (Character.isLetter(ch)) {
                stack.push(ch);
            }
        }

        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        return sb.reverse().toString();
    }

    private void pop(Deque<Character> stack, int n) {
        List<Character> helper = new ArrayList<>();
        while (!stack.isEmpty() && stack.peek() != '[') {
            helper.add(stack.pop());
        }
        stack.pop();
        for (int i = 0; i < n; i++) {
            for (int j = helper.size() - 1; j >= 0; j--) {
                stack.push(helper.get(j));
            }
        }
    }
}

Summary

  • This is the basic calculator kind of problem, we can reduce the problem into a series of sub problems. When a character is number, character, "+""-", "*""/", "[" or "]".