Labels

Saturday, February 28, 2015

Unique Binary Search Trees II

Given n, generate all structurally unique BST's (binary search trees) that store values 1...n.
For example,
Given n = 3, your program should return all 5 unique BST's shown below.
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3 
 
 
Naive Way: Since it is associated with its previous question. I'll bring the code in unique-binary-search-trees for reference.

 public class Solution {  
   public int numTrees(int n) {  
     // base case  
     if(n <= 1){return 1;}  
     // recursion  
     int sum = 0;  
     for(int i = 1;i <= n;i++)  
       sum += numTrees(i-1-0) * numTrees(n-i);  
     return sum;  
   }  
 }  

The code cannot be inherited since it demand us to return the structure itself instead of the number of the structure. But the idea can be inherited.

Use each number i as root node, then the left branch will be what's less than i, the right branch will be what's larger than i. For left branch and right branch, we can do the same trick. But this time we need to provide both boundary for the range.

This solution needs O(2^n) run time and space because of the scale of the output.

 /**  
  * Definition for binary tree  
  * public class TreeNode {  
  *   int val;  
  *   TreeNode left;  
  *   TreeNode right;  
  *   TreeNode(int x) { val = x; left = null; right = null; }  
  * }  
  */  
 public class Solution {  
   public List<TreeNode> generateTrees(int n) {  
     return generateTree(1,n+1);  
   }  
   private List<TreeNode> generateTree(int min, int max){  
     List<TreeNode> list = new ArrayList<TreeNode>();  
     // base case  
     if(min >= max){  
       TreeNode node = null;  
       list.add(node);  
     }  
     // general case  
     for(int i = min;i < max;i++){  
       List<TreeNode> left = generateTree(min,i);  
       List<TreeNode> right = generateTree(i+1,max);  
       for(int p = 0;p < left.size();p++){  
         for(int q = 0;q < right.size();q++){  
           TreeNode root = new TreeNode(i);  
           root.left = left.get(p);  
           root.right = right.get(q);  
           list.add(root);  
         }  
       }  
     }  
     return list;  
   }  
 }  

Unique Binary Search Trees

Given n, how many structurally unique BST's (binary search trees) that store values 1...n?
For example,
Given n = 3, there are a total of 5 unique BST's.
   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3 
 
 
 
 
Naive Way: I just came up with an idea that is similar to what I did in  Unique Binary Search Trees II.
The idea is to use each number i as root node, then the left branch will be what's less than i, the right branch will be what's larger than i. The total number of distinct structure is their product. Thus, sum up the product for all numbers.

A recursive solution popped up.

 public class Solution {  
   public int numTrees(int n) {  
     // base case  
     if(n <= 1){return 1;}  
     // recursion  
     int sum = 0;  
     for(int i = 1;i <= n;i++)  
       sum += numTrees(i-1-0) * numTrees(n-i);  
     return sum;  
   }  
 }  

Use path memorize can reduce the run time from O(2^n) to O(n^2).

 public class Solution {  
   public int numTrees(int n) {  
     Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
     map.put(0,1);  
     map.put(1,1);  
     return numTrees(n, map);  
   }  
   private int numTrees(int n, Map<Integer, Integer> map){  
     // check memory  
     if(map.containsKey(n)) return map.get(n);  
     // recursion  
     int sum = 0;  
     for(int i = 1;i <= n;i++)  
       sum += numTrees(i-1, map) * numTrees(n-i, map);  
     map.put(n, sum);  
     return sum;  
   }  
 }  


Improved Way: In the Discuss, I saw somebody using DP, which is quite similar to the above. It is written by liaison from leetcode.

 public int numTrees(int n) {  
   int [] G = new int[n+1];  
   G[0] = G[1] = 1;  
   for(int i=2; i<=n; ++i) {  
     for(int j=1; j<=i; ++j) {  
       G[i] += G[j-1] * G[i-j];  
     }  
   }  
   return G[n];  
 }  

Also, somebody raise up that this problem follows the Catalan Number. Can use Catalan formula to generate the number.

Distinct Subsequences

Given a string S and a string T, count the number of distinct subsequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).
Here is an example:
S = "rabbbit", T = "rabbit"
Return 3.

Naive Way: I came up with DP at the first glance. However, it is hard to write the iteration logic. I decide to put DP away. I turned to a DFS solution which follows a brute force idea. I am sure it works to solve the problem but didn't get passed by OJ. The run time is O(2^n).

 public class Solution {  
   private int sum;  
   public int numDistinct(String S, String T) {  
     sum = 0;  
     dfs(S,T,0,0);  
     return sum;  
   }  
   private void dfs(String S, String T, int i, int j){  
     // end case  
     if(i >= S.length() || j >= T.length()) return;  
     // recursion  
     for(int u = i;u < S.length();u++){  
       if(S.charAt(u) == T.charAt(j)){  
         if(j==T.length()-1) sum++;  
         dfs(S,T,u+1,j+1);  
       }  
     }  
   }  
 }  

Then I turn back to DP. Even though it's hard to figure out how the iteration logic works, I cannot think of other ways. So I stick to DP. I didn't work out the logic, but I work out the code for the logic using listing-> find pattern method. I listed many cases for opt[i][j] with its three highly related opt[i-1][j], opt[i-1][j-1], opt[i][j-1]. I know opt[i][j] is gonna come out from these three sub opts. After finally figure out opt[i][j] = opt[i-1][j] + opt[i-1][j-1] when S[i]==T[j], I turn back to think how the inner logic is. It is as follows:

When S[i] != T[j], we know that opt[i][j] = opt[i-1][j]! Simple but hard to come up with. Just think that since S[i]!=T[j], S[i] is useless in covering T[0....j].

When S[i] == T[j], S[i] matched T[j]! That brings us to if S[0....i-1] and T[0...j-1] are matched pair, S[0...i] and T[0...j] are matched pairs, too. And the value will be kept. Thus, when S[i] == T[j],
opt[i][j] = opt[i-1][j] + opt[i-1][j-1].

The algorithm took O(nm) run time and space.

 public class Solution {  
   public int numDistinct(String S, String T) {  
     // DP  
     int opt[][] = new int[S.length()+1][T.length()+1];  
     // base case  
     for(int i = 0;i <= S.length();i++) opt[i][0] = 1;  
     // iteration  
     for(int i = 1;i <= S.length();i++)  
       for(int j = 1;j <= T.length();j++)  
         opt[i][j] = opt[i-1][j] + (S.charAt(i-1)== T.charAt(j-1)?opt[i-1][j-1]:0);  
     return opt[S.length()][T.length()];  
   }  
 }  

Improved Way: As usual, once the original version of a 2D space DP came out, there is always a way to convert 2D matrix to 1D array once the logic doesn't affect previous rows.

 public class Solution {  
   public int numDistinct(String S, String T) {  
     // DP  
     int opt[] = new int[T.length()+1];  
     // iteration  
     for(int i = 1;i <= S.length();i++){  
       int pre = 1;  
       for(int j = 1;j <= T.length();j++){  
         int temp = opt[j];  
         opt[j] = opt[j] + (S.charAt(i-1)== T.charAt(j-1)?pre:0);  
         pre = temp;  
       }  
     }  
     return opt[T.length()];  
   }  
 }  

Friday, February 27, 2015

Find Minimum 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).
Find the minimum element.
You may assume no duplicate exists in the array.

Naive Way: I think a sorted array is highly related to a binary search approach, especially finding an element. Use the picture I draw in search-in-rotated-sorted-array-ii . There are two situations when doing a binary search approach. It the first scene, when b2 > b1 and e1 > e2, the minimum lies in the second half. It the second scene, whose condition is the opposite, minimum lies in the first half. And what we are looking for is the position where num[i-1] > num[i]. If such position doesn't found, we can return the first element.



 public class Solution {  
   public int findMin(int[] num) {  
     return binarySearch(num, 0, num.length-1);  
   }  
   private int binarySearch(int[] num, int begin, int end){  
     while(begin < end){  
       int middle = (begin+end)/2;  
       if(num[middle] > num[middle+1]) return num[middle+1];  
       if(num[middle] > num[end]) begin = middle+1;  
       else end = middle;  
     }  
     return num[begin];  
   }  
 }  

