Friday, February 28, 2014

Leecode: Multiply String


Given two numbers represented as strings, return multiplication of the numbers as a string.
Note: The numbers can be arbitrarily large and are non-negative.
大数相乘的算法
  • 直接会cause overflow,所以每次都要两个single digit相乘,最大81,不会溢出。
  • 比如385 * 97, 就是个位=5 * 7,十位=8 * 7 + 5 * 9 ,百位=3 * 7 + 8 * 9,  千位=3*9
    可以每一位用一个Int表示,存在一个int[]里面。
  • 这个数组最大长度是num1.len + num2.len,比如99 * 99,最大不会超过10000,所以4位就够了。
  • 这种个位在后面的,不好做(10的0次方,可惜对应位的数组index不是0而是n-1),
    所以干脆先把string reverse了代码就清晰好多。
  • 最后结果前面的0要清掉。
Code:

public String multiply(String num1, String num2) {
    num1 = new StringBuilder(num1).reverse().toString();
    num2 = new StringBuilder(num2).reverse().toString();
    // even 99 * 99 is < 10000, so maximaly 4 digits
    int[] d = new int[num1.length() + num2.length()];
    for (int i = 0; i < num1.length(); i++) {
        int a = num1.charAt(i) - '0';
        for (int j = 0; j < num2.length(); j++) {
            int b = num2.charAt(j) - '0';
            d[i + j] += a * b;
        }
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < d.length; i++) {
        int digit = d[i] % 10;
        int carry = d[i] / 10;
        sb.insert(0, digit);
        if (i < d.length - 1)
            d[i + 1] += carry;
        }
    //trim starting zeros
    while (sb.length() > 0 && sb.charAt(0) == '0') {
        sb.deleteCharAt(0);
    }
    return sb.length() == 0 ? "0" : sb.toString();
}

完全按照两个数相乘的步骤做法:

public String multiply(String num1, String num2) {
        String ret = "";
        
        if(num1.length()==0 || num2.length()==0) return ret;
        if(num1.equals("0") || num2.equals("0")) return "0";
        
        int[] data = new int[num1.length() + num2.length()];
        
        for(int i = num1.length()-1; i>=0; i--){
            int a = num1.charAt(i) - '0';
            for(int j = num2.length()-1; j>=0; j--){
                int b = num2.charAt(j)- '0';
                data[i+j+1] += a*b;
            }
        }
        
        int digit = 0;
        int carry = 0;
         
        StringBuilder rt = new StringBuilder();
        for(int i= data.length-1; i>=0; i--){
            digit = (data[i]+carry)%10;
            rt.insert(0, digit);
            carry = (data[i]+carry)/10;
        }
        
        for(int i =0; i< rt.length(); i++){
            if(rt.charAt(0) == '0')
                rt.deleteCharAt(0);
        }
        
        ret = rt.length()==0? "0" : rt.toString();
        
        return ret;
    }



Tuesday, February 25, 2014

Leetcode: Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

clarification:  Here "adjacent" of a[i][j] means a[i+1][j] and a[i][j+1], if available(not out of bound), while a[i+1][j-1] is not "adjacent" element.
Note: it's not necessary to worry about the boundary of the index. The length of row i+1 is always bigger than that of row i by 1, so here j+1 is always valid.


This is a typical DP problem and can be solved with both top-down recursive method and bottom-up DP table.


My Solutions:


Top-down recursive solution:

    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        if(triangle.size()==0) return -1;
        int n = 0;
 
        return minimumTotal(triangle, 0, n);
    }
    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle, int curLayer, int Index){
        
        int ret =0;
        int temp1=0, temp2=0;
        
        if(curLayer == triangle.size()) return 0;
        
        ArrayList<Integer> al = triangle.get(curLayer);
        
        
        temp1 = minimumTotal(triangle, curLayer+1, Index);
        
        temp2 = minimumTotal(triangle, curLayer+1, Index+1);
        
        ret = temp1>=temp2? al.get(Index)+temp2 : al.get(Index)+temp1;   
            
        return ret;
        
    }

This solution got TLE error in leetcode.

