Woodstock Blog

a tech blog for general algorithmic interview questions

[LeetCode Plus] Searching a 2D Sorted Matrix

Question

link

Write an efficient algorithm that searches for a value in an n x m table (two-dimensional array). This table is sorted along the rows and columns — that is,

Table[i][j] ≤ Table[i][j + 1],
Table[i][j] ≤ Table[i + 1][j]

Related questions

Search a 2D Matrix.

Searching a 2D Sorted Matrix.

Count negative in a 2D Sorted Matrix.

Analysis

This is a extremely high-freq question. Almost every company will ask.

This question is not to be confused with Search a 2D Matrix.

Solution One: Step-wise Linear Search. Standard solution.

Time = O(n). Worse case 2n steps.

Note that this is the best solution!

begin with the upper right corner, we traverse one step to the left or bottom. For example, the picture below shows the traversed path (the red line) when we search for 13. Essentially, each step we are able to eliminate either a row or a column.

Solution Two: Quad Partition 四分法.

Time = O(n1.58). Analysis can be found in the original question post.

Basic idea is like binary search - get mid and divide. We can then discard ¼ of the matrix. For example: search for 6, we can discard the bottom right sub-matrix.

Solution Three: Diagonal-based binary partition. This is based on previous solution, but better.

Time = O(nlgn).

This is a good solution, but it would fail in a non-square matrix, so…

Code

step-wise linear search

bool stepWise(int mat[][N_MAX], int N, int target, 
              int &row, int &col) {
  if (target < mat[0][0] || target > mat[N-1][N-1]) return false;
  row = 0;
  col = N-1;
  while (row <= N-1 && col >= 0) {
    if (mat[row][col] < target) 
      row++;
    else if (mat[row][col] > target)
      col--;
    else
      return true;
  }
  return false;
}

[NineChap 3.1] Binary Tree DFS and Divide Conquer

DFS

Recursion or While-Loop?

We can use recursion, because unless it’s pre-order traverse, binary tree questions can be difficult.

Solving the problem is more important.

Divide & Conquer Algorithm

  1. Merge Sort
  2. Quick Sort
  3. Most of Binary tree questions

Solution modal

Generally, D&C questions would do 2 things at same time:

  1. Divide - For binary tree, it mean solve left child, and solve right child
  2. Conquer - return result value

A very common type would be validating the left/right children and return -1 if the validation failed. Otherwise, a result value is returned. In this way, by checking the positive/negative sign, we know whether this node is valid, and if valid, we know the returned value.

This idea is extensivelly used among all binary tree questions. See “Lowest Common Ancestor (LCA)” for more details.

Template

Divide & Conquer, link

public class Solution {
    public ResultType traversal(TreeNode root) {
        // null or leaf
        if (root == null) {
            // do something and return;
        }

        // Divide
        ResultType left = traversal(root.left);
        ResultType right = traversal(root.right);

        // Conquer
        ResultType result = Merge from left and right.
        return result;
    }
}

Question list

Traversal

  1. Binary Tree Preorder Traversal

  2. Binary Tree Inorder Traversal

  3. Binary Tree Postorder Traversal

Divide & Conquer

  1. Maximum Depth of Binary Tree

  2. Minimum Depth of Binary Tree

  3. Balanced Binary Tree

  4. Binary Tree Maximum Path Sum - the most important question for this category

Additional

  1. Lowest Common Ancestor Problem

    problem one

    problem two

    problem three

Code

Binary Tree Preorder Traversal

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> ans = new LinkedList<Integer>();
    if (root == null) return ans;
    Stack<TreeNode> stack = new Stack<TreeNode>();
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        ans.add(cur.val);
        if (cur.right != null) {
            stack.push(cur.right);
        } 
        if (cur.left != null) {
            stack.push(cur.left);
        }
    }
    return ans;
}

There is a not-recommended but good-to-know solution of Divide & Conquer (not written by me)

public class Solution {
    public ArrayList<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        // null or leaf
        if (root == null) {
            return result;
        }

        // Divide
        ArrayList<Integer> left = preorderTraversal(root.left);
        ArrayList<Integer> right = preorderTraversal(root.right);

        // Conquer
        result.add(root.val);
        result.addAll(left);
        result.addAll(right);
        return result;
    }
}