Sort List

Sort a linked list in O(n log n) time using constant space complexity.

Naive Way:时间复杂度的要求决定了只能是merge sort, quick sort 或者用 heap。空间复杂度先排除heap。Quick sort不熟,先试merge sort。merge sort能否只用O(1) space?好像是可以的。

写了N久终于写完了。用一快一慢两个指针引领要被merge的部分,merge函数需要在末尾清零(null),主函数需要用一个指针标记剩下的部分,以便前面部分merge完以后接上。

 /**  
  * Definition for singly-linked list.  
  * class ListNode {  
  *   int val;  
  *   ListNode next;  
  *   ListNode(int x) {  
  *     val = x;  
  *     next = null;  
  *   }  
  * }  
  */  
 public class Solution {  
   public ListNode sortList(ListNode head) {  
     ListNode fake = new ListNode(0);  
     ListNode cur = fake, fast = fake, slow = fake;  
     fake.next = head;  
     // get the length of list  
     int length = 0;  
     while(cur.next!=null){  
       length++;  
       cur = cur.next;  
     }  
     for(int step = 1;step < length;step*=2){  
       cur = fake;  
       while(cur.next!=null){  
         slow = cur.next;  
         fast = cur.next;  
         int i = 0;  
         // find correct merge starting position  
         while(fast.next!=null && i < step){fast = fast.next; i++;}  
         if(i!=step) break;  
         ListNode temp = fast;  
         i = 0;  
         while(temp!=null && i < step){temp = temp.next; i++;}  
         // merge two lists  
         cur.next = merge(slow, fast, step);  
         // connect with remaining nodes  
         i = 0;  
         while(i < 2*step && cur.next!=null){cur = cur.next;i++;}  
         cur.next = temp;  
       }  
     }  
     return fake.next;  
   }  
   private ListNode merge(ListNode a, ListNode b, int len){  
     ListNode fake = new ListNode(0);  
     ListNode cur = fake;  
     int i = 0,j = 0;  
     while(i < len && j < len && a!=null && b!=null){  
       if(a.val < b.val){  
         cur.next = a;  
         a = a.next;  
         i++;  
       }else{  
         cur.next = b;  
         b = b.next;  
         j++;  
       }  
       cur = cur.next;  
     }  
     while(i < len && a!=null){  
       cur.next = a;  
       a = a.next;  
       i++;  
       cur = cur.next;  
     }  
     while(j < len && b!=null){  
       cur.next = b;  
       b = b.next;  
       j++;  
       cur = cur.next;  
     }  
     cur.next = null;  
     return fake.next;  
   }  
 }  


Improved Way:看到Discuss里很多人都是 大的merge  call 小的merge,那样就会造成一个stack的空间使用,就不是O(1)的了,要实现O(1),必须从小的往大的写,这样才不会同时进行多个merge。

一些提升的地方:

有一个人用Bit 运算移位来实现step*2,这样挺好的。

有一个人将获取slow, 和fast的位置写成单独的函数,这样主函数就会清晰很多。

最重要的问题:Quick Sort 是否可以写成O(1)的关于链表的。从算法的流程上讲是可以的,先对整个list 进行左右交换,然后对前一半和后一半分别进行左右交换,这样下来应该可以不适用额外空间。

Thursday, February 26, 2015

Balanced Binary Tree

Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.

Naive Way: 一开始想到的还是一个recursive的方法,求出两边的height,然后一旦有不符合的就返回false。

算法复杂度O(n), space O(n)。

 /**  
  * Definition for binary tree  
  * public class TreeNode {  
  *   int val;  
  *   TreeNode left;  
  *   TreeNode right;  
  *   TreeNode(int x) { val = x; }  
  * }  
  */  
 public class Solution {  
   boolean balanced;  
   public boolean isBalanced(TreeNode root) {  
     balanced = true;  
     heightOf(root);  
     return balanced;  
   }  
   private int heightOf(TreeNode root){  
     if(root==null) return 0;  
     int left = heightOf(root.left), right = heightOf(root.right);  
     if(Math.abs(left-right) > 1) balanced = false;  
     return Math.max(left,right)+1;  
   }  
 }  


以上做法是一个postorder traversal 的做法,所以应该可以写成对应的iterative的形式。

算法复杂度O(n), space O(n)。

 public class Solution {  
   public boolean isBalanced(TreeNode root) {  
     if(root==null) return true;  
     Stack<TreeNode> stack = new Stack<TreeNode>();  
     Map<TreeNode, Integer> map = new HashMap<TreeNode, Integer>();  
     stack.push(root);  
     while(!stack.isEmpty()){  
       TreeNode node = stack.pop();  
       if((node.left==null || node.left!=null && map.containsKey(node.left)) &&(node.right==null || node.right!=null && map.containsKey(node.right))){  
         int left = node.left==null?0:map.get(node.left);  
         int right = node.right==null?0:map.get(node.right);  
         if(Math.abs(left-right) > 1) return false;  
         map.put(node, Math.max(left, right)+1);  
       }else{  
         if(node.left!=null && !map.containsKey(node.left)){  
           stack.push(node);  
           stack.push(node.left);  
         }else{  
           stack.push(node);  
           stack.push(node.right);  
         }  
       }  
     }  
     return true;  
   }  
 }  


Improved Way: 看到一个人用return -1来消去全局变量的做法,实在高明。
来自 pavel-shlyk

 public boolean isBalanced(TreeNode root) {  
   return root == null || balance(root, 1) > 0;  
 }  
 private int balance(TreeNode node, int level) {  
   int l = node.left != null ? balance(node.left, level+1) : level;  
   int r = node.right != null ? balance(node.right, level+1) : level;  
   if (l == -1 || r == -1 || Math.abs(r - l) > 1) return -1;  
   return Math.max(l, r);  
 }  

Wednesday, February 25, 2015

Edit Distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)
You have the following 3 operations permitted on a word:
a) Insert a character
b) Delete a character
c) Replace a character


Naive Way:这道题应该是DP教学题目,还是挺难的。
基本逻辑为
opt[i][j] // minimum steps to convert word1[0...i] to word2[0...j]
// base case
opt[0][j] = j, opt[i][0] = i; for all i,j
// iteration
opt[i][i] = min(opt[i-1][j] + 1, opt[i][j-1] + 1, opt[i-1][j-1]+(word1[i]==word[j]?0:1));
这个min里对应的三种情况分别是                 insert word1     insert word2               replace a character

大概是之前做过一遍吧,这次想这个逻辑想的特别转,一次通过了。

 public class Solution {  
   public int minDistance(String word1, String word2) {  
     int opt[][] = new int[word1.length()+1][word2.length()+1];  
     // base case  
     for(int i = 0;i <= word1.length();i++) opt[i][0] = i;  
     for(int j = 0;j <= word2.length();j++) opt[0][j] = j;  
     // iteration  
     for(int i = 1;i <= word1.length();i++)  
       for(int j = 1;j <= word2.length();j++)  
         opt[i][j] = Math.min(Math.min(opt[i-1][j]+1,opt[i][j-1]+1),opt[i-1][j-1] + (word1.charAt(i-1)==word2.charAt(j-1)?0:1));  
     return opt[word1.length()][word2.length()];  
   }  
 }  


Improved Way:提高DP的方法,我觉得一是看能不能消去一层循环(时间上),而是看能不能用少一维的空间(空间上)。这道题时间上应该就是这样了,空间貌似值得推敲。

自习琢磨一番,将2D的空间变成1D的。

 public class Solution {  
   public int minDistance(String word1, String word2) {  
     int opt[] = new int[word2.length()+1];  
     // base case  
     for(int j = 0;j <= word2.length();j++) opt[j] = j;  
     // iteration  
     for(int i = 1;i <= word1.length();i++){  
       int pre = i, corner = i-1;  
       for(int j = 1;j <= word2.length();j++){  
         int temp = corner;  
         corner = opt[j];  
         temp += (word1.charAt(i-1)==word2.charAt(j-1)?0:1);   
         opt[j] = Math.min(temp,Math.min(opt[j],pre)+1);  
         pre = opt[j];  
       }  
       opt[word2.length()] = pre;  
     }  
     return opt[word2.length()];  
   }  
 }   