Bottom-up solution: (Note the O(n) extra space requirement, I used only an ArrayList<Integer> with the length equal to the last ArrayList from the input).漂亮!

public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        if(triangle.size()==0) return -1;
        int n = triangle.size();

        
        ArrayList<Integer> ret = new ArrayList<Integer>();
        
        ArrayList<Integer> al = triangle.get(n-1);
        for(int j= 0; j< al.size(); j++){
            ret.add(al.get(j));
        }
        for(int i = n-2; i >= 0; i--){
            al = triangle.get(i);
            for(int j= 0; j< al.size(); j++){
                int temp = ret.get(j)<=ret.get(j+1)?ret.get(j) :ret.get(j+1);
                ret.set(j, al.get(j) + temp);
            }
            ret.remove(ret.size()-1);
        }
        
        return ret.get(0);    
        
    }

Pass the leetcode test!!

Sunday, February 23, 2014

Good Websites for Job Interview

Basic knowledge on computer science:

http://www.programmerinterview.com/

Leetcode: Populating Next Right Pointers in Each Node I&&II

Given a binary tree

struct TreeLinkNode {
      TreeLinkNode *left;
      TreeLinkNode *right;
      TreeLinkNode *next;
    }

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.
Initially, all next pointers are set to NULL.
Note:
  • You may only use constant extra space.
  • You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children).
For example,
Given the following perfect binary tree,
         1
       /  \
      2    3
     / \  / \
    4  5  6  7
 After calling your function, the tree should look like:
        1 -> NULL
       /  \
      2 -> 3 -> NULL
     / \  / \
    4->5->6->7 -> NULL
My Solution: This is a typical BFS problem. Instead of using the second queue to indicate a level, we can use the feature of a perfect binary tree: Number of nodes = 2^i - 1 (i = 1..n, ith level)
numNode indicates that current node is the nth in the original tree. If current node index is equal to 2^level - 1, current node is the rightmost node on the same level and its next is set to NULL. Otherwise its next is set to the first one in the queue.
Populating Next Right Pointers in Each Node II
Follow up for problem "Populating Next Right Pointers in Each Node".
What if the given tree could be any binary tree? Would your previous solution still work?
The equation  2^level - 1 doesn't hold any more.
Got a smart idea after doing google-search: use NULL as the indicator for the end of each level. Actually this method can also solve the first problem. Nice~~~~
My solution based on the idea:
Actually this problem can be solved in many different ways.



Friday, February 21, 2014

Leetcode: Search in Rotated Sorted Array I&II




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.


 public int search(int[] A, int target) {
        if(A == null || A.length ==0) return -1;
        
        int start=0;
        int end = A.length-1;
        int mid;

        
        while (start <= end){// Note: <=
            // avoid overflow if using (start+end)/2
            mid = start + (end-start)/2;
            
            if(A[mid] == target) return mid;
            
            if(A[start] <= A[mid]){
           // from start to mid must be sorted ascending
                if(target>=A[start] && target<A[mid]){
                    end = mid-1;// no duplicate, so can safely -1
                }
                else
                    start = mid+1;
            }
            else{//from mid to end must be sorted and ascending
                if(target>A[mid] && target<=A[end]){
                    start = mid+1;
                }
                else
                    end = mid-1;
            }
            
        }
        
        return -1;         
    }


To avoid some noisy <= or >=, rewrite code as

public int search(int[] A, int target) {
        if(A == null || A.length ==0) return -1;
        
        int start=0;
        int end = A.length-1;
        int mid;

        
        while (start <= end){// Note: <=
            if(A[start] == target) return start;
            if(A[end] == target) return end;
            // avoid overflow if using (start+end)/2
            mid = start + (end-start)/2;
            
            if(A[mid] == target) return mid;
            // from start to mid must be sorted ascending
            if(A[start] <= A[mid]){
                if(target>A[start] && target<A[mid]){
                    end = mid-1;// no duplicate, so we can safely -1
                }
                else
                    start = mid+1;
            }
            else{//from mid to end must be sorted and ascending
                if(target>A[mid] && target<A[end]){
                    start = mid+1;
                }
                else
                    end = mid-1;
            }
            
        }
        
        return -1;       
    }

