Leetcode 102. Binary Tree Level Order Traversal

Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).

For example:
Given binary tree [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

return its level order traversal as:

[
  [3],
  [9,20],
  [15,7]
]

Analysis:

Use queue.

Time complexity: O(n)

Space complexity: O(n)

Code is below:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<List> levelOrder(TreeNode root) {
        // O(n) BFS
        
        List<List> result = new ArrayList<>();
        
        if (root == null) {
            return result;
        }
        
        Queue queue = new LinkedList<>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            ArrayList level = new ArrayList<>();
            int size = queue.size();
            
            for (int i = 0; i < size; i++) {
                // poll the head
                TreeNode head = queue.poll();
                level.add(head.val);
                
                // add the left subnode
                if (head.left != null) {
                    queue.offer(head.left);
                }
                
                // add the right subnode
                if (head.right != null) {
                    queue.offer(head.right);
                }
            }
            result.add(level);
        }
        return result;
    }
}

Leetcode 235 & 236. Lowest Common Ancestor of a Binary (Search) Tree

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”

        _______6______
       /              \
    ___2__          ___8__
   /      \        /      \
   0      _4       7       9
         /  \
         3   5

For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

Analysis:

Check for the properties of the BST. If root.val > both p.val and q.val, we know the lca is in the left side, then goto the left subtree. If root.val < both p.val & q.val, then go to right subtree. Otherwise, return root.

Time complexity: O(logn)

Space complexity: O(1)

Code is below:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // O(logn)
        
        if (root == null || p == null || q == null) {
            return null;
        }
        
        if (root.val > p.val && root.val > q.val) {
            // left sub tree
            return lowestCommonAncestor(root.left, p, q);
        } else if (root.val < p.val && root.val < q.val) {
            // right sub tree
            return lowestCommonAncestor(root.right, p, q);
        } else {
            return root;
        }
    }
}

Followup:

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”

        _______3______
       /              \
    ___5__          ___1__
   /      \        /      \
   6      _2       0       8
         /  \
         7   4

For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. Another example is LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.

Analysis:

No properties similar to BST. We can use divide and conquer. Divide the tree to be the left and right. If left & right not null, return root. If left not null, return left, otherwise return right.

Time complexity: O(n)

Space complexity: O(1)

Code is below:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // O(n)
        
        if (root == null || root == p || root == q) {
            return root;
        }
        
        // divide: get the left and right lca
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        
        // conquer: check for the left and right
        // If left and right both exits, then return root
        if (left != null && right != null) {
            return root;
        }
        
        if (left != null) {
            return left;
        }
        
        if (right != null) {
            return right;
        }
        
        return null;
    }
}

Leetcode 21. Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

Analysis:

Make a dummy node, compare the two values in the two lists. If one is smaller, add it to the head. Update the head to next.

Note: the two lists may not be the same length. After all iteration, we need to check the lefts and add them to the result.

Time complexity: O(n)

Space complexity: O(1)

Code is below:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // O(n)
        
        if (l1 == null) {
            return l2;
        }
        
        if (l2 == null) {
            return l1;
        }
        
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                l1 = l1.next;
            } else {
                cur.next = l2;
                l2 = l2.next;
            }
            
            cur = cur.next;
        }
        
        // Check if there're lefts
        if (l1 != null) {
            cur.next = l1;
        }
        if (l2 != null) {
            cur.next = l2;
        }
        
        return dummy.next;
    }
}

Leetcode 53. Maximum Subarray

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

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

click to show more practice.

More practice:If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle

Analysis:

Keep two values, one is the sum for all values. One is the global max sum. If the sum is less than 0, make it to be 0.

Time complexity: O(n)

Space complexity: O(1)

Code is below:

public class Solution {
    public int maxSubArray(int[] nums) {
        // O(n)
        
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        int max = Integer.MIN_VALUE;
        int sum = 0;
        
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            max = Math.max(max, sum);
            sum = Math.max(sum, 0);
        }
        
        return max;
    }
}

Leetcode 238. Product of Array Except Self

Given an array of n integers where n > 1, nums, return an array output such that output[i] is equal to the product of all the elements ofnums except nums[i].

Solve it without division and in O(n).

For example, given [1,2,3,4], return [24,12,8,6].

Follow up:
Could you solve it with constant space complexity? (Note: The output array does not count as extra space for the purpose of space complexity analysis.)

Analysis:

To get the products of all others, we can have an array of the same length. Then go through the array, get all products for all lefts. Then Go from the back and multiply all values on the right.

Note: we need to set up the first value res[0]  to be 1 initially.

Time complexity: O(n)

Space complexity: O(n)

Code is below:

public class Solution {
    public int[] productExceptSelf(int[] nums) {
        // O(n) O(1)
        
        if (nums == null || nums.length == 0) {
            return nums;
        }
        
        int[] res = new int[nums.length];
        int left = 1;
        int right = 1;
        
        res[0] = 1;
        
        // Get the product of all lefts
        for (int i = 1; i < nums.length; i++) {             left = left * nums[i - 1];             res[i] = left;         }                  // Go from the back, get the product of all rights         for (int i = nums.length - 2; i >= 0; i--) {
            right = right * nums[i + 1];
            res[i] = right * res[i];
        }
        
        return res;
    }
}

Leetcode 1, 167 & 170 Two Sum I, II && III

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

Analysis:

To get two values which the sum of them equals to the target, the brute force one is to have 2 for loop to go over the array twice. This could take O(n^2).

So consider how to get an value very quick, we can have hashtable, which can quickly get the value in nearly O(1).

So basically, what we can do it to put all values in a hash table. Then go over it again to find another value.

Note: here we use the value as key and the index as the value.

Time complexity: O(n)

Space complexity: O(n)

The code is below:

public class Solution {
    public int[] twoSum(int[] nums, int target) {
        // Hashtable O(n) O(n)
        
        // Corner case
        if (nums == null || nums.length == 0) {
            return null;
        }
        
        HashMap<Integer, Integer> map = new HashMap<>();
        
        // number as key, index as value
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        
        for (int i = 0; i < nums.length; i++) {
            int temp = target - nums[i];
            if (map.containsKey(temp) && map.get(temp) != i) {
                return new int[] {i, map.get(temp)};
            }
        }
        
        return null;
    }
}

 

Follow up:

Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution.

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

Analysis:

Sorted array? Very familiar? Yes, binary search.

Also we can use another powerful method called two pointers: we can have a start and end. If start + end = target, then return them. If start + end < target, we know that start is small, so we add start by 1. Otherwise reduce the end by 1.

Time complexity: O(n)

Space complexity: O(1)

The code is below:

public class Solution {
    public int[] twoSum(int[] numbers, int target) {
        //Two pointers O(n) O(1)
        
        // Corner case
        if (numbers == null || numbers.length == 0) {
            return null;
        }
        
        int start = 0;
        int end = numbers.length - 1;
        
        while (start < end) {
            if (numbers[start] + numbers[end] == target) {
                return new int[] {start + 1, end + 1};
            } else if (numbers[start] + numbers[end] < target) {
                start++;
            } else {
                end--;
            }
        }
        return null;
    }
}

Followup:
Design and implement a TwoSum class. It should support the following operations: add and find.

add – Add the number to an internal data structure.
find – Find if there exists any pair of numbers which sum is equal to the value.

For example,
add(1); add(3); add(5);
find(4) -> true
find(7) -> false

Analysis:

This is more like a design problem. So we can use the similar method to have a hash table to store the value. For adding, we can use map.put() and for finding, we can go through the entrySet and find another value.

Note: for special case where we have multiple values to be the same, like 0, 0, 0 if we want to find 0, it also should return true. So we should add a condition that, if i == j then the occurrence should be larger than 1.

Time complexity: O(n)

Space complexity: O(n)

The code is below:

public class TwoSum {

    HashMap<Integer, Integer> map = new HashMap<>();

    // Add the number to an internal data structure.
	public void add(int number) {
	    if (map.containsKey(number)) {
	        map.put(number, map.get(number) + 1);
	    } else {
	        map.put(number, 1);
	    }
	}

    // Find if there exists any pair of numbers which sum is equal to the value.
    public boolean find(int value) {
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            // Find i + j == value
            int i = entry.getKey();
            int j = value - i;
            
            // special case when i == j 
            if ((i == j && entry.getValue() > 1) || (i != j && map.containsKey(j))) {
                return true;
            }
        }
        return false;
    }
	
}


// Your TwoSum object will be instantiated and called as such:
// TwoSum twoSum = new TwoSum();
// twoSum.add(number);
// twoSum.find(value);

Leetcode 367. Valid Perfect Square

Given a positive integer num, write a function which returns True if num is a perfect square else False.

Note: Do not use any built-in library function such as sqrt.

Example 1:

Input: 16
Returns: True

Example 2:

Input: 14
Returns: False

Analysis:

This problem is the same as the problem of leetcode 69: https://codebysteven.wordpress.com/2016/09/08/leetcode-69-sqrtx/

Basically we can just change the return value from double to be boolean.

Time complexity: O(logn)

Space complexity: O(1)

Below is the code:

public class Solution {
    public boolean isPerfectSquare(int num) {
        // O(logn) binary search
        
        // Corner case
        if (num == 0 || num == 1) {
            return true;
        }
        
        long start = 1;
        long end = num;
        
        while (start + 1 < end) {
            long mid = start + (end - start) / 2;
            
            if (mid * mid == num) {
                return true;
            } else if (mid * mid < num) {
                start = mid;
            } else {
                end = mid;
            }
        }
        
        return false;
    }
}

Leetcode 33 & 81. Search in Rotated Sorted Array I & II

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Analysis:

For rotated array, we must have figure to analysis it. Basically it falls into the following two cases: either the mid is larger than the end, or the mid is smaller than the end.

Then in each case, we can use binary search to find the target value. Note that, we should compare both the start, mid, end with the target.

img_4163

Time complexity: O(logn)

Space complexity: O(1)

Below is the code:

public class Solution {
    public int search(int[] nums, int target) {
        // O(logn) binary search
        
        // Corner case
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int start = 0;
        int end = nums.length - 1;
        
        while (start + 1 < end) {             int mid = start + (end - start) / 2;             if (nums[mid] == target) {                 return mid;             }             // case 1             if (nums[mid] > nums[end]) {
                if (nums[mid] >= target && target >= nums[start]) {
                    end = mid;
                } else {
                    start = mid;
                }
            } else { // case 2
                if (nums[mid] <= target && target <= nums[end]) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
        }
        
        if (nums[start] == target) {
            return start;
        } else if (nums[end] == target) {
            return end;
        }
        
        return -1;
    }
}

Follow up:
Follow up for “Search in Rotated Sorted Array”:
What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Write a function to determine if a given target is in the array.

Analysis:
In this case, if duplicates allowed, then we can not use binary search. So normally just use the brute force method.

The code is below:

public class Solution {
    public boolean search(int[] nums, int target) {
        // O(n) normally won't apprear during interview
        
        if(nums == null || nums.length == 0) {
            return false;
        }
        
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == target) {
                return true;
            }
        }
        
        return false;
    }
}

Leetcode 33. Search in Rotated Sorted Array

Leetcode 33. Search in Rotated Sorted Array

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Analysis:

This is a common problem for Binary search. Here the array is not normally sorted, but rotated. But we can still use the Binary search to reduce the search range to half.

However, we need to add more conditions to check the range, we need to draw two lines here to help us understand the rotated sorted array.

Time complexity: O(logn)

Space complexity: O(1)

public class Solution {
    public int search(int[] nums, int target) {
        // Binary search O(logn)
        
        // Corner Case
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int start = 0;
        int end = nums.length - 1;
        
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            
            if (nums[mid] == target) {
                return mid;
            } 
            
            // One line
            if (nums[start] < nums[mid]) {
                if (nums[start] <= target && nums[mid] >= target) {
                    end = mid;
                } else {
                    start = mid;
                }
            } else { 
                // Another line
                if (nums[mid] <= target && nums[end] >= target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
        }
        
        if (nums[start] == target) {
            return start;
        }
        
        if (nums[end] == target) {
            return end;
        }
        
        return -1;
    }
}

 

Extention: 81. Search in Rotated Sorted Array II

Follow up for “Search in Rotated Sorted Array”:
What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Write a function to determine if a given target is in the array.

If we have duplicates in the array, could we use the binary search?

No!

Because we cannot cut the array to half, as the other half may contain the same number as previous half.

Leetcode 34 Search for a Range

Leetcode 34 Search for a Range

Given a sorted array of integers, find the starting and ending position of a given target value.

Your algorithm’s runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

Analysis

This problem is another common one for Binary search. When searching for an range, we can consider it as searching for the first and last index for the target. Then this could easily lead us to Binary search.

Basically we use twice Binary search to find the first and last occurrence  of the target.

Time complexity: O(logn)

Space complexity: O(1)

Note: the method to find the first and last occurrence is similar but different.

1) When consider the nums[mid] == target, if it’s first, we need to set end = mid. If it’s last, then set start = mid

2) When to check the index, if it’s first, we check from start, while if it’s last, we check from end.

Below is the code:

public class Solution {
    public int[] searchRange(int[] nums, int target) {
        // Corner case
        if (nums == null || nums.length == 0) {
            int[] results = {-1, -1};
            return results;
        }
        
        int[] results = new int[2];
        results[0] = searchLow(nums, target);
        results[1] = searchHigh(nums, target);
        
        return results;
    }
    
    // Search for the first index
    public int searchLow(int[] nums, int target) {
        int start = 0;
        int end = nums.length - 1;
        
        while (start + 1 < end) {             int mid = start + (end - start) / 2;             if (nums[mid] == target) {                 end = mid;             } else if (nums[mid] > target) {
                end = mid;
            } else {
                start = mid;
            }
        }
        
        if (nums[start] == target) {
            return start;
        } else if (nums[end] == target) {
            return end;
        }
        
        return -1;
    }
    
    // Search for the last index
    public int searchHigh(int[] nums, int target) {
        int start = 0;
        int end = nums.length - 1;
        
        while (start + 1 < end) {             int mid = start + (end - start) / 2;             if (nums[mid] == target) {                 start = mid;             } else if (nums[mid] > target) {
                end = mid;
            } else {
                start = mid;
            }
        }
        
        if (nums[end] == target) {
            return end;
        } else if (nums[start] == target) {
            return start;
        }
        
        return -1;       
    }
}