Department Highest Salary

The Employee table holds all employees. Every employee has an Id, a salary, and there is also a column for the department Id.
+----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 70000  | 1            |
| 2  | Henry | 80000  | 2            |
| 3  | Sam   | 60000  | 2            |
| 4  | Max   | 90000  | 1            |
+----+-------+--------+--------------+
The Department table holds all departments of the company.
+----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------+
Write a SQL query to find employees who have the highest salary in each of the departments. For the above tables, Max has the highest salary in the IT department and Henry has the highest salary in the Sales department.
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| Sales      | Henry    | 80000  |
+------------+----------+--------+ 
 


Naive Way: 做数据库的题真是比做算法题快多了。但还不是很熟练。

 SELECT D.name as Department, E1.name as Employee, E1.Salary as Salary  
 FROM Employee E1 join Department D  
 WHERE E1.DepartmentId = D.Id   
 AND E1.Salary >= (SELECT MAX(Salary) from Employee E2  
 WHERE E1.DepartmentId = E2.DepartmentId);  


Improved Way: 

Employees Earning More Than Their Managers

The Employee table holds all employees including their managers. Every employee has an Id, and there is also a column for the manager Id.
+----+-------+--------+-----------+
| Id | Name  | Salary | ManagerId |
+----+-------+--------+-----------+
| 1  | Joe   | 70000  | 3         |
| 2  | Henry | 80000  | 4         |
| 3  | Sam   | 60000  | NULL      |
| 4  | Max   | 90000  | NULL      |
+----+-------+--------+-----------+
Given the Employee table, write a SQL query that finds out employees who earn more than their managers. For the above table, Joe is the only employee who earns more than his manager.
+----------+
| Employee |
+----------+
| Joe      |
+----------+ 
 
 
 
Naive Way: 这回一次就过了。
 SELECT E1.Name AS Employee  
 FROM Employee E1 join Employee E2  
 WHERE E1.ManagerId = E2.Id AND E1.Salary > E2.Salary;  


Improved Way:还有用left join 的,来自kkamkou

 SELECT e1.`Name`  
  FROM `Employee` e1  
   LEFT JOIN `Employee` e2 ON(e2.`Id` = e1.`ManagerId`)  
   WHERE e1.`ManagerId` IS NOT NULL AND e1.`Salary` > e2.`Salary`  

Duplicate Emails

Write a SQL query to find all duplicate emails in a table named Person.
+----+---------+
| Id | Email   |
+----+---------+
| 1  | a@b.com |
| 2  | c@d.com |
| 3  | a@b.com |
+----+---------+
For example, your query should return the following for the above table:
+---------+
| Email   |
+---------+
| a@b.com |
+---------+
Note: All emails are in lowercase.

 Naive Way: 我先建一个Table(Email, count(*)), 然后在这个Table里找count(*) > 1的。


 SELECT Email  
 FROM   
 (SELECT Email, count(*) AS num  
 FROM Person  
 FROUP BY Email) E  
 WHERE num > 1;  

Improved Way: 不需要使用多余的subquery。

 SELECT Email from Person  
 GROUP BY Email  
 HAVING count(Email)>1;  

Customers Who Never Order

Suppose that a website contains two tables, the Customers table and the Orders table. Write a SQL query to find all customers who never order anything.
Table: Customers.
+----+-------+
| Id | Name  |
+----+-------+
| 1  | Joe   |
| 2  | Henry |
| 3  | Sam   |
| 4  | Max   |
+----+-------+
Table: Orders.
+----+------------+
| Id | CustomerId |
+----+------------+
| 1  | 3          |
| 2  | 1          |
+----+------------+
Using the above tables as example, return the following:
+-----------+
| Customers |
+-----------+
| Henry     |
| Max       |
+-----------+ 
 