If duplicates are allowed, that would be question II

My First Solution. Finally give up. Doesn't  work well.

  public boolean search(int[] A, int target) {
        if(A == null || A.length ==0) return false;
       
        int start=0;
        int end = A.length-1;
        int mid;

        while (start < end){// Note: <=
            if(A[start] == target) return true;
            if(A[end] == target) return true;
           
            mid = start + (end-start)/2;// avoid overflow if using (start+end)/2
           
            if(A[mid] == target) return true;
           
            if(A[start] < A[mid]){// from start to mid must be sorted ascending
               
                if(target>A[start] && target<A[mid]){
                    while(A[mid]==A[mid-1] && mid>start){mid--;}
                    end = mid-1;// no duplicate, so we can safely -1
                }
                else{
                    while(A[mid]==A[mid+1] && mid<end){mid++;}
                    start = mid+1;
                }
            }
            else if(A[start] > A[mid]){//from mid to end must be sorted and ascending
                if(target>A[mid] && target<A[end]){
                    while(A[mid]==A[mid+1] && mid<end){mid++;}
                    start = mid+1;
                }
                else{
                    while(A[mid]==A[mid-1] && mid>start){mid--;}
                    end = mid-1;
                }
            }
            else{// A[start] == A[mid]
                if(mid == start) {start++;}
                else{
                    //three possibilities, e.g. 11113 or 1131 or 1231111
                    if(A[start] ==A[end]){// something like 1231111 or 111113
                    //OK!!!!!Give up here. Cannot proceed@!
                        while(A[mid]==A[mid-1] && mid>start){mid--;}
                        end = mid-1;
                    }
                    else{
                        while(A[mid]==A[mid+1] && mid<end){mid++;}
                        start = mid+1;
                    }
                }
            }
           
        }
       
        if(A[start] == target) return true;
        else return false; 
    }

 Next solution. Simply add " while(A[start] ==A[end] && start != end) end--;" to solve it.


public class Solution {
    public boolean search(int[] A, int target) {
       
        if(A == null || A.length ==0) return false;
       
        int start=0;
        int end = A.length-1;
        int mid;

       
        while (start <= end){// Note: <=
       
            while(A[start] ==A[end] && start != end) end--;
           
            if(A[start] == target) return true;
            if(A[end] == target) return true;
           
            mid = start + (end-start)/2;// avoid overflow if using (start+end)/2
           
            if(A[mid] == target) return true;
           
            if(A[start] <= A[mid]){// from start to mid must be sorted ascending
                if(target>A[start] && target<A[mid]){
                    end = mid-1;// no duplicate, so we can safely -1
                }
                else
                    start = mid+1;
            }
            else{//from mid to end must be sorted and ascending
                if(target>A[mid] && target<A[end]){
                    start = mid+1;
                }
                else
                    end = mid-1;
            }
           
        }
       
        return false; 
       
       
    }
 
}

Thursday, February 20, 2014

Sort

Mergesort VS Quicksort

Mergesort

Algorithm

  1. Divide the unsorted list into n sublists, each containing 1 element (a list of 1 element is considered sorted).
  2. Repeatedly merge sublists to produce new sorted sublists until there is only 1 sublist remaining. This will be the sorted list.

Performance

  • Worst Case O(n log n)
  • Average Case O(n log n)
  • Worst Case Space Complexity O(n) auxiliary

Example code