Binary Tree Inorder Traversal

Keep traversing left until a NULL is found. When it happens, pop one and traverse right once. Remember this solution!

public ArrayList<Integer> inorderTraversal(TreeNode root) {
    ArrayList<Integer> ans = new ArrayList<Integer>();
    if (root == null) {
        return ans;
    }
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode p = root;
    while (p != null || !stack.isEmpty()) {
        if (p != null) {
            stack.push(p);
            p = p.left;
        }
        else {
            p = stack.pop();
            ans.add(p.val);
            p = p.right;
        }
    }
    return ans;
}

Binary Tree Postorder Traversal

I failed to write the code even after reading the solution. I need to memorize this solution by heart.

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> ans = new ArrayList<Integer>();
    if (root == null) {
        return ans;
    }
    Stack<TreeNode> stack = new Stack<TreeNode>();
    stack.push(root);
    TreeNode pre = null;
    TreeNode cur = null;
    while (!stack.isEmpty()) {
        cur = stack.peek();
        if (pre == null || pre.left == cur || pre.right == cur) {
            if (cur.left != null) {
                stack.push(cur.left);
            } else if (cur.right != null) {
                stack.push(cur.right);
            }
        } else if (cur.left == pre) {
            if (cur.right != null) {
                stack.push(cur.right);
            }
        } else if (cur.right == pre || cur == pre) {
            // note that 'pre' and 'cur' are never going to be apart
            // for more then 1 edge (they can overlap) 
            ans.add(stack.pop().val);
        }
        pre = cur;
    }
    return ans;
}

Maximum Depth of Binary Tree

// 1 minute
public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

Minimum Depth of Binary Tree

public int minDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    return checkLeaf(root);
}

private int checkLeaf(TreeNode node) {
    if (node.left == null && node.right == null) {
        return 1;
    }
    int ll = Integer.MAX_VALUE;
    int rr = Integer.MAX_VALUE;
    if (node.left != null) ll = checkLeaf(node.left);
    if (node.right != null) rr = checkLeaf(node.right);
    return 1 + Math.min(ll, rr);
}

After checking the answer, the code above can be optimized:

public int minDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    return getMin(root);
}

public int getMin(TreeNode root){
    if (root == null) {
        return Integer.MAX_VALUE; // important
    }

    if (root.left == null && root.right == null) {
        return 1;
    }

    return Math.min(getMin(root.left), getMin(root.right)) + 1;
}

Balanced Binary Tree

// 4 minutes
public boolean isBalanced(TreeNode root) {
    return getDepth(root) != -1;
}

private int getDepth(TreeNode node) {
    if (node == null) {
        return 0;
    }
    int ll = getDepth(node.left);
    int rr = getDepth(node.right);
    if (ll == -1 || rr == -1 || Math.abs(ll - rr) > 1) {
        return -1;
    }
    return 1 + Math.max(ll, rr);
}

Binary Tree Maximum Path Sum

Although the following code works:

int max = Integer.MIN_VALUE;

public int maxPathSum(TreeNode root) {
    maxDepth(root);
    return max;
}

private int maxDepth(TreeNode node) {
    if (node == null) {
        return 0;
    }
    int ll = maxDepth(node.left);
    int rr = maxDepth(node.right);
    int currentMaxPath = ll + rr + node.val;
    max = Math.max(max, currentMaxPath);
    return Math.max(0, node.val + Math.max(ll, rr));
}

Mr. Huang said it’s AN EXTREMELY BAD IDEA TO USE GLOBAL VARIABLE in Java. It’s just terrible. Don’t do it.

According to Mr. Huang’s suggestion, I added another class called “ResultType”. This can help me return 2 values at 1 single traversal.

Code is below. One ‘catch-ya’ is when NULL is found, the maxPath should return Integer.MIN_VALUE instead of 0.

This code is much easier for both me and anyone else to understand, so stick to this solution, and never use global variable in Java!

private class ResultType {
    int singlePath, maxPath;
    ResultType(int singlePath, int maxPath) {
        this.singlePath = singlePath;
        this.maxPath = maxPath;
    }
}

public int maxPathSum(TreeNode root) {
    ResultType result = helper(root);
    return result.maxPath;
}