值得纪念的一次,第一次做出一道数据库的题,我没有任何数据库基础可言,全是自学youtube Standford 那个教程的(https://www.youtube.com/watch?v=D-k-h0GuFmE&list=PL6hGtHedy2Z4EkgY76QOcueU8lAC4o6c3),觉得这道题好简单啊。


 SELECT Name AS Customers  
 FROM Customers  
 WHERE Id NOT IN  
 (SELECT CustomerId AS Id FROM Orders);  

发图一张,特此纪念。

 
  

Recover Binary Search Tree

Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?

Naive Way:O(1)的做法我实在是想不出来,明明inorder traversal 就要O(n) space了。

这是O(n) space 的做法,这样做比较无脑,任何错误都可以纠正。

 /**  
  * Definition for binary tree  
  * public class TreeNode {  
  *   int val;  
  *   TreeNode left;  
  *   TreeNode right;  
  *   TreeNode(int x) { val = x; }  
  * }  
  */  
 public class Solution {  
   public void recoverTree(TreeNode root) {  
     List<Integer> vals = new ArrayList<Integer>();  
     List<TreeNode> nodes = new ArrayList<TreeNode>();  
     inorder(root, nodes, vals);  
     Collections.sort(vals);  
     for(int i = 0;i < nodes.size();i++)  
       nodes.get(i).val = vals.get(i);  
   }  
   private void inorder(TreeNode root, List<TreeNode> nodes, List<Integer> vals){  
     if(root==null) return;  
     inorder(root.left, nodes, vals);  
     nodes.add(root);  
     vals.add(root.val);  
     inorder(root.right, nodes, vals);  
   }  
 }  


Improved Way: 原来还有一个叫Morris Threading Traversal 的东西。这里有一个blog,是一个中国人专门写Morris Traversal的,http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html,非常有用。线索二叉树。

针对这道题,Morris Traversal 解决了用O(1)的space遍历BST,但是找到错误交换的两个点依然是一个问题。肯定是在Morris Traversal的算法上修改。先把Morris Traversal的算法列出来。

 /**  
  * Definition for binary tree  
  * public class TreeNode {  
  *   int val;  
  *   TreeNode left;  
  *   TreeNode right;  
  *   TreeNode(int x) { val = x; }  
  * }  
  */  
 public void morrisTraversal(TreeNode root){  
     TreeNode temp = null;  
     while(root!=null){  
       if(root.left!=null){  
         // connect threading for root  
         temp = root.left;  
         while(temp.right!=null && temp.right != root)  
           temp = temp.right;  
         // the threading already exists  
         if(temp.right!=null){  
           temp.right = null;  
           System.out.println(root.val);  
           root = root.right;  
         }else{  
           // construct the threading  
           temp.right = root;  
           root = root.left;  
         }  
       }else{  
         System.out.println(root.val);  
         root = root.right;  
       }  
     }  
   }   


两个加粗部分对应遍历时的输出顺序,所以找出错误交换的节点也应该是在这里修改。如何修改?因为MorrisTraversal得到的应该是升序的,所以前一个大于后一个说明前一个一定是错的,为什么不是后一个是错的呢?因为后一个比前一个小说明后一个应该被先遍历到,前一个节点至少应该是后一个节点那么大小。而一旦找到了第一个错误的节点,第二次再遇到前一个大于后一个的时候,错误的就是后一个了。为什么呢?因为我们知道了第一个错误的节点是value大于它本来的value,所以我们现在要找的就是value小于它本来value的节点,因为这个节点本来应该是第一个错误的节点所在的位置。
而当出错的是连续两个节点时,可能只检测到一个非升序情况,因为连着的这两个是错的。这时要暂时另第二个错误节点等于第一个后面的节点。

所以要做的就是把  
System.out.println(root.val);
替换成
if(pre!=null && pre.val > root.val){
      if(first==null){first = pre;second = root;}
      else{second = root;} 

}
pre = root;  

这里非常好理解,因为原来是按照System.out.println(root.val)的顺序输出的,也就是说该位置得到的每一次root值都是排好序的,那么将上一次的root作为这一次的pre就是按照顺序的pre和root,如果二者大小关系不对,可知错误节点在此处。



 public void recoverTree(TreeNode root) {  
     TreeNode pre = null;  
     TreeNode first = null, second = null;  
     // Morris Traversal  
     TreeNode temp = null;  
     while(root!=null){  
       if(root.left!=null){  
         // connect threading for root  
         temp = root.left;  
         while(temp.right!=null && temp.right != root)  
           temp = temp.right;  
         // the threading already exists  
         if(temp.right!=null){  
           if(pre!=null && pre.val > root.val){  
             if(first==null){first = pre;second = root;}  
             else{second = root;}  
           }  
           pre = root;  
           temp.right = null;  
           root = root.right;  
         }else{  
           // construct the threading  
           temp.right = root;  
           root = root.left;  
         }  
       }else{  
         if(pre!=null && pre.val > root.val){  
           if(first==null){first = pre;second = root;}  
           else{second = root;}  
         }  
         pre = root;  
         root = root.right;  
       }  
     }  
     // swap two node values;  
     if(first!= null && second != null){  
       int t = first.val;  
       first.val = second.val;  
       second.val = t;  
     }  
   }  

Divide Two Integers

Divide two integers without using multiplication, division and mod operator.
If it is overflow, return MAX_INT.

Naive Way:通常不让用这些数学运算符都是在指向比特运算符。除以2,4,8就好办了,可以通过移位实现,除以3怎么办呢。可不可以从结果开始想,除以3相当于先把结果左移1位(2),再加上他自己(1)。那么除以7就相当于找到一个数左移2位,左移1位,左移0位之和为除数。如果不能整除,就是除数在(左移2位,左移1位,左移0位之和)和(左移2位,左移1位,左移1位之和)之间。好像可以用一个recursive的方法,每次都找都最高位的商,剩下的交给下一级recursive call。

还有负数,真是烦。因为Integer.MIN_VALUE没有对应的正数,写的时候特别麻烦。不能先全转成正数,于是我就打算全用负数做。

最后的最后,终于是全通过了,并且迫于无奈只能将dividend = Integer.MIN_VALUE, divisor = -1这个case单独列出了,因为它使唯一一个会超出表示范围的数。

复杂度上并没有使用binary search 找当前位,尝试过,很难,尤其是负数。 边界由
divisor << cur < 0 && cur < 31 控制,第一个是要保持负数形式,唯一一个例外就是Integer.MIN_VALUE除以1的时候,因为没有上限,-1移位成Integer.MIN_VALUE时下一个就只会移0位,因为java不让左移33位,会变回左移1位,所以有了第二个条件 cur <31。

 public class Solution {  
   public int divide(int dividend, int divisor) {  
     if(dividend==Integer.MIN_VALUE && divisor==-1) return Integer.MAX_VALUE;  
     if(dividend > 0 && divisor > 0) return divideHelper(-dividend, -divisor);  
     else if(dividend > 0) return -divideHelper(-dividend,divisor);  
     else if(divisor > 0) return -divideHelper(dividend,-divisor);  
     else return divideHelper(dividend, divisor);  
   }  
   private int divideHelper(int dividend, int divisor){  
     // base case  
     if(divisor < dividend) return 0;  
     // get highest digit of divisor  
     int cur = 0, res = 0;  
     while((divisor << cur) >= dividend && divisor << cur < 0 && cur < 31) cur++;  
     res = dividend - (divisor << cur-1);  
     if(res > divisor) return 1 << cur-1;  
     return (1 << cur-1)+divide(res, divisor);  
   }  
 }   


Improved Way:看到Discuss里大部分人都是用long做的,有人就问,如果让你divide的是两个long呢。long的做法完全可以用正数做了。我觉得应该能使用Binary search提高效率。记住要补上!

Tuesday, February 24, 2015

Rotate Array

Rotate an array of n elements to the right by k steps.
For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.

Naive Way: 要想三种方法。
1.最简单的一种就是用一个新数组去装载新的数组然后再复制回去。这种方法需要O(n) space,算法复杂度是O(n)。实际做的时候还发现k会出现比n大的情况。

 public class Solution {  
   public void rotate(int[] nums, int k) {  
     k %= nums.length;  
     int temp[] = new int[nums.length];  
     for(int i = 0;i < nums.length;i++)  
       temp[i] = i < k? nums[nums.length-k+i]:nums[i-k];  
     for(int i = 0;i < nums.length;i++)  
       nums[i] = temp[i];  
   }  
 }  


在原来的方法上想,优化一下可以使空间可以降到O(min(k,n-k))

 public class Solution {  
   public void rotate(int[] nums, int k) {  
     k %= nums.length;  
     if(nums.length-k > k){  
       int temp[] = new int[k];  
       for(int i = 0;i < k;i++)  
         temp[i] = nums[nums.length-k+i];  
       for(int i = nums.length-1;i >= k;i--)  
         nums[i] = nums[i-k];  
       for(int i = 0;i < k;i++)  
         nums[i] = temp[i];  
     }else{  
       int temp[] = new int[nums.length-k];  
       for(int i = 0;i < temp.length;i++)  
         temp[i] = nums[i];  
       for(int i = 0;i < k;i++)  
         nums[i] = nums[i+nums.length-k];  
       for(int i = 0;i < nums.length-k;i++)  
         nums[i+k] = temp[i];  
     }  
   }  
 }  


2. 可以模仿rotate linked list 的插值法,不过在数组上插值,每一次就要移动 n 位了,这样就得到一个算法复杂度为O(nk), space O(1) 的方法。但是这样会超时。

 public class Solution {  
   public void rotate(int[] nums, int k) {  
     k %= nums.length;  
     for(int i = 0;i < k;i++){  
       int temp = nums[nums.length-k+i];  
       for(int j = nums.length-k+i;j >= i+1;j--)  
         nums[j] = nums[j-1];  
       nums[i] = temp;  
     }  
   }  
 }  



3.可不可以仅通过swap来实现O(1)。 好像可以通过recursive的greedy算法实现。
于是写了这个O(1) space的算法,弄了好久。核心思想是在不产生overlap的情况下,尽可能多的交换, 然后剩下的没有进行交换的则建立新的分割,由下一个recursive的函数进行交换。

算法复杂度是O(n) (每个数最多被交换两次)

 public class Solution {  
   public void rotate(int[] nums, int k) {  
     k %= nums.length;  
     rotate(nums,0,nums.length-k-1);  
   }  
   /*  
   * begin is where the sequence to be processed starts  
   * end is the cut where separates the two slices of the array  
   * nums = [<processed>,<       to be processed         >]  
   *                  |                        |   
   *                  <begin, end>, <end+1, nums.length-1>  
   * Afterwards:        <processed >, <  to be processed    >   
   */  
   private void rotate(int[] nums, int begin, int end){  
     // base case  
     if(end == nums.length-1) return;  
     // recursive  
     int k = Math.min(end-begin+1, nums.length-1-end);  
     for(int i = 0; i < k;i++)  
       swap(nums, begin+i, end+1+i);  
     if(k==end-begin+1)  
       rotate(nums, begin+k, end+k);  
     else  
       rotate(nums, begin+k, nums.length-k-1);  
   }  
   private void swap(int[] nums, int index1, int index2){  
     int temp = nums[index1];  
     nums[index1] = nums[index2];  
     nums[index2] = temp;  
   }  
 }  


Improved Way:在Discuss里看到了两个很好的方法。

第一个是
mjsaber的,不得不说他这个做法实在太聪明了。先把整个数组头尾互换,此时要交换的两部分已经处于各自半区了,但是顺序是反的,然后在内部翻转。这个方法是主流的方法。
 public class Solution {  
   public void rotate(int[] nums, int k) {  
     if (nums == null || nums.length == 0) {  
       return;  
     }  
     k = k % nums.length;  
     reverse(nums, 0, nums.length-k-1);  
     reverse(nums, nums.length-k, nums.length-1);  
     reverse(nums, 0, nums.length-1);  
   }  
   private void reverse(int[] num, int left, int right) {  
     while (left < right) {  
       int t = num[left];  
       num[left] = num[right];  
       num[right] = t;  
       left++;  
       right--;  
     }  
   }  
 }  




第二个是 xcv58的,采用从后往前换,每次换好一个找当前这个要换的位置。

 public void rotate(int nums[], int n, int k) {  
   for (int i = 0, j = k % n, previous = nums[i], anchor = i, count = n; count > 0; count--, j = (i + k) % n) {  
     int tmp = nums[j];  
     nums[j] = previous;  
     previous = tmp;  
     if ((i = j) == anchor) {  
       i = (i + 1) % n;  
       previous = nums[i];  
       anchor = i;  
     }  
   }  
 }  

Saturday, February 21, 2015

N-Queens II

Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.













Naive Way:在N-queens 中,要求是返回所有解法,那么就知道解法的个数了,但是作为一道新题,是否说明可以用低于返回所有解法的复杂度求得解的个数。

最后还是用了DFS原来的方法,做了一些简化。

 public class Solution {  
   class Node{  
     int x,y;  
     Node(int xx, int yy){  
       x = xx;  
       y = yy;  
     }  
   }  
   public int totalNQueens(int n) {  
     // DFS  
     int board[][] = new int[n][n];  
     int count = 0;  
     Stack<Node> stack = new Stack<Node>();  
     for(int j = 0;j < n;j++)  
       stack.push(new Node(0,j));  
     while(!stack.isEmpty()){  
       Node node = stack.pop();  
       boolean hasNext = false;  
       setBoard(board,node,false);  
       for(int j = 0;j < n;j++)  
         if(node.x+1 < n && board[node.x+1][j] == 0) stack.push(new Node(node.x+1,j));  
       for(int j = 0;j < n;j++)  
         if(node.x+1 < n &&board[node.x+1][j] == 0) hasNext = true;  
       count += node.x==n-1?1:0;  
       if(!hasNext){  
         // back trace  
         if(stack.isEmpty()) break;  
         Node peek = stack.peek();  
         for(int i = node.x;i >= peek.x;i--)  
           for(int j = n-1;j >= 0;j--)  
             if(((i==node.x && j <= node.y) || (i==peek.x && j > peek.y) || (i< node.x && i > peek.x)) && board[i][j]==-1)  
               setBoard(board, new Node(i,j), true);  
       }  
     }  
     return count;  
   }  
   private void setBoard(int[][] b, Node node, boolean clear){  
     int add = clear?-1:1;  
     for(int i = 0;i < b.length;i++)  
       b[i][node.y] +=b[i][node.y]==-1?0:add;  
     for(int j = 0;j < b[0].length;j++)  
       b[node.x][j] +=b[node.x][j]==-1?0:add;  
     for(int i = 0;i < b.length;i++){  
       if(node.x+i < b.length && node.y+i < b[0].length)  
         b[node.x+i][node.y+i] += b[node.x+i][node.y+i]==-1?0:add;  
       if(node.x+i < b.length && node.y-i >= 0)  
         b[node.x+i][node.y-i] += b[node.x+i][node.y-i]==-1?0:add;  
       if(node.x-i >= 0 && node.y+i < b[0].length)  
         b[node.x-i][node.y+i] += b[node.x-i][node.y+i]==-1?0:add;  
       if(node.x-i >= 0 && node.y-i >= 0)  
         b[node.x-i][node.y-i] += b[node.x-i][node.y-i]==-1?0:add;  
     }  
     b[node.x][node.y] = clear?0:-1;  
   }  
 }  



Improved Way:在Discuss看到很多人都不需要用二维矩阵。方法都特别厉害。

https://oj.leetcode.com/discuss/18411/accepted-java-solution
AlexTheGreat 的做法是将所有格子分为col, row, diag1, diag2。col和row 是指列和行。
diag1是指从左上至右下的对角线, diag2是指从左下至右上的对角线。这样就将一个矩阵拆成4个1D向量。
最厉害的还是代码的回溯方式,遍历一个row之后可立即消去之前遍历的痕迹。

 /**  
  * don't need to actually place the queen,  
  * instead, for each row, try to place without violation on  
  * col/ diagonal1/ diagnol2.  
  * trick: to detect whether 2 positions sit on the same diagnol:  
  * if delta(col, row) equals, same diagnol1;  
  * if sum(col, row) equals, same diagnal2.  
  */  
 private final Set<Integer> occupiedCols = new HashSet<Integer>();  
 private final Set<Integer> occupiedDiag1s = new HashSet<Integer>();  
 private final Set<Integer> occupiedDiag2s = new HashSet<Integer>();  
 public int totalNQueens(int n) {  
   return totalNQueensHelper(0, 0, n);  
 }  
 private int totalNQueensHelper(int row, int count, int n) {  
   for (int col = 0; col < n; col++) {  
     if (occupiedCols.contains(col))  
       continue;  
     int diag1 = row - col;  
     if (occupiedDiag1s.contains(diag1))  
       continue;  
     int diag2 = row + col;  
     if (occupiedDiag2s.contains(diag2))  
       continue;  
     // we can now place a queen here  
     if (row == n-1)  
       count++;  
     else {  
       occupiedCols.add(col);  
       occupiedDiag1s.add(diag1);  
       occupiedDiag2s.add(diag2);  
       count = totalNQueensHelper(row+1, count, n);  
       // recover  
       occupiedCols.remove(col);  
       occupiedDiag1s.remove(diag1);  
       occupiedDiag2s.remove(diag2);  
     }  
   }  
   return count;  
 }  



然后还有一些用bit manipulate的做法,我就看懂了一个,觉得这种做法最厉害之处在于不用back trace。来自leetcode用户weird

首先还是定下,col, row, diag1, diag2的4个1D向量, 而row向量会作为recursive的参数成为循环的increment。关键的代码
(col & (cm|dm1|dm2) )==0 
是来判断 当前已知的占据信息 (cm|dm1|dm2) 中,board[col][row]这一点是否被占据了,如果不被占据,即该bit 为0,就可以升一行,并将该点被占据的信息记入cm 中,然后更新 dm1 和dm2。至于dm1 和 dm2 是如何更新的。首先要认清这个回溯算法是不需要back trace的,每次传进 bts ()中的内容都是之前一行的占据情况,同一行不同列使用的信息都是一样的,就是之前一行的占据情况。所以dm1的更新只需要记录对于下一个点会有影响的对角线上的点是否被占据,写一个点必是下一行的某个点,而那时的col会比现在的高一个bit,所以dm1要左移一个bit。简单的说就是cm, dm1,dm2记录的都是相对位置,不是绝对位置。



 public class Solution {  
   int count=0;  
   public int totalNQueens(int n) {  
     bts(0,n,0,0,0);  
     return count;  
   }  
   //passing column mask, left diagonal mask, right diagonal mask  
   void bts(int row, int n, int cm, int dm1, int dm2){  
     if (row==n){  
       count++;  
       return;  
     }  
     for (int col=(1<<(n-1));col>0;col>>=1)  
       if ((col & (cm|dm1|dm2) )==0 )  
         bts(row+1,n, cm|col, (col|dm1)<<1, (col|dm2)>>1);  
   }  
 }   

Friday, February 20, 2015

ZigZag Conversion

The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P   A   H   N
A P L S I I G
Y   I   R
And then read line by line: "PAHNAPLSIIGYIR" Write the code that will take a string and make this conversion given a number of rows:
string convert(string text, int nRows);
convert("PAYPALISHIRING", 3) should return "PAHNAPLSIIGYIR".


Naive Way:我第一次做的时候以为3是个常数呢。这种zigzag scan 有规律可循啊,只要找到index的映射规律,就可以一次scan 实现。首先例子是一个nRows = 3的,写一个nRows = 4的。

P         I         N
A    L S      I  G
Y A    H R
P         I

首先看第一行,前一个和后一个的间隔是 2*nRows - 1(折点)-1(起点) -1(终点),如果是步长就还要+1算上自己,那么步长就是 2*nRows-2,这个规律是普适的。
然后看第二行,前一个和中间那个的步长是 2*(nRows-1)-2,这个很好理解就是 减少了两个点。 注意到,竖直方向上前一个和竖直方向上后一个的步长还是 2*nRows-2,相当于之前分析部分起点+1, 终点+1。所以可以把中间部分的单独列出来考虑。
第三行就显而易见,竖直方向上第一个和中间的步长是2*(nRows-2)-2,因为又减少了两个。
第四行的情况和第一行一样。

总结一下就是,基本步长为2*nRows-2。如果不是第一行和最后一行,就要增加中间点,它和前一个竖直方向上的步长是 2*(nRows-index)-2,index是行的序号。还有就是nRows=1时,步长为0,不能统一,单独步长。

算法复杂度O(n), space O(n)。 一次扫描。

 public class Solution {  
   public String convert(String s, int nRows) {  
     StringBuilder str = new StringBuilder();  
     for(int i = 0;i < nRows;i++){  
       int j = i;  
       while(j < s.length()){  
         str.append(s.charAt(j));  
         if(i!=0 && i!=nRows-1 && j+2*(nRows-i)-2 < s.length())  
           str.append(s.charAt(j+2*(nRows-i)-2));  
         j += nRows==1?1:(2*nRows-2);  
       }  
     }  
     return str.toString();  
   }  
 }  

Reverse Nodes in k-Group


Reverse Nodes in k-Group



 


Given a linked list, reverse the nodes of a linked list k at a time and return its modified 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 

 



Naive Way: 如何reverse Linked List。是护住最后一个节点,不断的往后insert节点,那么reverse k 个也是一样的。护住的可以是起点也可以是终点,但这道题要求最后不够k 个保持原样,护住起点不能够有效将最后多出部分写入大循环,护住终点的方法则可以先判断是否找到终点来对付这种情况。



 



算法复杂度O(n), space O(1)。第一次将节点插入终点后面的时候,我记下了新的起点的位置,这样就不用再完成k个再遍历至那里。


  /**  
  * Definition for singly-linked list.  
  * public class ListNode {  
  *   int val;  
  *   ListNode next;  
  *   ListNode(int x) {  
  *     val = x;  
  *     next = null;  
  *   }  
  * }  
  */  
 public class Solution {  
   public ListNode reverseKGroup(ListNode head, int k) {  
     ListNode fakeHead = new ListNode(0);  
     ListNode cur = fakeHead, tail = cur;  
     fakeHead.next = head;  
     int i = 0;  
     while(tail!=null){  
       for(i = 0;i < k && tail.next!=null;i++)  
         tail = tail.next;  
       if(i<k) return fakeHead.next;  
       ListNode nextCur = null;  
       while(cur.next!=tail){  
         ListNode temp = cur.next;  
         if(nextCur==null) nextCur = temp;  
         cur.next = temp.next;  
         temp.next = tail.next;  
         tail.next = temp;  
       }  
       cur = nextCur;  
       tail = cur;  
     }  
     return fakeHead.next;  
   }  
 }  

 




 



 



Improved Way: 在Discuss里还有一种recursive的方法,就是将翻转k个写进一个子函数,每次调用后把它接起来,觉得O(1) space 还是不适合recursive。

Longest Consecutive Sequence


Longest Consecutive Sequence



 


Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
For example,
Given [100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
Your algorithm should run in O(n) complexity.

Naive Way:O(n)应有两种方案,一种是用hashmap,一种是用bucket sort的思想。想了一想bucket sort,可以将数字全部分割成小组,然后再每个小组内再分割,直到某个小组容量正好等于数的数量就是连续的,但是还要追溯相邻的小组,反正不太像O(n)的。如果用hashmap,好像好做一点,可以map相邻的两个数,记下起点,最后不停追溯map,但是每次记录起点又需要O(n)的追溯了。还可以map连续出现的个数,比如一开始100->1, 4->1, 200->1,然后3出现,就要使得3->2, 4->2,这样有个麻烦就是如果已匹配好的连续数段很长,还要每一个更新一遍映射。后来仔细想了想,不用所有的都更新,只需要更新一头一尾。

算法复杂度O(n), space O(n)。



 

 public class Solution {  
   public int longestConsecutive(int[] num) {  
     int longest = 0;  
     Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
     for(int i = 0;i < num.length;i++){  
       // if there is no duplicates, these two lines can be commented  
       if(map.containsKey(num[i])) continue;  
       map.put(num[i],1);  
       int end = num[i];  
       int begin = num[i];  
       if(map.containsKey(num[i]+1))  
         end = num[i] + map.get(num[i]+1);  
       if(map.containsKey(num[i]-1))  
         begin = num[i] - map.get(num[i]-1);  
       longest = Math.max(longest, end-begin+1);  
       map.put(end, end-begin+1);  
       map.put(begin, end-begin+1);  
     }  
     return longest;  
   }  
 }  

Thursday, February 19, 2015

Two Sum


Two Sum



 


Given an array of integers, 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 


 

Naive Way:如果序列是排好序的,可不可以Binary Search。联想3sum的做法,好像可以。但是如果不是排好序的就不行,因为要返回Index而非boolean值,排序会打乱index。可不可以在O(n)的时间内做出来。可以,用一个HashTable去存已经遍历过的。




这是O(n) run time, O(n) space的做法。



 

 public class Solution {  
   public int[] twoSum(int[] numbers, int target) {  
     int rslt[] = {-1,-1};  
     Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
     for(int i = 0;i < numbers.length;i++){  
       int t = target - numbers[i];  
       if(map.containsKey(t)){  
         rslt[0] = map.get(t);  
         rslt[1] = i+1;  
         return rslt;  
       }else{  
         map.put(numbers[i],i+1);  
       }  
     }  
     return rslt;  
   }  
 }  

Reverse Words in a String II

Given an input string, reverse the string word by word. A word is defined as a sequence of non-space characters.
The input string does not contain leading or trailing spaces and the words are always separated by a single space.
For example,
Given s = "the sky is blue",
return "blue is sky the".
Could you do it in-place without allocating extra space?


Naive Way:  和Reverse Words in String不同的是,这次的String是标准格式。但这次严格要求O(1)的space。因为没钱买,所以看了一下问答部分,这次它将String 改成了 char[],返回值是void。这样才能实现O(1) space,因为String是immutable的。

我想到一个简单的办法,用一头一尾交换先将整个char[] 翻转,然后对每一个单词再作内部翻转,这样就可以实现O(1) space,但是需要扫两遍。



 public class Solution{  
   public static void main(String args[]){  
     char[] s = {'t','h','e',' ','s','k','y',' ','i','s',' ','b','l','u','e'};  
     Solution ss = new Solution();  
     ss.reverseWords(s);  
     for(int i = 0;i < s.length;i++)  
       System.out.print(s[i] + " ");  
     System.out.println();  
   }  
   public void reverseWords(char[] s){  
     reverseWords(s,0,s.length-1);  
     for(int i = 0, j = 0;i <= s.length;i++){  
       if(i==s.length || s[i] == ' '){  
         reverseWords(s,j,i-1);  
         j = i+1;  
       }  
     }  
   }  
   private void reverseWords(char[] s, int begin, int end){  
     while(begin < end){  
       char c = s[begin];  
       s[begin] = s[end];  
       s[end] = c;  
       begin++;  
       end--;  
     }  
   }  
 }  

Reverse Words in a String


Reverse Words in a String



 


Given an input string, reverse the string word by word.
For example,
Given s = "the sky is blue",
return "blue is sky the".

Naive Way: 如果能使用String.split()就会很方便。

如答案所说,使用String.split() 是需要两次扫描的。如果从后往前扫String,就只需要一次扫面。

 public class Solution {  
   public String reverseWords(String s) {  
     StringBuilder rslt = new StringBuilder();  
     String[] str = s.split(" ");  
     for(int i = str.length-1;i >= 0;i--){  
       if(i!=str.length-1 && str[i].length()!=0) rslt.append(" ");  
       rslt.append(str[i]);  
     }  
     return rslt.toString();  
   }  
 }   


这次从后往前扫,只需要一次。

 public class Solution {  
   public String reverseWords(String s) {  
     StringBuilder rslt = new StringBuilder();  
     int i = s.length(), j = s.length();  
     while(--i >= -1){  
       if(i==-1 || s.charAt(i) == ' '){  
         if(i+1!=j && rslt.length()!=0) rslt.append(" ");  
         rslt.append(s.substring(i+1,j));  
         j = i;  
       }  
     }  
     return rslt.toString();  
   }  
 }  

Wednesday, February 18, 2015

Single Number II


Single Number II



 


Given an array of integers, every element appears three times except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Naive Way:根据Single Number 中的做法,有没有一种运算x 能使得
1 x 1 x 1 = 0;
0 x 0 x 0 = 0;
1 x 0 = 1;
0 x 0 = 0;

要是有也不是一种好办法,因为如果k=4,5,6...就还要想新的运算。所以必须有一种运算能够满足k个的情况。

以上推断给了一个实例,如果k个数,在同一个 bit 位上就可能会有 k 个 1,如果用一个 int 表示每一位上 1 的个数,有k个数的就会有k个1,最后除以k就会被除尽,而多出的那个1就是多出的那个 single one 的对应 bit 位上的 1。

算法复杂度 O(n), space O(1)

 public class Solution {  
   public int singleNumber(int[] A) {  
     int k = 3;  
     int x = 0;  
     int num[] = new int[32];  
     for(int i = 0;i < A.length;i++)  
       for(int j = 0;j < num.length;j++)  
         num[j] += ((A[i] & (1 << j)) != 0)?1:0;  
     for(int j = 0;j < num.length;j++) x |= ((num[j]%k) << j);  
     return x;  
   }  
 }  



Improved Way:此题在Discuss里的方法还真不少。
  public class Solution {  
   public int singleNumber(int[] A) {  
     int ones = 0, twos = 0;  
     for(int i = 0; i < A.length; i++){  
       ones = (ones ^ A[i]) & ~twos;  
       twos = (twos ^ A[i]) & ~ones;  
     }  
     return ones;  
   }  
 }  

这里有一个方法找到了一种 x 运算满足之前说的条件。

来自leetcode 用户  againest1 。理解这个算法首先要认识到数字出现的顺序对于比特运算是无关的,根据比特运算的交换律。那么我们可以把这个序列看成是连续三个一样的不停出现,直到那一个落单的。
第一个 x 出现,ones = x & ~0 = x, twos = x & ~x = 0;
第二个 x 出现,ones = x ^ x & ~0 = 0 & ~0 = 0; twos = 0 ^ x & ~0 = x;
第三个 x 出现,ones = 0 ^ x & ~x = x & ~x = 0; twos = x ^ x & ~0 = 0;
只出现一次会被ones记录,只出现两次会被twos记录,出现3次两者都不记录。


 这里还有一个解释超长的,做法是一样的 https://oj.leetcode.com/discuss/9763/accepted-proper-explaination-does-anyone-have-better-idea

 

Single Number


Single Number



 


Given an array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Naive Way:好有纪念意义的一道题啊,这是我做leetcode的第一道题,对我有启蒙之恩。当时竟不知道Map为何物,更惶论HashTable了。

即使不知道这些也是可以做的,这是我第一次的做法。

 public int singleNumber(int[] A) {  
     int map[] = new int[(A.length+1)/2];  
     int len = map.length;  
     int temp;  
     // 0 case && 1 case  
     int count0 = 0;  
     int count1 = 0;  
     for(int i = 0;i < A.length;i++){  
       if(A[i] == 0){  
         count0++;  
       }  
       if(A[i] == 1){  
         count1++;  
       }  
     }  
     if(count0 == 1){  
       return 0;  
     }  
     if(count1 == 1){  
       return 1;  
     }  
     // general case  
     for(int i = 0;i < A.length;i++){  
       temp = Math.abs(A[i]%len);  
       while(map[temp] != 0 && map[temp] != A[i]){  
         temp++;  
         temp = temp%len;  
       }  
       if(map[temp] == 0){  
         map[temp] = A[i];  
       }else if(map[temp] == A[i]){  
         map[temp] = 1;  
       }  
     }  
     for(int i = 0;i < len;i++){  
       if(map[i] != 0 && map[i] != 1){  
         return map[i];  
       }  
     }  
     return 0;  
   }  


Improved Way: 现在我知道 a^a = 0这回事了。


 

  public class Solution {  
   public int singleNumber(int[] A) {  
     int x = 0;  
     for(int i = 0;i < A.length;i++) x ^= A[i];  
     return x;  
   }  
 }  

Substring with Concatenation of All Words

You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) in S that is a concatenation of each word in L exactly once and without any intervening characters.
For example, given:
S: "barfoothefoobarman"
L: ["foo", "bar"]
You should return the indices: [0,9].
(order does not matter).


Naive Way: brute force 的方法将是每一个字符的遍历,看它所引领的字符串是否为字典所组成。假设|S| = n, |L| = m, |L[0]| = k,这样的算法复杂度是O(nm)。

而我的基本的想法是隔着一个单词长度的遍历,用一个queue控制进出,然后用一个map装入L的信息,一旦queue.size() == L.length 说明找到一个match。

 如果出现这种情况 L:["foo","bar","arf"...],说明一个substring即使在字典中找到,也不可以一个单词一个单词的向后递增。

假设每个单词长度是k,任然可以用上述方法,需要为每一个间隔都设置一个queue和一个map。然后每个间隔单独执行以上算法,这样就需要k个queue和k各map。算法复杂度是O(n)。
我觉得这个算法复杂度真心足够了。

有一个容易出错的地方是当map不包含当前word时,可能queue出来的那个正好等于当前word,此时,要做的就是不改变map,把当前word推进queue,然后queue再排出最前面的word。

算法复杂度是O(n), space O(km)

 public class Solution {  
   public List<Integer> findSubstring(String S, String[] L) {  
     List<Integer> rslt = new ArrayList<Integer>();  
     // edge case  
     if(L.length==0) return rslt;  
     // initialize k queues and k maps  
     int k = L[0].length();  
     List<Queue<String>> queues = new ArrayList<Queue<String>>();  
     List<Map<String, Integer>> maps = new ArrayList<Map<String, Integer>>();  
     Map<String, Integer> map_temp = new HashMap<String, Integer>();  
     for(int i = 0;i < L.length;i++) setMap(map_temp, L[i]);  
     for(int i = 0;i < k;i++) queues.add(new LinkedList<String>());  
     for(int i = 0;i < k;i++) maps.add(new HashMap<String, Integer>(map_temp));  
     // go though S  
     for(int i = 0;i <= S.length()-k;i++){  
       Queue<String> queue = queues.get(i%k);  
       Map<String, Integer> map = maps.get(i%k);   
       String s = S.substring(i,i+k);  
       if(map.containsKey(s)){  
         queue.add(s);  
         map.put(s,map.get(s)-1);  
         if(map.get(s)==0) map.remove(s);  
       }else{  
         if(!queue.isEmpty()){  
           if(s.equals(queue.peek())) // when the word to be poll == the word to be add  
             queue.add(queue.poll()); // don't need to modify map  
           else  
             while(!queue.isEmpty()) setMap(map, queue.poll());  
         }  
       }  
       // find a match  
       if(queue.size()==L.length){  
         rslt.add(i-(L.length-1)*k);  
         setMap(map,queue.poll());  
       }  
     }  
     return rslt;  
   }  
   private void setMap(Map<String, Integer> map, String s){  
     if(!map.containsKey(s))  
       map.put(s,1);  
     else  
       map.put(s,map.get(s)+1);  
     return;  
   }  
 }  



Improved Way: 我看了https://oj.leetcode.com/discuss/20151/an-o-n-solution-with-detailed-explanation这个算法后发现queue完全可以用一个变量来代替,记录长度即可。

 public class Solution {  
   public List<Integer> findSubstring(String S, String[] L) {  
     List<Integer> rslt = new ArrayList<Integer>();  
     // edge case  
     if(L.length==0) return rslt;  
     // initialize k queues and k maps  
     int k = L[0].length();  
     int queues[] = new int[k];  
     List<Map<String, Integer>> maps = new ArrayList<Map<String, Integer>>();  
     Map<String, Integer> map_temp = new HashMap<String, Integer>();  
     for(int i = 0;i < L.length;i++) setMap(map_temp, L[i]);  
     for(int i = 0;i < k;i++) queues[i] = 0;  
     for(int i = 0;i < k;i++) maps.add(new HashMap<String, Integer>(map_temp));  
     // go though S  
     for(int i = 0;i <= S.length()-k;i++){  
       int queue = queues[i%k];  
       Map<String, Integer> map = maps.get(i%k);   
       String s = S.substring(i,i+k);  
       if(map.containsKey(s)){  
         queue++;  
         map.put(s,map.get(s)-1);  
         if(map.get(s)==0) map.remove(s);  
         // find a match  
         if(queue==L.length){  
           rslt.add(i-(queue-1)*k);  
           setMap(map, S.substring(i-k*(queue-1),i-k*(queue-1)+k));  
           queue--;  
         }  
       }else{  
         if(queue > 0){  
           if(!s.equals(S.substring(i-k*queue,i-k*queue+k)))  
             while(queue > 0){  
               setMap(map, S.substring(i-k*queue,i-k*queue+k));  
               queue--;  
             }  
         }  
       }  
       queues[i%k] = queue;  
     }  
     return rslt;  
   }  
   private void setMap(Map<String, Integer> map, String s){  
     if(!map.containsKey(s))  
       map.put(s,1);  
     else  
       map.put(s,map.get(s)+1);  
     return;  
   }  
 }  


Unique Paths II


Unique Paths II



 


Follow up for "Unique Paths":
Now consider if some obstacles are added to the grids. How many unique paths would there be?
An obstacle and empty space is marked as 1 and 0 respectively in the grid.
For example,

There is one obstacle in the middle of a 3x3 grid as illustrated below.
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
The total number of unique paths is 2.
Note: m and n will be at most 100.

Naive Way: 在算法上修改。这是Unique Path 的算法。

public class Solution {
    public int uniquePaths(int m, int n) {
        int opt[][] = new int[m][n];
        // base case
        for(int i = 0;i < m;i++) opt[i][0] = 1;
        for(int j = 0;j < n;j++) opt[0][j] = 1;
        // ieration
        for(int i = 1;i < m;i++)
            for(int j = 1;j < n;j++)
                opt[i][j] = opt[i-1][j] + opt[i][j-1];
        return opt[m-1][n-1];
    }
}


如果当前有障碍物,那么就要置0,所以新逻辑是
opt[i][0] = matrix[i][0] ==0?0:opt[i-1][0];
opt[0][j] = matrix[0][j]==0?0:opt[0][j-1];
opt[i][j] = matrix[i][j]==0?0:opt[i-1][j]+opt[i][j-1];

算法复杂度为O(nm), space O(nm)。

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        if(m==0) return 0;
        int n = obstacleGrid[0].length;
        
        int opt[][] = new int[m][n];
        // base case
        opt[0][0] = obstacleGrid[0][0]==1?0:1;
        for(int i = 1;i < m;i++) opt[i][0] = obstacleGrid[i][0]==1?0:opt[i-1][0];
        for(int j = 1;j < n;j++) opt[0][j] = obstacleGrid[0][j]==1?0:opt[0][j-1];
        // ieration
        for(int i = 1;i < m;i++)
            for(int j = 1;j < n;j++)
                opt[i][j] = obstacleGrid[i][j]==1?0:(opt[i-1][j] + opt[i][j-1]);
        return opt[m-1][n-1]; 
    }
}