public ListNode sortList(ListNode head) {
    
  ListNode node = head;
        int n =0;
        
        while(node!= null){
            n++;
            node = node.next;
        }
        
        node = head;
        
        return sortList(head, n);
        
        
    }
    
 public ListNode sortList(ListNode head, int len){
     ListNode node = head;
        ListNode pre = null;
        ListNode left = head;
        ListNode right = null;
        
        if(len<2) return head;
        
   for(int i=1; i<=len/2; i++){
             pre = node;
             node = node.next;
      }
         
        pre.next = null;
        right = node;
        
        left = sortList(left, len/2);
        right = sortList(right, len-len/2);
        
        return mergeList(left, len/2,  right, len-len/2);
 }
    
    public ListNode mergeList(ListNode left, int leftLen, ListNode right, int rightLen){
     ListNode newListHead = null;
     ListNode node = null;
     ListNode leftNode = left, rightNode = right;
         
     if(left.val > right.val){
      newListHead = right;
      right = right.next;
      rightLen--;
      rightNode = right;
     }
     else{
      newListHead = left;
      left = left.next;
      leftLen--;
      leftNode = left;
     }
     
     node = newListHead;
      
     while(leftLen>0 || rightLen>0){
      
      if(leftLen>0 && rightLen>0){
       if(leftNode.val > rightNode.val){
        node.next = rightNode;
        rightNode = rightNode.next;
        rightLen--;
        node = node.next;
        node.next = null;
        
       }
       else{
        node.next = leftNode;
        leftNode = leftNode.next;
        leftLen--;
        node = node.next;
        node.next = null;
       }
      }
      else if(leftLen == 0){
       node.next = rightNode;
       break;
      }
      else if(rightLen == 0){
       node.next = leftNode;
       break;
      }
      
     }
     
     return newListHead;
    }
 
Reference wikipedia Mergesort

Quicksort

Algorithm

Quicksort is a divide and conquer algorithm. Quicksort first divides a large list into two smaller sub-lists: the low elements and the high elements. Quicksort can then recursively sort the sub-lists. The steps are:
  1. Pick an element, called a pivot, from the list.
  2. Reorder the list so that all elements with values less than the pivot come before the pivot, while all elements with values greater than the pivot come after it (equal values can go either way). After this partitioning, the pivot is in its final position. This is called the partition operation.
  3. Recursively apply the above steps to the sub-list of elements with smaller values and separately the sub-list of elements with greater values. The base case of the recursion are lists of size zero or one, which never need to be sorted.

Performance

  • Worst Case O(n2)
  • Average Case O(n log n)
  • Worst Case Space Complexity O(n) auxiliary (naive) or O(log n) auxiliary

Example code

public void sort(int[] num, int start, int end){  
    int pivotIdex = partition(num, start, end);
    
    if(start < pivotIdex-1)
     sort(num, start, pivotIdex-1);
    if(pivotIdex<end)
        sort(num, pivotIdex, end);   
   }
   
public int partition(int arr[], int left, int right){    
         int i = left, j = right;
         int tmp;
         int pivot = arr[(left + right) / 2];
         while (i <= j) {
               while (arr[i] < pivot)
                     i++;
               while (arr[j] > pivot)
                     j--;
               if (i <= j) {
                     tmp = arr[i];
                     arr[i] = arr[j];
                     arr[j] = tmp;
                     i++;
                     j--;
               }
         };
         return i;   
   } 

Comparison

Although heapsort has the same time bounds as merge sort, it requires only Θ(1) auxiliary space instead of merge sort's Θ(n). On typical modern architectures, efficient quicksort implementations generally outperform mergesort for sorting RAM-based arrays. On the other hand, merge sort is a stable sort and is more efficient at handling slow-to-access sequential media. Merge sort is often the best choice for sorting a linked list: in this situation it is relatively easy to implement a merge sort in such a way that it requires only Θ(1) extra space, and the slow random-access performance of a linked list makes some other algorithms (such as quicksort) perform poorly, and others (such as heapsort) completely impossible.
As of Perl 5.8, merge sort is its default sorting algorithm (it was quicksort in previous versions of Perl). In Java, the Arrays.sort() methods use merge sort or a tuned quicksort depending on the datatypes and for implementation efficiency switch to insertion sort when fewer than seven array elements are being sorted.[11] Python uses timsort, another tuned hybrid of merge sort and insertion sort, that has become the standard sort algorithm in Java SE 7,[12] on the Android platform,[13] and in GNU Octave.[14]
Reference WikiPedia Quicksort

Wednesday, February 19, 2014

code wrap