private ResultType helper(TreeNode node) {
    // null case
    if (node == null) {
        return new ResultType(0, Integer.MIN_VALUE);
    }
    // divide
    ResultType ll = helper(node.left);
    ResultType rr = helper(node.right);
    // conquer
    int curSinglePath = Math.max(0, node.val + 
            Math.max(ll.singlePath, rr.singlePath));
    int childMaxPath = Math.max(ll.maxPath, rr.maxPath);
    int curMaxPath = Math.max(childMaxPath, node.val + 
            ll.singlePath + rr.singlePath);
    // done
    return new ResultType(curSinglePath, curMaxPath);
}

Lowest Common Ancestor - I wrote three new posts on this topic:

Problem 1: BST: top-down O(height) solution

Problem 2: Binary Tree: bottom-up O(n) solution

Problem 3: Binary Tree with a link to parent

[NineChap 3.2] Binary Tree BFS

Template (BFS)

BFS can be implemented using either 2 queues (replacing) or 1 queue. Of course 1 queue is better.

link

public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
    ArrayList result = new ArrayList();

    if (root == null)
        return result;

    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);

    while (!queue.isEmpty()) {
        ArrayList<Integer> level = new ArrayList<Integer>(); // important
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            TreeNode head = queue.poll();
            level.add(head.val);
            if (head.left != null)
                queue.offer(head.left);
            if (head.right != null)
                queue.offer(head.right);
        }
        result.add(level); // important
    }

    return result;
}

Question list

BFS

  1. Binary Tree Level Order Traversal

  2. Binary Tree Level Order Traversal II

  3. Binary Tree Zigzag Level Order Traversal

Additional

  1. Construct Binary Tree from Preorder and Inorder

  2. Construct Binary Tree from Inorder and Postorder

Code

First 3 questions are basically same. Below code is for question 1. There is no ‘catch-ya’, it’s very standard code.

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> ans = new LinkedList<List<Integer>>();
    if (root == null) {
        return ans;
    }
    Queue<TreeNode> q = new LinkedList<TreeNode>();
    q.offer(root);
    while(!q.isEmpty()) {
        int size = q.size();
        List<Integer> level = new LinkedList<Integer>();
        for (int i = 0; i < size; i ++) {
            TreeNode node = q.poll();
            level.add(node.val);
            if (node.left != null) {
                q.offer(node.left);
            }
            if (node.right != null) {
                q.offer(node.right);
            }
        }
        ans.add(level);
    }
    return ans;
}

Construct Binary Tree from Preorder and Inorder - written by me

public TreeNode buildTree(int[] preorder, int[] inorder) {
    if (preorder == null || inorder == null || preorder.length != inorder.length) {
        return null;
    }
    return helper(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);
}

private TreeNode helper(int[] preorder, int a, int b, int[] inorder, int c, int d) {
    if (a > b) {
        return null;
    }
    int headVal = preorder[a];
    TreeNode head = new TreeNode(headVal);
    int p = c;
    while (p <= d) {
        if (inorder[p] == headVal) {
            break;
        }
        p ++;
    }
    head.left = helper(preorder, a+1, a+p-c, inorder, c, p-1);
    head.right = helper(preorder, b-d+p+1, b, inorder, p+1, d);
    return head;
}

Construct Binary Tree from Inorder and Postorder - similar to previous code, copied from ninechap

private int findPosition(int[] arr, int start, int end, int key) {
    int i;
    for (i = start; i <= end; i++) {
        if (arr[i] == key) {
            return i;
        }
    }
    return -1;
}

private TreeNode myBuildTree(int[] inorder, int instart, int inend,
        int[] postorder, int poststart, int postend) {
    if (instart > inend) {
        return null;
    }

    TreeNode root = new TreeNode(postorder[postend]);
    int position = findPosition(inorder, instart, inend, postorder[postend]);

    root.left = myBuildTree(inorder, instart, position - 1,
            postorder, poststart, poststart + position - instart - 1);
    root.right = myBuildTree(inorder, position + 1, inend,
            postorder, poststart + position - instart, postend - 1);
    return root;
}

public TreeNode buildTree(int[] inorder, int[] postorder) {
    if (inorder.length != postorder.length) {
        return null;
    }
    return myBuildTree(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);
}