对应的,写出简化space的形式。这里有一点和之前不一样是i=0也要写进循环,它对应单一一行的情况,因为之前没有障碍,一行就是1,现在还要判断一下是否有障碍,

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        if(m==0) return 0;
        int n = obstacleGrid[0].length;
        int row[] = new int[m];
        // base case
        for(int i = 0;i < m;i++) row[i] = obstacleGrid[i][0]==1?0:(i==0?1:row[i-1]);
        // iteration
        for(int j = 1;j < n;j++)
            for(int i = 0;i < m;i++)
                row[i] = obstacleGrid[i][j]==1?0:((i==0 || obstacleGrid[i-1][j]==1?0:row[i-1]) + (obstacleGrid[i][j-1]==1?0:row[i]));
        return row[m-1]; 
    }
}

附上之前的代码方便对照:

public class Solution {
    public int uniquePaths(int m, int n) {
        if(m > n) return uniquePaths(n,m);
        int row[] = new int[m];
        // base case
        for(int i = 0;i < m;i++) row[i] = 1;
        // ieration
        for(int j = 1;j < n;j++)
            for(int i = 1;i < m;i++)
                row[i] = row[i] + row[i-1];
        return row[m-1];
    }
}

还有就是为了达到 O(min(m,n))的space,应该要比较一下n和m,取较小的做数组。