code
1:    string multiply(string num1, string num2) {  
2:      // Start typing your C/C++ solution below  
3:      // DO NOT write int main() function  
4:      if(num1.size() ==0 || num2.size() ==0) return 0;  
5:      string res(num1.size()+num2.size()+1, '0');  
6:      std::reverse(num1.begin(), num1.end());  
7:      std::reverse(num2.begin(), num2.end());  
 
https://gist.github.com/

Tuesday, February 18, 2014

Leetcode: Generate Parentheses


Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
"((()))", "(()())", "(())()", "()(())", "()()()"
solution: 递归构造字符串。当左括号出现次数<N时,可以放置新的左括号。当右括号出现个数小于左括号出现个数时,就可以放置新的右括号。小破题,但需要对递归思想有很清楚的理解.

Idea: This is a typical problem solved by recursion. The key point is the following rule: when the number of left parentheses is less than N, feel free to put a left parenthesis; Only when the number of right ones is less than the number of left ones,we can put a right parenthesis.
public ArrayList<String> generateParenthesis(int n) {
        
    ArrayList<String> ret = new ArrayList<String>();
    String str = "";
        
    if(n<1) return ret;
        
    getParenthesis(n, n, str, ret);
        
    return ret;
}
    
public void getParenthesis(int numL, int numR, String solution, ArrayList<String> ret){
        
    if(numL==0 && numR==0){
        ret.add(solution);
        return;
    }
        
    if(numL>0){
        getParenthesis(numL-1, numR, solution+"(", ret );
    }
        
    if(numL < numR){
        getParenthesis(numL, numR-1, solution+")", ret );
    }
        
}


Leetcode: Restore IP Addresses

Given a string containing only digits, restore it by returning all possible valid IP address combinations.
For example:
Given "25525511135",
return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)

My Solution: This is a classic DFS problem. Not difficult itself, but need to pay attention to some details, such as to avoid the accumulated effects by using temporary local variables, tempLen and tempSol.


public static ArrayList<String> restoreIpAddresses(String s) {
        
  ArrayList<String> ret = new ArrayList<String>();
  String solution = "";
  
  if(s.length()<4 || s.length()>12) return ret;
   
  restoreIp(s, solution, ret, 1);
  
  String str = solution;
  
  return  ret;
 }
 
 public static void restoreIp(String s, String solution, ArrayList<String> ret, int len){
  String cur = "";
  String restStr = "";
  
  if(len == 4){
   if(isValid(s)){
    solution += s;
    ret.add(solution);
   }
   return;
  }
  
  for(int i=0; i<3; i++){
   //cur = s.substring(0,i+1);//sigh!!!!! out of index
   if(s.length() <= i+1)
       cur = s;
   else
       cur = s.substring(0,i+1);
       
   if(isValid(cur)){
    restStr = s.substring(i+1);
    if(restStr.equals(""))return;
    String tempSol = solution;//this is very important!!!!
    int tempLen = len;//this is very important!!!!!
    tempSol +=cur + ".";
    tempLen++;
    if(tempLen>4) return;
    restoreIp(restStr, tempSol, ret, tempLen);
   }
  }
  
 }
 
 public static boolean isValid(String s){
  int val = Integer.parseInt(s);
  if(s.length()>1 && s.charAt(0) == '0') return false;//very important!!!!! to avoid 05
  if(val>=0 && val<=255) return true;
  else return false;
 }





Experience: Read the comments with the code.

Monday, February 17, 2014

Leetcode: Combination Sum I && II

Combination Sum I

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
All numbers (including target) will be positive integers.
Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7 and target 7,
A solution set is:
[7]
[2, 2, 3]

Solution:

Idea: this problem is not difficult to solve with backtrack.
Two key points: 1. The classic pattern of recursion: use two vectors to recursively save the result, one for the final result and the other for one possible solution. 2. remember to pop_back after adding to the solution to ret. 3. start from current index in the new recursion to get answers like [[1,1,1], [1,2]], input [1, 2], 3



Combination Sum II


Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:
All numbers (including target) will be positive integers.
Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5 and target 8,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]

The difference lies that for next round start from i+1 as indicated in the following code:



LeetCode: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

Solution: Two Dimensional Dynamic Programming


Remember when a problem is to find the optimal value, DP is a possible solution.