[NineChap 3.3] Binary Search Tree

Question list

BST

  1. Validate Binary Search Tree

  2. Insert a Node in Binary Search Tree

  3. Delete a Node in Binary Search Tree

  4. Search Range in a Binary Search Tree

Additional

  1. Recover Binary Search Tree - used global variable

  2. Convert Sorted Array to Binary Search Tree

  3. Convert Sorted List to Binary Search Tree - used global variable

Code

Validate Binary Search Tree

public boolean isValidBST(TreeNode root) {
    return validate(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
}

private boolean validate(TreeNode node, int min, int max) {
    if (node == null) {
        return true;
    }
    if (node.val <= min || max <= node.val) {
        return false;
    }
    return validate(node.left, min, node.val) & validate(node.right, node.val, max);
}

Insert a Node in Binary Search Tree and Delete a Node in Binary Search Tree are written in a new post.

Search Range in a Binary Search Tree

There is a new post for this.

Recover Binary Search Tree

// 3 global variables used
TreeNode first = null;
TreeNode second = null; 
TreeNode current = null;

public void recoverTree(TreeNode root) {
    traverse(root);
    if (first != null) {
        int temp = first.val;
        first.val = second.val;
        second.val = temp;
    }
}

public void traverse(TreeNode root) {
    if (root == null) {
        return;
    }
    traverse(root.left);
    // inorder traversal
    if (current != null && current.val > root.val) {
        if (first == null) {
            first = current;
        }
        second = root;
    }
    current = root;
    traverse(root.right);
}

Convert Sorted Array to Binary Search Tree

public TreeNode sortedArrayToBST(int[] num) {
    if (num == null || num.length == 0) {
        return null;
    }
    return build(num, 0, num.length - 1);
}

private TreeNode build(int[] num, int start, int end) {
    if (start > end) {
        return null;
    }
    int mid = start + (end - start) / 2;
    TreeNode head = new TreeNode(num[mid]);
    head.left = build(num, start, mid - 1);
    head.right = build(num, mid + 1, end);
    return head;
}

Convert Sorted List to Binary Search Tree - note that this solution uses 1 global variable

ListNode cur = null;

public TreeNode sortedListToBST(ListNode head) {
    // count total length of the list 
    ListNode p = head;
    int count = 0;
    while (p != null) {
        p = p.next;
        count++;
    }
    // start to traverse the tree and fill in data
    cur = head;
    return build(0, count - 1);
}

private TreeNode build(int start, int end) {
    if (start > end) {
        return null;
    }
    int mid = start + (end - start) / 2;
    TreeNode head = new TreeNode(0);
    head.left = build(start, mid - 1);
    head.val = cur.val;
    cur = cur.next;
    head.right = build(mid + 1, end);
    return head;
}

[LeetCode Plus] Lowest Common Ancestor of Binary Tree (II)

Question

link

Given a binary tree, find the lowest common ancestor of two given nodes in the tree. Each node contains a parent pointer which links to its parent.


Note:
This is Part II of Lowest Common Ancestor of a Binary Tree. If you need to find the lowest common ancestor without parent pointers, please read Lowest Common Ancestor of a Binary Tree Part I.

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

If you are not so sure about the definition of lowest common ancestor (LCA), please refer to my previous post: Lowest Common Ancestor of a Binary Search Tree (BST) or the definition of LCA here. Using the tree above as an example, the LCA of nodes 5 and 1 is 3. Please note that LCA for nodes 5 and 4 is 5.

In my last post: Lowest Common Ancestor of a Binary Tree Part I, we have devised a recursive solution which finds the LCA in O(n) time. If each node has a pointer that link to its parent, could we devise a better solution?

Hint:
No recursion is needed. There is an easy solution which uses extra space. Could you eliminate the need of extra space?

Analysis

If have parent pointer, we do not wish to use extra space for the solution.

  1. Find the height of both nodes (from the head)
  2. By calculating the height difference, move the lower nodes up (follow the parent path) to the same level as the other node.
  3. Two nodes move up together until they meet.
  4. This solution requires no extra space.

Here is a very similar question: Intersection of 2 LinkedList.

Code

// As root->parent is NULL, we don't need to pass root in.
Node *LCA(Node *p, Node *q) {
  int h1 = getHeight(p);
  int h2 = getHeight(q);
  // swap both nodes in case p is deeper than q.
  if (h1 > h2) {
    swap(h1, h2);
    swap(p, q);
  }
  // invariant: h1 <= h2.
  int dh = h2 - h1;
  for (int h = 0; h < dh; h++)
    q = q->parent;
  while (p && q) {
    if (p == q) return p;
    p = p->parent;
    q = q->parent;
  }
  return NULL;  // p and q are not in the same tree
}

int getHeight(Node *p) {
  int height = 0;
  while (p) {
    height++;
    p = p->parent;
  }
  return height;
}

[LeetCode Plus] Lowest Common Ancestor of Binary Tree (I)

Question

link

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


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

If you are not so sure about the definition of lowest common ancestor (LCA), please refer to my previous post: Lowest Common Ancestor of a Binary Search Tree (BST) or the definition of LCA here. Using the tree above as an example, the LCA of nodes 5 and 1 is 3. Please note that LCA for nodes 5 and 4 is 5.

Hint:
Top-down or bottom-up? Consider both approaches and see which one is more efficient.

This question appears on CC150v5 Q4.7.

Analysis

This tree is not BST, so it’s more difficult then previous. Top-down approach would take O(n2) time due to duplicate traverse.

However, there is a very good bottom-up approach with O(n) time. This solution, though very tricky, is the most standard and common interview question that can be asked about Binary Tree.

We traverse from the bottom, and once we reach a node which matches one of the two nodes, we pass it up to its parent. The parent would then test its left and right subtree if each contain one of the two nodes. If yes, then the parent must be the LCA and we pass its parent up to the root. If not, we pass the lower node which contains either one of the two nodes (if the left or right subtree contains either p or q), or NULL (if both the left and right subtree does not contain either p or q) up.

The coding is very concise.

Notes:
The LCA problem had been studied extensively by many computer scientists. There exists efficient algorithms for finding LCA in constant time after initial processing of the tree in linear time. For the adventurous reader, please read this article for more details: Range Minimum Query and Lowest Common Ancestor in Topcoder.

Code

updated on Sep 15th, 2014: code from CC150v5 Q4.7

public static TreeNode commonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null) {
        return null;
    } else if (root == p) {
        return p;
    } else if (root == q) {
        return q;
    }
    if (commonAncestor(root.left, p, q) == null) {
        return commonAncestor(root.right, p, q);
    } else if (commonAncestor(root.right, p, q) == null) {
        return commonAncestor(root.left, p, q);
    } else {
        return root;
    }
}