Improved Way: 这道题跟之前有一个很大的不同在于,输入参数有一个m-by-n的数组,如果可以破坏这个数组,就可以利用原来的数组存opt而不需要额外的空间。

space O(1)

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        if(m==0) return 0;
        int n = obstacleGrid[0].length;
        
        // base case
        obstacleGrid[0][0] = obstacleGrid[0][0]==1?0:1;
        for(int i = 1;i < m;i++) obstacleGrid[i][0] = obstacleGrid[i][0]==1?0:obstacleGrid[i-1][0];
        for(int j = 1;j < n;j++) obstacleGrid[0][j] = obstacleGrid[0][j]==1?0:obstacleGrid[0][j-1];
        // ieration
        for(int i = 1;i < m;i++)
            for(int j = 1;j < n;j++)
                obstacleGrid[i][j] = obstacleGrid[i][j]==1?0:(obstacleGrid[i-1][j] + obstacleGrid[i][j-1]);
        return obstacleGrid[m-1][n-1]; 
    }
}

Unique Paths


Unique Paths



 


A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).
How many possible unique paths are there?


Above is a 3 x 7 grid. How many possible unique paths are there?
Note: m and n will be at most 100.


Naive Way: 第一反应是用DP做。
基本逻辑为:
opt(i,j) // # of paths from (0,0) to (i,j)
// base case
opt(i,0)=1 0<=i<m
opt(0,j)=1 0<=j<n
// iteration
opt(i,j) = opt(i-1,j) + opt(i,j-1)