This is a classic DP problem.

dp[i][j] refers to edit distance between X[0...i] and Y[0...j], so

Think about it. For the last step, we can either:

a. insert a letter of Y[len2-1] to X

b. delete a letter X[len1-1] from X
c. substitue X[len1-1] with Y[len2-1]

Basic cases: dp[0][0] = 0; dp[i][0] = i,  i delete operations, similarly (empty string to word[0..i]), dp[0][j] = j, j insert operations


2. Optimal functions:

dp[i][j]=min((d[i-1][j]+1),(d[i][j-1]+1),(d[i-1][j-1]+0 or 1))

d[i-1][j] last step is to delete X[i] (X[1.i-1] to Y[1..j] is already optimal)

d[i][j-1] last step is to insert Y[j] ( X[1..i] to Y[1..j-1] is already optimal)

d[i-1][j-1] last step is to substitute X[i] with Y[j], if(X[i] == Y[j]) plus 0, else plus 1


这里是中文解释(改进版)

但对于编辑距离的话,当我们要计算d(i,j)时,即计算A(i)到B(j)之间的编辑距离,此时,设A(i)形式是somestr1c;B(i)形如somestr2d的话,

将somestr1变成somestr2的编辑距离已知是d(i-1,j-1)

将somestr1c变成somestr2的编辑距离已知是d(i,j-1) 将somestr1变成somestr2d的编辑距离已知是d(i-1,j) 那么利用这三个变量,就可以递推出d(i,j)了: 如果c==d,显然编辑距离和d(i-1,j-1)是一样的 如果c!=d,情况稍微复杂一点,
如果将c替换成d,编辑距离是somestr1变成somestr2的编辑距离 + 1,也就是d(i-1,j-1) + 1
d(i,j-1) 意味着已经将somestr1c变成somestr2的编辑距离, 现在只要再insert 字母d即可, 即d(i,j-1) + 1
d(i-1,j) 意味着已经将somestr1变成somestr2d了,即已经将somestr1c变成(somestr2d)c了 现在只需把A的最后一个字母c delete即可,d(i-1,j) + 1 



Experience:

DP array, use dp[m][n] to represent the result, so have to assign (m+1)*(n+1).

Using dp[m-1][n-1] is not recommended here.

LeetCode: Word Ladder I

LeetCode: Word Ladder I

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters. 
My solution:

The idea is to use BFS. First replace each character in a string with all other 25 letters to see if the new string is in the dictionary. If so, add it into the queue. To avoid strings that have been used previously, we use a set *usedWords*;
Another key point is to use two queues, one of which holds all new created strings on the same level, so that we can get a place to increase the level information.

The following code passes all test cases at one time.
public class Solution {
 
  public int ladderLength(String start, String end, HashSet<String> dict) {
        int len =0;
        char originalChar;
        String str;
        int i;
       
    if (start==null||end==null||dict==null||start.length()==0||start.length()!=end.length()){
            return 0;
        }
        //use a set to record all words
        HashSet<String> usedWords = new HashSet<String>();
       
        Queue<String> qToReadFrom = new LinkedList<String>();
   
        qToReadFrom.offer(start);//or q.add()

        while(!qToReadFrom.isEmpty()){
           
            Queue<String> qLevel = new LinkedList<String>();
           
            while(!qToReadFrom.isEmpty()){
               
                str = qToReadFrom.poll();
               
                for(i=0; i<str.length(); i++){
                    originalChar = str.charAt(i);
                    StringBuilder sb = new StringBuilder(str);
                    for(char j='a'; j<='z'; j++){
                        if(j == originalChar) continue;
                        sb.setCharAt(i, j);
                       
                        if(sb.toString().equals(end)){
                           
                            return len +2;
                        }
                       
                      if(!usedWords.contains(sb.toString()) && dict.contains(sb.toString())){                       
                            usedWords.add(sb.toString());
                            qLevel.add(sb.toString());
                        }
                       
                    }
                }
                           
            }
            //Level++
            qToReadFrom = qLevel;      
            len++;
        }  
       
        return 0;
    }
   
}