[LeetCode Plus] Lowest Common Ancestor of BST

Question

link

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


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

Using the above tree as an example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. But how about LCA of nodes 2 and 4? Should it be 6 or 2?

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)." Since a node can be a descendant of itself, the LCA of 2 and 4 should be 2, according to this definition.

Hint:
A top-down walk from the root of the tree is enough.

Analysis

This question is the easiest of this series of questions. I will quote the solution analysis.

There’s only three cases you need to consider.

1. Both nodes are to the left of the tree.
2. Both nodes are to the right of the tree.
3. One node is on the left while the other is on the right. This node must be LCA. 
4. Current node equals to one of the two nodes, this node must be the LCA. 

The run time complexity is O(h), where h is the height of the BST.

Code

The code is not written by me.

Node *LCA(Node *root, Node *p, Node *q) {
  if (!root || !p || !q) return NULL;
  if (max(p->data, q->data) < root->data)
    return LCA(root->left, p, q);
  else if (min(p->data, q->data) > root->data)
    return LCA(root->right, p, q);
  else
    return root;
}

[Question] First Character Appearing Only Once

Question

link

Problem: Implement a function to find the first character in a string which only appears once. For example: It returns ‘b’ when the input is “abaccdeff”.

Analysis

Great solution from Ryan:

You can’t know that the character is un-repeated until you’ve processed the whole string, so my suggestion would be…

Keep 2 lists.

One stores chars that appear once, the other list stores repeated chars.

Code is shown below.

def first_non_repeated_character(string):
  chars = []
  repeated = []
  for character in string:
    if character in chars:
      chars.remove(character)
      repeated.append(character)
    else:
      if not character in repeated:
        chars.append(character)
  if len(chars):
    return chars[0]
  else:
    return False