算法复杂度为O(nm), space O(mn)

public class Solution {
    public int uniquePaths(int m, int n) {
        int opt[][] = new int[m][n];
        // base case
        for(int i = 0;i < m;i++) opt[i][0] = 1;
        for(int j = 0;j < n;j++) opt[0][j] = 1;
        // ieration
        for(int i = 1;i < m;i++)
            for(int j = 1;j < n;j++)
                opt[i][j] = opt[i-1][j] + opt[i][j-1];
        return opt[m-1][n-1];
    }
}


将2D矩阵用两个1D数组表示可节省空间。space O(m+n)

public class Solution {
    public int uniquePaths(int m, int n) {
        int row[] = new int[m];
        int col[] = new int[n];
        // base case
        for(int i = 0;i < m;i++) row[i] = 1;
        for(int j = 0;j < n;j++) col[j] = 1;
        // ieration
        for(int i = 1;i < m;i++){
            for(int j = 1;j < n;j++){
                row[i] += col[j];
                col[j] = row[i];
            }
        }
        return row[m-1];
    }
}


Improved Way: Discuss里有两种更好的方法,一种是对两个1D数组的再做简化。
因为从上一格到下一格只有一条路,下一格其实并不需要额外记录,只需用上一格的数据就可以,所以一旦上一层的前一格记录好了,当前层的前一个也就记录好了,因为二者是一样的。只需要对每一层做row[i] = row[i] + row[i-1]的操作了。

space 是 O(min(m,n))

public class Solution {
    public int uniquePaths(int m, int n) {
        if(m > n) return uniquePaths(n,m);
        int row[] = new int[m];
        // base case
        for(int i = 0;i < m;i++) row[i] = 1;
        // ieration
        for(int j = 1;j < n;j++)
            for(int i = 1;i < m;i++)
                row[i] += row[i-1];
        return row[m-1];
    }
}


还有一个方法是 这个问题和杨辉三角 是一样的,可以构建一格杨辉三角取对应位置的值。这里等我做了杨辉三角再补充。