[NineChap 2.2] Sorted Array

Sorted Array

Template

There is no template.

Question list

  1. Remove Duplicates from Sorted Array

  2. Remove Duplicates from Sorted Array II

  3. Merge Sorted Array

  4. Median of Two Sorted Arrays

  5. Recover Rotated Sorted Array

Additional

  1. Reverse Words in a String

  2. Rotate String

    Given string “abcdefg” and offset = 3, the rotated string is “efgabcd”.

Code

Remove Duplicates from Sorted Array

public int removeDuplicates(int[] A) {
    if (A == null || A.length == 0) {
        return 0;
    }
    int left = 1;
    int right = 1;
    while (right < A.length) {
        if (A[right - 1] != A[right]) {
            A[left] = A[right];
            left++;
        }
        right++;
    }
    return left;
}

Remove Duplicates from Sorted Array II - slightly difficult in coding

public int removeDuplicates(int[] A) {
    if (A == null || A.length == 0) {
        return 0;
    }
    int left = 1;
    int right = 1;
    boolean twice = false;
    while (right < A.length) {
        if (A[right - 1] != A[right]) {
            A[left++] = A[right++];
            twice = false;
        } else if (!twice){
            A[left++] = A[right++];
            twice = true;
        } else {
            right++;
        }
    }
    return left;
}

Merge Sorted Array

Easy question, tail to head merge.

Median of Two Sorted Arrays

This question is Find kth largest from A&B. Refer to original post.

Recover Rotated Sorted Array

I wrote a new post.

Reverse Words in a String

public String reverseWords(String s) {
    if (s == null || s.length() == 0) {
        return s;
    }
    String[] words = s.split(" ");
    String firstReversed = "";
    for (int i = 0; i < words.length; i ++) {
        if (words[i].equals("")) continue;
        firstReversed += inPlaceReverse(words[i]) + " ";
    }
    return inPlaceReverse(firstReversed);
}

private String inPlaceReverse(String str) {
    if (str == null || str.length() == 0) return str;
    char[] chars = str.trim().toCharArray();
    int left = 0;
    int right = chars.length - 1;
    while (left < right) {
        char temp = chars[left];
        chars[left] = chars[right];
        chars[right] = temp;
        left ++;
        right --;
    }
    return String.valueOf(chars);
}

Rotate String

Same strategy.

Conclusion

  1. If array is sorted, try binary search
  2. If array is not sorted, try sort it first
  3. When you see ‘rotated array’, think of ‘list reverse’.

[LintCode] Recover Rotated Sorted Array

Question

link

Given a rotated sorted array, recover it to sorted array in-place.

Example

[4, 5, 1, 2, 3] -> [1, 2, 3, 4, 5]

Challenge

In-place, O(1) extra space and O(n) time.

Clarification

What is rotated array:

    - For example, the orginal array is [1,2,3,4], The rotated array of it can be [1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]

Analysis

O(n) time and O(a) space is required.

Find the rotate position and rotate each half. After this:

[4, 5, 1, 2, 3] -> [5, 4, 3, 2, 1]

Then reverse it again. This solution is called “三步翻转法”, an extremely common interview algorithm. Similar questions are [LeetCode 151] Reverse Words in a String.

Updated on Apr 11th, 2015:

Thanks to the nice little help from Shawn, I found out that using binary search to find the rotation point is impossible, because of duplication. I wasn’t able to point this out previously, thus apologize to all!

My code

public void recoverRotatedSortedArray(ArrayList<Integer> nums) {
    // write your code
    if (nums == null || nums.size() <= 1) {
        return;
    }
    int p = 1;
    while (p < nums.size()) {
        if (nums.get(p - 1) > nums.get(p)) {
            break;
        }
        p++;
    }
    inPlaceRotate(nums, 0, p - 1);
    inPlaceRotate(nums, p, nums.size() - 1);
    inPlaceRotate(nums, 0, nums.size() - 1);
}

private void inPlaceRotate(ArrayList<Integer> nums, int left, int right) {
    while (left < right) {
        int temp = nums.get(left);
        nums.set(left, nums.get(right));
        nums.set(right, temp);
        left++;
        right--;
    }
}