• 代码面试最常用的10大算法(一)


    1.String/Array/Matrix

    在Java中,String是一个包含char数组和其它字段、方法的类。如果没有IDE自动完成代码,下面这个方法大家应该记住:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    toCharArray() //get char array of a String
    Arrays.sort()  //sort an array
    Arrays.toString(char[] a) //convert to string
    charAt(int x) //get a char at the specific index
    length() //string length
    length //array size
    substring(int beginIndex)
    substring(int beginIndex, int endIndex)
    Integer.valueOf()//string to integer
    String.valueOf()/integer to string

     

    String/arrays很容易理解,但与它们有关的问题常常需要高级的算法去解决,例如动态编程、递归等。

    下面列出一些需要高级算法才能解决的经典问题:

     

     

     Evaluate Reverse Polish Notation:

    The problem:

    Evaluate the value of an arithmetic expression in Reverse Polish Notation.
    
    Valid operators are +, -, *, /. Each operand may be an integer or another expression.
    
    Some examples:
      ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9
      ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6
    

    1. Naive Approach

    This problem is simple. After understanding the problem, we should quickly realize that this problem can be solved by using a stack. We can loop through each element in the given array. When it is a number, push it to the stack. When it is an operator, pop two numbers from the stack, do the calculation, and push back the result.

    Evaluate Reverse Polish Notation

    The following is the code. It runs great by feeding a small test. However, this code contains compilation errors in leetcode. Why?

    public class Test {
     
    	public static void main(String[] args) throws IOException {
    		String[] tokens = new String[] { "2", "1", "+", "3", "*" };
    		System.out.println(evalRPN(tokens));
    	}
     
    	public static int evalRPN(String[] tokens) {
    		int returnValue = 0;
    		String operators = "+-*/";
     
    		Stack<String> stack = new Stack<String>();
     
    		for (String t : tokens) {
    			if (!operators.contains(t)) {
    				stack.push(t);
    			} else {
    				int a = Integer.valueOf(stack.pop());
    				int b = Integer.valueOf(stack.pop());
    				switch (t) {
    				case "+":
    					stack.push(String.valueOf(a + b));
    					break;
    				case "-":
    					stack.push(String.valueOf(b - a));
    					break;
    				case "*":
    					stack.push(String.valueOf(a * b));
    					break;
    				case "/":
    					stack.push(String.valueOf(b / a));
    					break;
    				}
    			}
    		}
     
    		returnValue = Integer.valueOf(stack.pop());
     
    		return returnValue;
    	}
    }

    The problem is that switch string statement is only available from JDK 1.7. Leetcode apparently use versions below that.

    2. Accepted Solution

    If you want to use switch statement, you can convert the above by using the following code which use the index of a string “+-*/”.

    public class Solution {
        public int evalRPN(String[] tokens) {
     
            int returnValue = 0;
     
            String operators = "+-*/";
     
            Stack<String> stack = new Stack<String>();
     
            for(String t : tokens){
                if(!operators.contains(t)){
                    stack.push(t);
                }else{
                    int a = Integer.valueOf(stack.pop());
                    int b = Integer.valueOf(stack.pop());
                    int index = operators.indexOf(t);
                    switch(index){
                        case 0:
                            stack.push(String.valueOf(a+b));
                            break;
                        case 1:
                            stack.push(String.valueOf(b-a));
                            break;
                        case 2:
                            stack.push(String.valueOf(a*b));
                            break;
                        case 3:
                            stack.push(String.valueOf(b/a));
                            break;
                    }
                }
            }
     
            returnValue = Integer.valueOf(stack.pop());
     
            return returnValue;
     
        }
    }




    Longest Palindromic Substring:
    Finding the longest palindromic substring is a classic problem of coding interview. In this post, I will summarize 3 different solutions for this problem.

    1. Naive Approach

    
    

    Naively, we can simply examine every substring and check if it is palindromic. The time complexity is O(n^3). If this is submitted to LeetCode onlinejudge, an error message will be returned – “Time Limit Exceeded”. Therefore, this approach is just a start, we need better algorithm.

    
    
    public static String longestPalindrome1(String s) {
     
    	int maxPalinLength = 0;
    	String longestPalindrome = null;
    	int length = s.length();
     
    	// check all possible sub strings
    	for (int i = 0; i < length; i++) {
    		for (int j = i + 1; j < length; j++) {
    			int len = j - i;
    			String curr = s.substring(i, j + 1);
    			if (isPalindrome(curr)) {
    				if (len > maxPalinLength) {
    					longestPalindrome = curr;
    					maxPalinLength = len;
    				}
    			}
    		}
    	}
     
    	return longestPalindrome;
    }
     
    public static boolean isPalindrome(String s) {
     
    	for (int i = 0; i < s.length() - 1; i++) {
    		if (s.charAt(i) != s.charAt(s.length() - 1 - i)) {
    			return false;
    		}
    	}
     
    	return true;
    }
    
    

    2. Dynamic Programming

    
    

    Let s be the input string, i and j are two indices of the string.

    
    

    Define a 2-dimension array “table” and let table[i][j] denote whether substring from i to j is palindrome.

    
    

    Start condition:

    
    
    table[i][i] == 1;
    table[i][i+1] == 1  => s.charAt(i) == s.charAt(i+1) 
    
    
    

    Changing condition:

    
    
    table[i][j] == 1 => table[i+1][j-1] == 1 && s.charAt(i) == s.charAt(j)
    
    
    

    Time O(n^2) Space O(n^2)

    
    
    public static String longestPalindrome2(String s) {
    	if (s == null)
    		return null;
     
    	if(s.length() <=1)
    		return s;
     
    	int maxLen = 0;
    	String longestStr = null;
     
    	int length = s.length();
     
    	int[][] table = new int[length][length];
     
    	//every single letter is palindrome
    	for (int i = 0; i < length; i++) {
    		table[i][i] = 1;
    	}
    	printTable(table);
     
    	//e.g. bcba
    	//two consecutive same letters are palindrome
    	for (int i = 0; i <= length - 2; i++) {
    		if (s.charAt(i) == s.charAt(i + 1)){
    			table[i][i + 1] = 1;
    			longestStr = s.substring(i, i + 2);
    		}	
    	}
    	printTable(table);
    	//condition for calculate whole table
    	for (int l = 3; l <= length; l++) {
    		for (int i = 0; i <= length-l; i++) {
    			int j = i + l - 1;
    			if (s.charAt(i) == s.charAt(j)) {
    				table[i][j] = table[i + 1][j - 1];
    				if (table[i][j] == 1 && l > maxLen)
    					longestStr = s.substring(i, j + 1);
    			} else {
    				table[i][j] = 0;
    			}
    			printTable(table);
    		}
    	}
     
    	return longestStr;
    }
    public static void printTable(int[][] x){
    	for(int [] y : x){
    		for(int z: y){
    			System.out.print(z + " ");
    		}
    		System.out.println();
    	}
    	System.out.println("------");
    }
    
    

    Given an input, we can use printTable method to examine the table after each iteration. For example, if input string is “dabcba”, the final matrix would be the following:

    
    
    1 0 0 0 0 0 
    0 1 0 0 0 1 
    0 0 1 0 1 0 
    0 0 0 1 0 0 
    0 0 0 0 1 0 
    0 0 0 0 0 1 
    
    
    

    From the table, we can clear see that the longest string is in cell table[1][5].

    
    

    3. Simple Algorithm

    
    

    Time O(n^2), Space O(1)

    
    
    public String longestPalindrome(String s) {
    	if (s.isEmpty()) {
    		return null;
    	}
     
    	if (s.length() == 1) {
    		return s;
    	}
     
    	String longest = s.substring(0, 1);
    	for (int i = 0; i < s.length(); i++) {
    		// get longest palindrome with center of i
    		String tmp = helper(s, i, i);
    		if (tmp.length() > longest.length()) {
    			longest = tmp;
    		}
     
    		// get longest palindrome with center of i, i+1
    		tmp = helper(s, i, i + 1);
    		if (tmp.length() > longest.length()) {
    			longest = tmp;
    		}
    	}
     
    	return longest;
    }
     
    // Given a center, either one letter or two letter, 
    // Find longest palindrome
    public String helper(String s, int begin, int end) {
    	while (begin >= 0 && end <= s.length() - 1 && s.charAt(begin) == s.charAt(end)) {
    		begin--;
    		end++;
    	}
    	return s.substring(begin + 1, end);
    }

    单词分割:

    Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

    For example, given
    s = “leetcode”,
    dict = ["leet", "code"].

    Return true because “leetcode” can be segmented as “leet code”.

    
    

    1. Naive Approach

    
    

    This problem can be solve by using a naive approach, which is trivial. A discussion can always start from that though.

    
    
    public class Solution {
        public boolean wordBreak(String s, Set<String> dict) {
                 return wordBreakHelper(s, dict, 0);
        }
     
        public boolean wordBreakHelper(String s, Set<String> dict, int start){
            if(start == s.length()) 
                return true;
     
            for(String a: dict){
                int len = a.length();
                int end = start+len;
     
                //end index should be <= string length
                if(end > s.length()) 
                    continue;
     
                if(s.substring(start, start+len).equals(a))
                    if(wordBreakHelper(s, dict, start+len))
                        return true;
            }
     
            return false;
        }
    }
    
    

    Time: O(2^n)

    
    

    2. Dynamic Programming

    
    

    The key to solve this problem by using dynamic programming approach:

    
    
    • Define an array t[] such that t[i]==true => 0-(i-1) can be segmented using dictionary
    • Initial state t[0] == true
    
    
    public class Solution {
        public boolean wordBreak(String s, Set<String> dict) {
            boolean[] t = new boolean[s.length()+1];
            t[0] = true; //set first to be true, why?
            //Because we need initial state
     
            for(int i=0; i<s.length(); i++){
                //should continue from match position
                if(!t[i]) 
                    continue;
     
                for(String a: dict){
                    int len = a.length();
                    int end = i + len;
                    if(end > s.length())
                        continue;
     
                    if(t[end]) continue;
     
                    if(s.substring(i, end).equals(a)){
                        t[end] = true;
                    }
                }
            }
     
            return t[s.length()];
        }
    }
    
    

    Time: O(string length * dict size)

    
    

    One tricky part of this solution is the case:

    
    
    INPUT: "programcreek", ["programcree","program","creek"]. 
    
    
    

    We should get all possible matches, not stop at “programcree”.

    
    

    3. The More Interesting Problem

    
    

    The dynamic solution can tell us whether the string can be broken to words, but can not tell us what words the string is broken to. So how to get those words?

    
    

    Possible Solution 1 (from jk451):

    
    

    Change the “t” array to integer instead of boolean.

    Replacing setting t[end] to true (i.e. saying you have found a break up of 0..end substring] with setting t[end] to i, thus saying you have found a break up of 0..end substring and the last word in that break up is substring i..end of the main string.

    Then at the end if I can break up the string, I check t[s.length()]. THe last word in the break up will substring starting at t[s.length()] and ending at s.length()-1. And you repeate this procedure to get the other words.

    字梯:

    The problem:

    
    
    Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
    
    Only one letter can be changed at a time
    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.
    
    

    This problem is a classic problem that has been asked frequently during interviews. The following are two Java solutions.

    
    

    1. Naive Approach

    
    

    In a simplest way, we can start from start word, change one character each time, if it is in the dictionary, we continue with the replaced word, until start == end.

    
    
    public class Solution {
        public int ladderLength(String start, String end, HashSet<String> dict) {
     
            int len=0;
            HashSet<String> visited = new HashSet<String>();
     
            for(int i=0; i<start.length(); i++){
                char[] startArr = start.toCharArray();
     
                for(char c='a'; c<='z'; c++){
                    if(c==start.toCharArray()[i]){
                        continue;
                    }
     
                    startArr[i] = c;
                    String temp = new String(startArr);
                    if(dict.contains(temp)){
                        len++;
                        start = temp;
                        if(temp.equals(end)){
                            return len;
                        }
                    }
                }
            }
     
            return len;
        }
    }
    
    

    Apparently, this is not good enough. The following example exactly shows the problem. It can not find optimal path. The output is 3, but it actually only takes 2.

    
    
    Input:	"a", "c", ["a","b","c"]
    Output:	3
    Expected:	2
    
    
    

    2. Breath First Search

    
    

    So we quickly realize that this looks like a tree searching problem for which breath first guarantees the optimal solution.

    
    

    Assuming we have all English words in the dictionary, and the start is “hit” as shown in the diagram below.

    
    

    word-ladder

    
    

    We can use two queues to traverse the tree, one stores the nodes, the other stores the step numbers. Before starting coding, we can visualize a tree in mind and come up with the following solution.

    
    
    public class Solution {
        public int ladderLength(String start, String end, HashSet<String> dict) {
     
            if (dict.size() == 0)  
                return 0; 
     
            LinkedList<String> wordQueue = new LinkedList<String>();
            LinkedList<Integer> distanceQueue = new LinkedList<Integer>();
     
            wordQueue.add(start);
            distanceQueue.add(1);
     
     
            while(!wordQueue.isEmpty()){
                String currWord = wordQueue.pop();
                Integer currDistance = distanceQueue.pop();
     
                if(currWord.equals(end)){
                    return currDistance;
                }
     
                for(int i=0; i<currWord.length(); i++){
                    char[] currCharArr = currWord.toCharArray();
                    for(char c='a'; c<='z'; c++){
                        currCharArr[i] = c;
     
                        String newWord = new String(currCharArr);
                        if(dict.contains(newWord)){
                            wordQueue.add(newWord);
                            distanceQueue.add(currDistance+1);
                            dict.remove(newWord);
                        }
                    }
                }
            }
     
            return 0;
        }
    }
    
    

    3. What learned from this problem?

    
    
    1. Use breath-first or depth-first search to solve problems
    2. Use two queues, one for words and another for counting
    Median of Two Sorted Arrays

    LeetCode Problem:

    
    

    There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

    
    

    Keys to solve this problem

    
    

    This problem can be converted to the problem of finding kth element, k is (A’s length + B’ Length)/2.

    
    

    If any of the two arrays is empty, then the kth element is the non-empty array’s kth element.
    If k == 0, the kth element is the first element of A or B.

    
    

    For normal cases(all other cases), we need to move the pointer at the pace of half of an array length.

    
    
    public static double findMedianSortedArrays(int A[], int B[]) {
    	int m = A.length;
    	int n = B.length;
     
    	if ((m + n) % 2 != 0) // odd
    		return (double) findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1);
    	else { // even
    		return (findKth(A, B, (m + n) / 2, 0, m - 1, 0, n - 1) 
    			+ findKth(A, B, (m + n) / 2 - 1, 0, m - 1, 0, n - 1)) * 0.5;
    	}
    }
     
    public static int findKth(int A[], int B[], int k, 
    	int aStart, int aEnd, int bStart, int bEnd) {
     
    	int aLen = aEnd - aStart + 1;
    	int bLen = bEnd - bStart + 1;
     
    	// Handle special cases
    	if (aLen == 0)
    		return B[bStart + k];
    	if (bLen == 0)
    		return A[aStart + k];
    	if (k == 0)
    		return A[aStart] < B[bStart] ? A[aStart] : B[bStart];
     
    	int aMid = aLen * k / (aLen + bLen); // a's middle count
    	int bMid = k - aMid - 1; // b's middle count
     
    	// make aMid and bMid to be array index
    	aMid = aMid + aStart;
    	bMid = bMid + bStart;
     
    	if (A[aMid] > B[bMid]) {
    		k = k - (bMid - bStart + 1);
    		aEnd = aMid;
    		bStart = bMid + 1;
    	} else {
    		k = k - (aMid - aStart + 1);
    		bEnd = bMid;
    		aStart = aMid + 1;
    	}
     
    	return findKth(A, B, k, aStart, aEnd, bStart, bEnd);
    }
    正则表达式匹配

    Problem:

    
    

    Implement regular expression matching with support for ‘.’ and ‘*’.

    
    
    '.' Matches any single character.
    '*' Matches zero or more of the preceding element.
    
    The matching should cover the entire input string (not partial).
    
    The function prototype should be:
    bool isMatch(const char *s, const char *p)
    
    Some examples:
    isMatch("aa","a") return false
    isMatch("aa","aa") return true
    isMatch("aaa","aa") return false
    isMatch("aa", "a*") return true
    isMatch("aa", ".*") return true
    isMatch("ab", ".*") return true
    isMatch("aab", "c*a*b") return true
    
    
    

    Thoughts for This Problem

    
    

    Overall, there are 2 different cases: 1) the second char of pattern is “*” , and 2) the second char of pattern is not “*”.

    
    

    For the 1st case, if the first char of pattern is not “.”, the first char of pattern and string should be the same. Then continue to match the left part.

    
    

    For the 2nd case, if the first char of pattern is “.” or first char of pattern == the first i char of string, continue to match the left.

    
    

    Be careful about the offset.

    
    

    Java Solution

    
    

    The following Java solution is accepted.

    
    
    public class Solution {
        public boolean isMatch(String s, String p) {
     
            if(p.length() == 0)
                return s.length() == 0;
     
            //p's length 1 is special case    
            if(p.length() == 1 || p.charAt(1) != '*'){
                if(s.length() < 1 || (p.charAt(0) != '.' && s.charAt(0) != p.charAt(0)))
                    return false;
                return isMatch(s.substring(1), p.substring(1));    
     
            }else{
                int len = s.length();
     
                int i = -1; 
                while(i<len && (i < 0 || p.charAt(0) == '.' || p.charAt(0) == s.charAt(i))){
                    if(isMatch(s.substring(i+1), p.substring(2)))
                        return true;
                    i++;
                }
                return false;
            } 
        }
    }
    插入间隔

    Problem:

    
    

    Given a set of non-overlapping & sorted intervals, insert a new interval into the intervals (merge if necessary).

    
    
    Example 1:
    Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9].
    
    Example 2:
    Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16].
    
    This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].
    
    
    

    Thoughts of This Problem

    
    

    Quickly summarize 3 cases. Whenever there is intersection, created a new interval.

    
    

    insert-interval

    
    

    Java Solution

    
    
    /**
     * Definition for an interval.
     * public class Interval {
     *     int start;
     *     int end;
     *     Interval() { start = 0; end = 0; }
     *     Interval(int s, int e) { start = s; end = e; }
     * }
     */
    public class Solution {
        public ArrayList<Interval> insert(ArrayList<Interval> intervals, Interval newInterval) {
     
            ArrayList<Interval> result = new ArrayList<Interval>();
     
            for(Interval interval: intervals){
                if(interval.end < newInterval.start){
                    result.add(interval);
                }else if(interval.start > newInterval.end){
                    result.add(newInterval);
                    newInterval = interval;        
                }else if(interval.end >= newInterval.start || interval.start <= newInterval.end){
                    newInterval = new Interval(Math.min(interval.start, newInterval.start), Math.max(newInterval.end, interval.end));
                }
            }
     
            result.add(newInterval); 
     
            return result;
        }
    }
    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.

    
    

    For example:

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

    Naive Approach

    
    

    This problem is pretty straightforward. We can simply examine every possible pair of numbers in this integer array.

    
    

    Time complexity in worst case: O(n^2).

    
    
    public static int[] twoSum(int[] numbers, int target) {
    	int[] ret = new int[2];
    	for (int i = 0; i < numbers.length; i++) {
    		for (int j = i + 1; j < numbers.length; j++) {
    			if (numbers[i] + numbers[j] == target) {
    				ret[0] = i + 1;
    				ret[1] = j + 1;
    			}
    		}
    	}
    	return ret;
    }
    
    

    Can we do better?

    
    

    Better Solution

    
    

    Use HashMap to store the target value.

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

    3Sum

    Problem:

    Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

    Note:
    Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
    The solution set must not contain duplicate triplets.

        For example, given array S = {-1 0 1 2 -1 -4},
    
        A solution set is:
        (-1, 0, 1)
        (-1, -1, 2)
    

    1. Naive Solution

    Naive solution is 3 loops, and this gives time complexity O(n^3). Apparently this is not an acceptable solution, but a discussion can start from here.

    public class Solution {
        public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
            //sort array
            Arrays.sort(num);
     
            ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
            ArrayList<Integer> each = new ArrayList<Integer>();
            for(int i=0; i<num.length; i++){
                if(num[i] > 0) break;
     
                for(int j=i+1; j<num.length; j++){
                    if(num[i] + num[j] > 0 && num[j] > 0) break;
     
                    for(int k=j+1; k<num.length; k++){
                      if(num[i] + num[j] + num[k] == 0) {
     
                          each.add(num[i]);
                          each.add(num[j]);
                          each.add(num[k]);
                          result.add(each);
                          each.clear();
                      }
                    }
                }
            }
     
            return result;
        }
    }

    * The solution also does not handle duplicates. Therefore, it is not only time inefficient, but also incorrect.

    Result:

    Submission Result: Output Limit Exceeded
    

    2. Better Solution

    A better solution is using two pointers instead of one. This makes time complexity of O(n^2).

    To avoid duplicate, we can take advantage of sorted arrays, i.e., move pointers by >1 to use same element only once.

    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
    	ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
     
    	if (num.length < 3)
    		return result;
     
    	// sort array
    	Arrays.sort(num);
     
    	for (int i = 0; i < num.length - 2; i++) {
    		// //avoid duplicate solutions
    		if (i == 0 || num[i] > num[i - 1]) {
     
    			int negate = -num[i];
     
    			int start = i + 1;
    			int end = num.length - 1;
     
    			while (start < end) {
    				//case 1
    				if (num[start] + num[end] == negate) {
    					ArrayList<Integer> temp = new ArrayList<Integer>();
    					temp.add(num[i]);
    					temp.add(num[start]);
    					temp.add(num[end]);
     
    					result.add(temp);
    					start++;
    					end--;
    					//avoid duplicate solutions
    					while (start < end && num[end] == num[end + 1])
    						end--;
     
    					while (start < end && num[start] == num[start - 1])
    						start++;
    				//case 2
    				} else if (num[start] + num[end] < negate) {
    					start++;
    				//case 3
    				} else {
    					end--;
    				}
    			}
     
    		}
    	}
     
    	return result;
    }
    4Sum

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

    
    

    Note:
    Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
    The solution set must not contain duplicate quadruplets.

    
    
        For example, given array S = {1 0 -1 0 -2 2}, and target = 0.
    
        A solution set is:
        (-1,  0, 0, 1)
        (-2, -1, 1, 2)
        (-2,  0, 0, 2)
    
    
    

    Thoughts

    
    

    A typical k-sum problem. Time is N to the poser of (k-1).

    
    

    Java Solution

    
    
    public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
    	Arrays.sort(num);
     
    	HashSet<ArrayList<Integer>> hashSet = new HashSet<ArrayList<Integer>>();
    	ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
     
    	for (int i = 0; i < num.length; i++) {
    		for (int j = i + 1; j < num.length; j++) {
    			int k = j + 1;
    			int l = num.length - 1;
     
    			while (k < l) {
    				int sum = num[i] + num[j] + num[k] + num[l];
     
    				if (sum > target) {
    					l--;
    				} else if (sum < target) {
    					k++;
    				} else if (sum == target) {
    					ArrayList<Integer> temp = new ArrayList<Integer>();
    					temp.add(num[i]);
    					temp.add(num[j]);
    					temp.add(num[k]);
    					temp.add(num[l]);
     
    					if (!hashSet.contains(temp)) {
    						hashSet.add(temp);
    						result.add(temp);
    					}
     
    					k++;
    					l--;
    				}
    			}
    		}
    	}
     
    	return result;
    }
    
    

    Here is the hashCode method of ArrayList. It makes sure that if all elements of two lists are the same, then the hash code of the two lists will be the same. Since each element in the ArrayList is Integer, same integer has same hash code.

    
    
    int hashCode = 1;
    Iterator<E> i = list.iterator();
    while (i.hasNext()) {
          E obj = i.next();
          hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
    }
    
    

    3Sum Closest

    Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution. For example, given array S = {-1 2 1 -4}, and target = 1. The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

    Thoughts

    This problem is similar with 3 Sum. This kind of problem can be solve by using similar approach, i.e., two pointers from both left and right.

    Java Solution

    public class Solution {
    	public int threeSumClosest(int[] num, int target) {
    		int min = Integer.MAX_VALUE;
    		int result = 0;
     
    		Arrays.sort(num);
     
    		for (int i = 0; i < num.length; i++) {
    			int j = i + 1;
    			int k = num.length - 1;
    			while (j < k) {
    				int sum = num[i] + num[j] + num[k];
    				int diff = Math.abs(sum - target);
    				if (diff < min) {
    					min = diff;
    					result = sum;
    				}
    				if (sum <= target) {
    					j++;
    				} else {
    					k--;
    				}
    			}
    		}
     
    		return result;
    	}
    }

    String to Integer

    Problem:

    Implement atoi to convert a string to an integer.

    Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.

    Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.

    Thoughts for This Problem

    The vague description give us space to consider different cases.

    1. null or empty string
    2. white spaces
    3. +/- sign
    4. calculate real value
    5. handle min & max
    

    Java Solution

    public int atoi(String str) {
    	if (str == null || str.length() < 1)
    		return 0;
     
    	// trim white spaces
    	str = str.trim();
     
    	char flag = '+';
     
    	// check negative or positive
    	int i = 0;
    	if (str.charAt(0) == '-') {
    		flag = '-';
    		i++;
    	} else if (str.charAt(0) == '+') {
    		i++;
    	}
    	// use double to store result
    	double result = 0;
     
    	// calculate value
    	while (str.length() > i && str.charAt(i) >= '0' && str.charAt(i) <= '9') {
    		result = result * 10 + (str.charAt(i) - '0');
    		i++;
    	}
     
    	if (flag == '-')
    		result = -result;
     
    	// handle max and min
    	if (result > Integer.MAX_VALUE)
    		return Integer.MAX_VALUE;
     
    	if (result < Integer.MIN_VALUE)
    		return Integer.MIN_VALUE;
     
    	return (int) result;
    }

    合并排序数组

    Problem:

    
    

    Given two sorted integer arrays A and B, merge B into A as one sorted array.

    
    

    Note:
    You may assume that A has enough space to hold additional elements from B. The number of elements initialized in A and B are m and n respectively.

    
    

    Thoughts for This Problem

    
    

    The key to solve this problem is moving element of A and B backwards. If B has some elements left after A is done, also need to handle that case.

    
    

    The takeaway message from this problem is that the loop condition. This kind of condition is also used for merging two sorted linked list.

    
    

    Java Solution 1

    
    
    public class Solution {
        public void merge(int A[], int m, int B[], int n) {
     
            while(m > 0 && n > 0){
                if(A[m-1] > B[n-1]){
                    A[m+n-1] = A[m-1];
                    m--;
                }else{
                    A[m+n-1] = B[n-1];
                    n--;
                }
            }
     
            while(n > 0){
                A[m+n-1] = B[n-1];
                n--;
            }
        }
    }
    
    

    Java Solution 2

    
    

    The loop condition also can use m+n like the following.

    
    
    public void merge(int A[], int m, int B[], int n) {
    	int i = m - 1;
    	int j = n - 1;
    	int k = m + n - 1;
     
    	while (k >= 0) {
    		if (j < 0 || (i >= 0 && A[i] > B[j]))
    			A[k--] = A[i--];
    		else
    			A[k--] = B[j--];
    	}
    }
    Valid Parentheses

    Problem:

    
    

    Given a string containing just the characters ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[' and ']‘, determine if the input string is valid.

    The brackets must close in the correct order, “()” and “()[]{}” are all valid but “(]” and “([)]” are not.

    
    

    Thoughts about This Problem

    
    

    Character is not a frequently used class, so need to know how to use it.

    
    

    Java Solution

    
    
    public static boolean isValid(String s) {
    	HashMap<Character, Character> map = new HashMap<Character, Character>();
    	map.put('(', ')');
    	map.put('[', ']');
    	map.put('{', '}');
     
    	Stack<Character> stack = new Stack<Character>();
     
    	for (int i = 0; i < s.length(); i++) {
    		char curr = s.charAt(i);
     
    		if (map.keySet().contains(curr)) {
    			stack.push(curr);
    		} else if (map.values().contains(curr)) {
    			if (!stack.empty() && map.get(stack.peek()) == curr) {
    				stack.pop();
    			} else {
    				return false;
    			}
    		}
    	}
     
    	return stack.empty();
    }
    
    

    Simplified Java Solution

    
    

    Almost identical, but convert string to char array at the beginning.

    
    
    public static boolean isValid(String s) {
    	char[] charArray = s.toCharArray();
     
    	HashMap<Character, Character> map = new HashMap<Character, Character>();
    	map.put('(', ')');
    	map.put('[', ']');
    	map.put('{', '}');
     
    	Stack<Character> stack = new Stack<Character>();
     
    	for (Character c : charArray) {
    		if (map.keySet().contains(c)) {
    			stack.push(c);
    		} else if (map.values().contains(c)) {
    			if (!stack.isEmpty() && map.get(stack.peek()) == c) {
    				stack.pop();
    			} else {
    				return false;
    			}
    		}
    	}
    	return stack.isEmpty();
    }
    实现strStr()

    Problem:

    
    

    Implement strStr().

    Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack.

    
    

    Thoughts

    
    

    First, need to understand the problem correctly, the pointer simply means a sub string.
    Second, make sure the loop does not exceed the boundaries of two strings.

    
    

    Java Solution

    
    
    public String strStr(String haystack, String needle) {
     
    	int needleLen = needle.length();
    	int haystackLen = haystack.length();
     
    	if (needleLen == haystackLen && needleLen == 0)
    		return "";
     
    	if (needleLen == 0)
    		return haystack;
     
    	for (int i = 0; i < haystackLen; i++) {
    		// make sure in boundary of needle
    		if (haystackLen - i + 1 < needleLen)
    			return null;
     
    		int k = i;
    		int j = 0;
     
    		while (j < needleLen && k < haystackLen && needle.charAt(j) == haystack.charAt(k)) {
    			j++;
    			k++;
    			if (j == needleLen)
    				return haystack.substring(i);
    		}
     
    	}
     
    	return null;
    }

    Set Matrix Zeroes

    This problem can solve by following 4 steps:

    • check if first row and column are zero or not
    • mark zeros on first row and column
    • use mark to set elements
    • set first column and row by using marks in step 1

    Java Solution

    public class Solution {
        public void setZeroes(int[][] matrix) {
            boolean firstRowZero = false;
            boolean firstColumnZero = false;
     
            //set first row and column zero or not
            for(int i=0; i<matrix.length; i++){
                if(matrix[i][0] == 0){
                    firstColumnZero = true;
                    break;
                }
            }
     
            for(int i=0; i<matrix[0].length; i++){
                if(matrix[0][i] == 0){
                    firstRowZero = true;
                    break;
                }
            }
     
            //mark zeros on first row and column
            for(int i=1; i<matrix.length; i++){
                for(int j=1; j<matrix[0].length; j++){
                    if(matrix[i][j] == 0){
                       matrix[i][0] = 0;
                       matrix[0][j] = 0;
                    }
                }
            }
     
            //use mark to set elements
            for(int i=1; i<matrix.length; i++){
                for(int j=1; j<matrix[0].length; j++){
                    if(matrix[i][0] == 0 || matrix[0][j] == 0){
                       matrix[i][j] = 0;
                    }
                }
            }
     
            //set first column and row
            if(firstColumnZero){
                for(int i=0; i<matrix.length; i++)
                    matrix[i][0] = 0;
            }
     
            if(firstRowZero){
                for(int i=0; i<matrix[0].length; i++)
                    matrix[0][i] = 0;
            }
     
        }
    }
    搜索插入位置

    Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You may assume no duplicates in the array.

    
    

    Here are few examples.

    
    
    [1,3,5,6], 5 -> 2
    [1,3,5,6], 2 -> 1
    [1,3,5,6], 7 -> 4
    [1,3,5,6], 0 -> 0
    
    
    

    Solution 1

    
    

    Naively, we can just iterate the array and compare target with ith and (i+1)th element. Time complexity is O(n)

    
    
    public class Solution {
        public int searchInsert(int[] A, int target) {
     
            if(A==null) return 0;
     
            if(target <= A[0]) return 0;
     
            for(int i=0; i<A.length-1; i++){
                if(target > A[i] && target <= A[i+1]){
                    return i+1;
                }
            }
     
            return A.length;
        }
    }
    
    

    Solution 2

    
    

    This also looks like a binary search problem. We should try to make the complexity to be O(nlogn).

    
    
    public class Solution {
        public int searchInsert(int[] A, int target) {
            if(A==null||A.length==0)
                return 0;
     
            return searchInsert(A,target,0,A.length-1);
        }
     
        public int searchInsert(int[] A, int target, int start, int end){
            int mid=(start+end)/2;
     
            if(target==A[mid]) 
                return mid;
            else if(target<A[mid]) 
                return start<mid?searchInsert(A,target,start,mid-1):start;
            else 
                return end>mid?searchInsert(A,target,mid+1,end):(end+1);
        }
    }
    
    
     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.

    Thoughts

    Because it requires O(n) complexity, we can not solve the problem by sorting the array first. Sorting takes at least O(nlogn) time.

    Java Solution 1

    We can use a HashSet to add and remove elements. HashSet is implemented by using a hash table. Elements are not ordered. The add, remove and contains methods have constant time complexity O(1).

    public int longestConsecutive(int[] num) {
    	Set<Integer> set = new HashSet<Integer>();
    	int max = 1;
     
    	for (int e : num)
    		set.add(e);
     
    	for (int e : num) {
    		int left = e - 1;
    		int right = e + 1;
    		int count = 1;
     
    		while (set.contains(left)) {
    			count++;
    			set.remove(left);
    			left--;
    		}
     
    		while (set.contains(right)) {
    			count++;
    			set.remove(right);
    			right++;
    		}
     
    		max = Math.max(count, max);
    	}
     
    	return max;
    }


    Valid Palindrome

    Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

    For example,
    “A man, a plan, a canal: Panama” is a palindrome.
    “race a car” is not a palindrome.

    Note:
    Have you consider that the string might be empty? This is a good question to ask during an interview.

    For the purpose of this problem, we define empty string as valid palindrome.

    Thoughts

    From start and end loop though the string, i.e., char array. If it is not alpha or number, increase or decrease pointers. Compare the alpha and numeric characters. The solution below is pretty straightforward.

    Java Solution 1

    public class Solution {
     
        public  boolean isPalindrome(String s) {
     
            if(s == null) return false;
            if(s.length() < 2) return true;
     
            char[] charArray = s.toCharArray();
            int len = s.length();
     
            int i=0;
            int j=len-1;
     
            while(i<j){
                char left =  charArray[i];
                char right = charArray[j];
     
                while(i<len-1 && !isAlpha(left) && !isNum(left)){
                    i++;
                    left =  charArray[i];
                }
     
                while(j>0 && !isAlpha(right) && !isNum(right)){
                    j--;
                    right = charArray[j];
                }
     
                if(i >= j)
                	break;
     
                left =  charArray[i];
                right = charArray[j];
     
                if(!isSame(left, right)){
                    return false;
                }
     
                i++;
                j--;
            }
            return true;
        }
     
        public  boolean isAlpha(char a){
            if((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')){
                return true;
            }else{
                return false;
            }
        }
     
        public  boolean isNum(char a){
            if(a >= '0' && a <= '9'){
                return true;
            }else{
                return false;
            }
        }
     
        public  boolean isSame(char a, char b){
            if(isNum(a) && isNum(b)){
                return a == b;
            }else if(Character.toLowerCase(a) == Character.toLowerCase(b)){
                return true;
            }else{
                return false;
            }
        }
    }

    Java Solution 2 – Remove Non-Letter First

    This solution removes the special characters first. (Thanks to Tia)

    public boolean isPalindrome(String s) {
    	s = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
     
    	int len = s.length();
    	if (len < 2)
    		return true;
     
    	Stack<Character> stack = new Stack<Character>();
     
    	int index = 0;
    	while (index < len / 2) {
    		stack.push(s.charAt(index));
    		index++;
    	}
     
    	if (len % 2 == 1)
    		index++;
     
    	while (index < len) {
    		if (stack.empty())
    			return false;
     
    		char temp = stack.pop();
    		if (s.charAt(index) != temp)
    			return false;
    		else
    			index++;
    	}
     
    	return true;
    }
     

    螺旋矩阵

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

    For example, given the following matrix:

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

    You should return [1,2,3,6,9,8,7,4,5].

    Java Solution 1

    If more than one row and column left, it can form a circle and we process the circle. Otherwise, if only one row or column left, we process that column or row ONLY.

    public class Solution {
        public ArrayList<Integer> spiralOrder(int[][] matrix) {
            ArrayList<Integer> result = new ArrayList<Integer>();
     
            if(matrix == null || matrix.length == 0) return result;
     
            int m = matrix.length;
            int n = matrix[0].length;
     
            int x=0; 
            int y=0;
     
            while(m>0 && n>0){
     
                //if one row/column left, no circle can be formed
                if(m==1){
                    for(int i=0; i<n; i++){
                        result.add(matrix[x][y++]);
                    }
                    break;
                }else if(n==1){
                    for(int i=0; i<m; i++){
                        result.add(matrix[x++][y]);
                    }
                    break;
                }
     
                //below, process a circle
     
                //top - move right
                for(int i=0;i<n-1;i++){
                    result.add(matrix[x][y++]);
                }
     
                //right - move down
                for(int i=0;i<m-1;i++){
                    result.add(matrix[x++][y]);
                }
     
                //bottom - move left
                for(int i=0;i<n-1;i++){
                    result.add(matrix[x][y--]);
                }
     
                //left - move up
                for(int i=0;i<m-1;i++){
                    result.add(matrix[x--][y]);
                }
     
                x++;
                y++;
                m=m-2;
                n=n-2;
            }
     
            return result;
        }
    }

    Java Solution 2

    We can also recursively solve this problem. The solution’s performance is not better than Solution or as clear as Solution 1. Therefore, Solution 1 should be preferred.

    public class Solution {
        public ArrayList<Integer> spiralOrder(int[][] matrix) {
            if(matrix==null || matrix.length==0) 
                return new ArrayList<Integer>();
     
            return spiralOrder(matrix,0,0,matrix.length,matrix[0].length);
        }
     
     
        public ArrayList<Integer> spiralOrder(int [][] matrix, int x, int y, int m, int n){
            ArrayList<Integer> result = new ArrayList<Integer>();
     
            if(m<=0||n<=0) 
                return result;
     
            //only one element left
            if(m==1&&n==1) {
                result.add(matrix[x][y]);
                return result;
            }
     
            //top - move right
            for(int i=0;i<n-1;i++){
                result.add(matrix[x][y++]);
            }
     
            //right - move down
            for(int i=0;i<m-1;i++){
                result.add(matrix[x++][y]);
            }
     
            //bottom - move left
            if(m>1){    
                for(int i=0;i<n-1;i++){
                    result.add(matrix[x][y--]);
                }
            }
     
            //left - move up
            if(n>1){
                for(int i=0;i<m-1;i++){
                    result.add(matrix[x--][y]);
                }
            }
     
            if(m==1||n==1)
                result.addAll(spiralOrder(matrix, x, y, 1, 1));
            else    
                result.addAll(spiralOrder(matrix, x+1, y+1, m-2, n-2));
     
            return result;
        }
    }

    搜索一个二维矩阵

    For example, consider the following matrix:

    
    
    [
      [1,   3,  5,  7],
      [10, 11, 16, 20],
      [23, 30, 34, 50]
    ]
    
    
    

    Given target = 3, return true.

    
    

    Java Solution

    
    

    This is a typical problem of binary search.

    
    

    You may try to solve this problem by finding the row first and then the column. There is no need to do that. Because of the matrix’s special features, the matrix can be considered as a sorted array. Your goal is to find one element in this sorted array by using binary search.

    
    
    public class Solution {
        public boolean searchMatrix(int[][] matrix, int target) {
            if(matrix==null || matrix.length==0 || matrix[0].length==0) 
                return false;
     
            int m = matrix.length;
            int n = matrix[0].length;
     
            int start = 0;
            int end = m*n-1;
     
            while(start<=end){
                int mid=(start+end)/2;
                int midX=mid/n;
                int midY=mid%n;
     
                if(matrix[midX][midY]==target) 
                    return true;
     
                if(matrix[midX][midY]<target){
                    start=mid+1;
                }else{
                    end=mid-1;
                }
            }
     
            return false;
        }
    }
    旋转图像

    You are given an n x n 2D matrix representing an image.

    Rotate the image by 90 degrees (clockwise).

    Follow up:
    Could you do this in-place?

    Naive Solution

    In the following solution, a new 2-dimension array is created to store the rotated matrix, and the result is assigned to the matrix at the end. This is WRONG! Why?

    public class Solution {
        public void rotate(int[][] matrix) {
            if(matrix == null || matrix.length==0)
                return ;
     
            int m = matrix.length;
     
            int[][] result = new int[m][m];
     
            for(int i=0; i<m; i++){
                for(int j=0; j<m; j++){
                    result[j][m-1-i] = matrix[i][j];
                }
            } 
     
            matrix = result;
        }
    }

    The problem is that Java is pass by value not by refrence! “matrix” is just a reference to a 2-dimension array. If “matrix” is assigned to a new 2-dimension array in the method, the original array does not change. Therefore, there should be another loop to assign each element to the array referenced by “matrix”. Check out “Java pass by value.”

    public class Solution {
        public void rotate(int[][] matrix) {
            if(matrix == null || matrix.length==0)
                return ;
     
            int m = matrix.length;
     
            int[][] result = new int[m][m];
     
            for(int i=0; i<m; i++){
                for(int j=0; j<m; j++){
                    result[j][m-1-i] = matrix[i][j];
                }
            } 
     
           for(int i=0; i<m; i++){
                for(int j=0; j<m; j++){
                    matrix[i][j] = result[i][j];
                }
            } 
        }
    }

    In-place Solution

    By using the relation “matrix[i][j] = matrix[n-1-j][i]“, we can loop through the matrix.

    public void rotate(int[][] matrix) {
    	int n = matrix.length;
    	for (int i = 0; i < n / 2; i++) {
    		for (int j = 0; j < Math.ceil(((double) n) / 2.); j++) {
    			int temp = matrix[i][j];
    			matrix[i][j] = matrix[n-1-j][i];
    			matrix[n-1-j][i] = matrix[n-1-i][n-1-j];
    			matrix[n-1-i][n-1-j] = matrix[j][n-1-i];
    			matrix[j][n-1-i] = temp;
    		}
    	}
    }
    三角形

    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.

    Top-Down Approach (Wrong Answer!)

    This solution gets wrong answer! I will try to make it work later.

    public class Solution {
        public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
     
           int[] temp = new int[triangle.size()];
    		int minTotal = Integer.MAX_VALUE;
     
    		for(int i=0; i< temp.length; i++){
    			temp[i] = Integer.MAX_VALUE;
    		}
     
    		if (triangle.size() == 1) {
    			return Math.min(minTotal, triangle.get(0).get(0));
    		}
     
    		int first = triangle.get(0).get(0);
     
    		for (int i = 0; i < triangle.size() - 1; i++) {
    			for (int j = 0; j <= i; j++) {
     
    				int a, b;
     
    				if(i==0 && j==0){
    					a = first + triangle.get(i + 1).get(j);
    					b = first + triangle.get(i + 1).get(j + 1);
     
    				}else{
    					a = temp[j] + triangle.get(i + 1).get(j);
    					b = temp[j] + triangle.get(i + 1).get(j + 1);
     
    				}
     
    				temp[j] = Math.min(a, temp[j]);
    				temp[j + 1] = Math.min(b, temp[j + 1]);
    			}
    		}
     
    		for (int e : temp) {
    			if (e < minTotal)
    				minTotal = e;
    		}
     
    		return minTotal;
        }
    }

    Bottom-Up (Good Solution)

    We can actually start from the bottom of the triangle.

    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
    	int[] total = new int[triangle.size()];
    	int l = triangle.size() - 1;
     
    	for (int i = 0; i < triangle.get(l).size(); i++) {
    		total[i] = triangle.get(l).get(i);
    	}
     
    	// iterate from last second row
    	for (int i = triangle.size() - 2; i >= 0; i--) {
    		for (int j = 0; j < triangle.get(i + 1).size() - 1; j++) {
    			total[j] = triangle.get(i).get(j) + Math.min(total[j], total[j + 1]);
    		}
    	}
     
    	return total[0];
    }
    Distinct Subsequences Total

    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.

    Thoughts

    When you see string problem that is about subsequence or matching, dynamic programming method should come to your mind naturally. The key is to find the changing condition.

    Java Solution 1

    Let W(i, j) stand for the number of subsequences of S(0, i) in T(0, j). If S.charAt(i) == T.charAt(j), W(i, j) = W(i-1, j-1) + W(i-1,j); Otherwise, W(i, j) = W(i-1,j).

    public int numDistincts(String S, String T) {
    	int[][] table = new int[S.length() + 1][T.length() + 1];
     
    	for (int i = 0; i < S.length(); i++)
    		table[i][0] = 1;
     
    	for (int i = 1; i <= S.length(); i++) {
    		for (int j = 1; j <= T.length(); j++) {
    			if (S.charAt(i - 1) == T.charAt(j - 1)) {
    				table[i][j] += table[i - 1][j] + table[i - 1][j - 1];
    			} else {
    				table[i][j] += table[i - 1][j];
    			}
    		}
    	}
     
    	return table[S.length()][T.length()];
    }

    Java Solution 2

    Do NOT write something like this, even it can also pass the online judge.

    public int numDistinct(String S, String T) {
    	HashMap<Character, ArrayList<Integer>> map = new HashMap<Character, ArrayList<Integer>>();
     
    	for (int i = 0; i < T.length(); i++) {
    		if (map.containsKey(T.charAt(i))) {
    			map.get(T.charAt(i)).add(i);
    		} else {
    			ArrayList<Integer> temp = new ArrayList<Integer>();
    			temp.add(i);
    			map.put(T.charAt(i), temp);
    		}
    	}
     
    	int[] result = new int[T.length() + 1];
    	result[0] = 1;
     
    	for (int i = 0; i < S.length(); i++) {
    		char c = S.charAt(i);
     
    		if (map.containsKey(c)) {
    			ArrayList<Integer> temp = map.get(c);
    			int[] old = new int[temp.size()];
     
    			for (int j = 0; j < temp.size(); j++)
    				old[j] = result[temp.get(j)];
     
    			// the relation
    			for (int j = 0; j < temp.size(); j++)
    				result[temp.get(j) + 1] = result[temp.get(j) + 1] + old[j];
    		}
    	}
     
    	return result[T.length()];
    }
     

    Maximum Subarray

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

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

    Naive Solution

    A wrong solution. Simply iterate the array like the following will not work.

    public class Solution {
    	public int maxSubArray(int[] A) {
    		int sum = 0;
    		int maxSum = Integer.MIN_VALUE;
     
    		for (int i = 0; i < A.length; i++) {
    			sum += A[i];
    			maxSum = Math.max(maxSum, sum);
     
    			if (sum < 0)
    				sum = 0;
    		}
     
    		return maxSum;
    	}
    }

    Dynamic Programming Solution

    We should ignore the sum of the previous n-1 elements if nth element is greater than the sum.

    public class Solution {
    	public int maxSubArray(int[] A) {
    		int max = A[0];
    		int[] sum = new int[A.length];
    		sum[0] = A[0];
     
    		for (int i = 1; i < A.length; i++) {
    			sum[i] = Math.max(A[i], sum[i - 1] + A[i]);
    			max = Math.max(max, sum[i]);
    		}
     
    		return max;
    	}
    }

    删除重复的排序数组

    Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

    
    

    Do not allocate extra space for another array, you must do this in place with constant memory.

    
    

    For example,
    Given input array A = [1,1,2],

    
    

    Your function should return length = 2, and A is now [1,2].

    
    

    Thoughts

    
    

    The problem is pretty straightforward. It returns the length of array with unique elements, but the original array need to be changed also. This problem should be reviewed with Remove Duplicates from Sorted Array II.

    
    

    Java Solution

    
    
    public class Solution {
        public int removeDuplicates(int[] A) {
            if(A.length < 2)
                return A.length;
     
            int j = 0;
            int i = 1;
     
            while(i < A.length){
                if(A[i] == A[j]){
                    i++;
                }else{
                    A[++j] = A[i++];
                }    
            }
     
            return j+1;
        }
    }


    删除重复的排序数组2

    Follow up for “Remove Duplicates”:
    What if duplicates are allowed at most twice?

    For example,
    Given sorted array A = [1,1,1,2,2,3],

    Your function should return length = 5, and A is now [1,1,2,2,3].

    Naive Approach

    Given the method signature “public int removeDuplicates(int[] A)”, it seems that we should write a method that returns a integer and that’s it. After typing the following solution:

    public class Solution {
        public int removeDuplicates(int[] A) {
            if(A == null || A.length == 0)
                return 0;
     
            int pre = A[0];
            boolean flag = false;
            int count = 0;
     
            for(int i=1; i<A.length; i++){
                int curr = A[i];
     
                if(curr == pre){
                    if(!flag){
                    	flag = true;
                        continue;
                    }else{
                        count++;
                    }
                }else{
                    pre = curr;
                    flag = false;
                }
            }
     
            return A.length - count;
        }
    }

    Online Judge returns:

    Submission Result: Wrong Answer
    Input:	[1,1,1,2]
    Output:	[1,1,1]
    Expected:	[1,1,2]
    

    So this problem also requires in-place array manipulation.

    Correct Solution

    We can not change the given array’s size, so we only change the first k elements of the array which has duplicates removed.

    public class Solution {
    	public int removeDuplicates(int[] A) {
    		if (A == null || A.length == 0)
    			return 0;
     
    		int pre = A[0];
    		boolean flag = false;
    		int count = 0;
     
    		// index for updating
    		int o = 1;
     
    		for (int i = 1; i < A.length; i++) {
    			int curr = A[i];
     
    			if (curr == pre) {
    				if (!flag) {
    					flag = true;
    					A[o++] = curr;
     
    					continue;
    				} else {
    					count++;
    				}
    			} else {
    				pre = curr;
    				A[o++] = curr;
    				flag = false;
    			}
    		}
     
    		return A.length - count;
    	}
    }

    Better Solution

    public class Solution {
    	public int removeDuplicates(int[] A) {
    		if (A.length <= 2)
    			return A.length;
     
    		int prev = 1; // point to previous
    		int curr = 2; // point to current
     
    		while (curr < A.length) {
    			if (A[curr] == A[prev] && A[curr] == A[prev - 1]) {
    				curr++;
    			} else {
    				prev++;
    				A[prev] = A[curr];
    				curr++;
    			}
    		}
     
    		return prev + 1;
    	}
    }
    查找没有重复的最长子串

    Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for “abcabcbb” is “abc”, which the length is 3. For “bbbbb” the longest substring is “b”, with the length of 1.

    
    

    Java Solution

    
    

    Use a flag array like CC 150 “determine if a string has all unique characters”.

    
    
    public int lengthOfLongestSubstring(String s) {
    	boolean[] flag = new boolean[256];
     
    	int result = 0;
    	int j = 0;
    	char[] arr = s.toCharArray();
     
    	for (int i = 0; i < arr.length; i++) {
    		char c = arr[i];
    		if (flag[c]) {
    			result = Math.max(result, i - j);
    			for (int k = j; k < i; k++) {
    				if (arr[k] == c) {
    					j = k + 1;
    					break;
    				}
    				flag[arr[k]] = false;
    			}	
    		} else {
    			flag[c] = true;
    		}
    	}
     
    	result=Math.max(arr.length-j,result);
     
    	return result;
    }

    包含两个独特字符的最长子串

    Given a string, find the longest substring that contains only two unique characters. For example, given “abcbbbbcccbdddadacb”, the longest substring that contains 2 unique character is “bcbbbbcccb”.

    
    

    Solution

    
    

    Here is a naive solution. It works. Basically, it has two pointers that track the start of the substring and the iteration cursor.

    
    
    public static String subString(String s) {
    	// checking
     
    	char[] arr = s.toCharArray();
    	int max = 0;
    	int j = 0;
    	int m = 0, n = 0;
     
    	HashSet<Character> set = new HashSet<Character>();
    	set.add(arr[0]);
     
    	for (int i = 1; i < arr.length; i++) {
    		if (set.add(arr[i])) {
    			if (set.size() > 2) {
    				String str = s.substring(j, i);
     
    				//keep the last character only
    				set.clear();
    				set.add(arr[i - 1]);
     
    				if ((i - j) > max) {
    					m = j;
    					n = i - 1;
    					max = i - j;
    				}
     
    				j = i - helper(str);
    			}
    		}
    	}
     
    	return s.substring(m, n + 1);
    }
     
    // This method returns the length that contains only one character from right side. 
    public static int helper(String str) {
    	// null & illegal checking here
    	if(str == null){
    		return 0;
    	}
     
    	if(str.length() == 1){
    		return 1;
    	}
     
    	char[] arr = str.toCharArray();
    	char p = arr[arr.length - 1];
    	int result = 1;
     
    	for (int i = arr.length - 2; i >= 0; i--) {
    		if (p == arr[i]) {
    			result++;
    		} else {
    			break;
    		}
    	}
     
    	return result;
    }


    Palindrome Partitioning

    Problem

    Given a string s, partition s such that every substring of the partition is a palindrome.

    Return all possible palindrome partitioning of s.

    For example, given s = “aab”,
    Return

      [
        ["aa","b"],
        ["a","a","b"]
      ]
    

    Java Solution

    public ArrayList<ArrayList<String>> partition(String s) {
    	ArrayList<ArrayList<String>> result = new ArrayList<ArrayList<String>>();
     
    	if (s == null || s.length() == 0) {
    		return result;
    	}
     
    	ArrayList<String> partition = new ArrayList<String>();
    	addPalindrome(s, 0, partition, result);
     
    	return result;
    }
     
    private void addPalindrome(String s, int start, ArrayList<String> partition,
    		ArrayList<ArrayList<String>> result) {
    	//stop condition
    	if (start == s.length()) {
    		ArrayList<String> temp = new ArrayList<String>(partition);
    		result.add(temp);
    		return;
    	}
     
    	for (int i = start + 1; i <= s.length(); i++) {
    		String str = s.substring(start, i);
    		if (isPalindrome(str)) {
    			partition.add(str);
    			addPalindrome(s, i, partition, result);
    			partition.remove(partition.size() - 1);
    		}
    	}
    }
     
    private boolean isPalindrome(String str) {
    	int left = 0;
    	int right = str.length() - 1;
     
    	while (left < right) {
    		if (str.charAt(left) != str.charAt(right)) {
    			return false;
    		}
     
    		left++;
    		right--;
    	}
     
    	return true;
    }
     
    
    

    链表

    在Java中实现链表是非常简单的,每个节点都有一个值,然后把它链接到下一个节点。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Node {
        int val;
        Node next;
      
        Node(int x) {
            val = x;
            next = null;
        }
    }
     

    比较流行的两个链表例子就是栈和队列。

    栈(Stack)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class Stack{
        Node top;
      
        public Node peek(){
            if(top != null){
                return top;
            }
      
            return null;
        }
      
        public Node pop(){
            if(top == null){
                return null;
            }else{
                Node temp = new Node(top.val);
                top = top.next;
                return temp;   
            }
        }
      
        public void push(Node n){
            if(n != null){
                n.next = top;
                top = n;
            }
        }
    }
    队列(Queue)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Queue{
        Node first, last;
    &nbsp;
        public void enqueue(Node n){
            if(first == null){
                first = n;
                last = first;
            }else{
                last.next = n;
                last = n;
            }
        }
    &nbsp;
        public Node dequeue(){
            if(first == null){
                return null;
            }else{
                Node temp = new Node(first.val);
                first = first.next;
                return temp;
            }  
        }
    }
    
    

    值得一提的是,Java标准库中已经包含一个叫做Stack的类,链表也可以作为一个队列使用(add()和remove())。(链表实现队列接口)如果你在面试过程中,需要用到栈或队列解决问题时,你可以直接使用它们。

    在实际中,需要用到链表的算法有:
    插入两个数字

    The problem:

    
    

    You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

    Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
    Output: 7 -> 0 -> 8

    
    

    Thoughts

    
    

    This is a simple problem. It can be solved by doing the following:

    
    
    1. Use a flag to mark if previous sum is >= 10
    2. Handle the situation that one list is longer than the other
    3. Correctly move the 3 pointers p1, p2 and p3 which pointer to two input lists and one output list
    
    

    This leads to solution 1.

    
    

    Solution 1

    
    
    // Definition for singly-linked list.
    public class ListNode {
        int val;
        ListNode next;
        ListNode(int x) {
            val = x;
            next = null;
        }
    }
     
    public class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
     
            ListNode p1 = l1;
            ListNode p2 = l2;
     
            ListNode newHead = new ListNode(0);
            ListNode p3 = newHead;
     
            int val;//store sum
     
            boolean flag = false;//flag if greater than 10
     
            while(p1 != null || p2 != null){
                //both p1 and p2 have value
                if(p1 != null && p2 != null){
     
                    if(flag){
                        val = p1.val + p2.val + 1;
                    }else{
                        val = p1.val + p2.val;
                    }
     
                    //if sum >= 10
                    if(val >= 10 ){
                        flag = true;
     
                    //if sum < 10
                    }else{
                        flag = false;
                    }
     
                    p3.next = new ListNode(val%10);
                    p1 = p1.next;
                    p2 = p2.next;
                //p1 is null, because p2 is longer                
                }else if(p2 != null){
     
                    if(flag){
                        val = p2.val + 1;
                        if(val >= 10){
                            flag = true;
                        }else{
                            flag = false;
                        }
                    }else{
                        val = p2.val;
                        flag = false;
                    }
     
                    p3.next = new ListNode(val%10); 
                    p2 = p2.next;
     
                ////p2 is null, because p1 is longer  
                }else if(p1 != null){
     
                    if(flag){
                        val = p1.val + 1;
                        if(val >= 10){
                            flag = true;
                        }else{
                            flag = false;
                        }
                    }else{
                        val = p1.val;
                        flag = false;
                    }
     
                    p3.next = new ListNode(val%10); 
                    p1 = p1.next;
                }
     
                p3 = p3.next;
            }
     
            //handle situation that same length final sum >=10
            if(p1 == null && p2 == null && flag){
                p3.next = new ListNode(1);
            }
     
            return newHead.next;
        }
    }
    
    

    The hard part is how to make the code more readable. Adding some internal comments and refactoring some code are helpful.

    
    

    Solution 2

    
    

    There is nothing wrong with solution 1, but the code is not readable. We can refactor the code and make it much shorter and cleaner.

    
    
    public class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
           int carry =0;
     
            ListNode newHead = new ListNode(0);
            ListNode p1 = l1, p2 = l2, p3=newHead;
     
            while(p1 != null || p2 != null){
                if(p1 != null){
                    carry += p1.val;
                    p1 = p1.next;
                }
     
                if(p2 != null){
                    carry += p2.val;
                    p2 = p2.next;
                }
     
                p3.next = new ListNode(carry%10);
                p3 = p3.next;
                carry /= 10;
            }
     
            if(carry==1) 
                p3.next=new ListNode(1);
     
            return newHead.next;
        }
    }
    
    

    Exactly the same thing!

    
    

    Quesion

    
    

    What is the digits are stored in regular order instead of reversed order?

    
    

    Answer: We can simple reverse the list, calculate the result, and reverse the result.

    重新排序列表

    The problem:

    
    

    Given a singly linked list L: L0→L1→ … →Ln-1→Ln,
    reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

    
    

    For example, given {1,2,3,4}, reorder it to {1,4,2,3}. You must do this in-place without altering the nodes’ values.

    
    

    Thoughts

    
    

    This problem is not straightforward, because it requires “in-place” operations. That means we can only change their pointers, not creating a new list.

    
    

    1. Solution

    
    

    This problem can be solved by doing the following:

    
    
    1. Break list in the middle to two lists (use fast & slow pointers)
    2. Reverse the order of the second list
    3. Merge two list back together
    
    

    The following code is a complete runnable class with testing.

    
    
    //Class definition of ListNode
    class ListNode {
    	int val;
    	ListNode next;
     
    	ListNode(int x) {
    		val = x;
    		next = null;
    	}
    }
     
    public class ReorderList {
     
    	public static void main(String[] args) {
    		ListNode n1 = new ListNode(1);
    		ListNode n2 = new ListNode(2);
    		ListNode n3 = new ListNode(3);
    		ListNode n4 = new ListNode(4);
    		n1.next = n2;
    		n2.next = n3;
    		n3.next = n4;
     
    		printList(n1);
     
    		reorderList(n1);
     
    		printList(n1);
    	}
     
    	public static void reorderList(ListNode head) {
     
    		if (head != null && head.next != null) {
     
    			ListNode slow = head;
    			ListNode fast = head;
     
    			//use a fast and slow pointer to break the link to two parts.
    			while (fast != null && fast.next != null && fast.next.next!= null) {
    				//why need third/second condition?
    				System.out.println("pre "+slow.val + " " + fast.val);
    				slow = slow.next;
    				fast = fast.next.next;
    				System.out.println("after " + slow.val + " " + fast.val);
    			}
     
    			ListNode second = slow.next;
    			slow.next = null;// need to close first part
     
    			// now should have two lists: head and fast
     
    			// reverse order for second part
    			second = reverseOrder(second);
     
    			ListNode p1 = head;
    			ListNode p2 = second;
     
    			//merge two lists here
    			while (p2 != null) {
    				ListNode temp1 = p1.next;
    				ListNode temp2 = p2.next;
     
    				p1.next = p2;
    				p2.next = temp1;		
     
    				p1 = temp1;
    				p2 = temp2;
    			}
    		}
    	}
     
    	public static ListNode reverseOrder(ListNode head) {
     
    		if (head == null || head.next == null) {
    			return head;
    		}
     
    		ListNode pre = head;
    		ListNode curr = head.next;
     
    		while (curr != null) {
    			ListNode temp = curr.next;
    			curr.next = pre;
    			pre = curr;
    			curr = temp;
    		}
     
    		// set head node's next
    		head.next = null;
     
    		return pre;
    	}
     
    	public static void printList(ListNode n) {
    		System.out.println("------");
    		while (n != null) {
    			System.out.print(n.val);
    			n = n.next;
    		}
    		System.out.println();
    	}
    }
    
    

    2. Takeaway Messages from This Problem

    
    

    The three steps can be used to solve other problems of linked list. A little diagram may help better understand them.

    
    

    Reverse List:

    
    

    reverse-list

    
    

    Merge List:

    
    

    merge-list-in-place

     
     链表周期
     

    Leetcode Problem: Linked List Cycle

    Given a linked list, determine if it has a cycle in it.

    1. Naive Approach

     class ListNode {
         int val;
         ListNode next;
         ListNode(int x) {
             val = x;
             next = null;
         }
     }
     
    public class Solution {
        public boolean hasCycle(ListNode head) {
            ListNode p = head;
     
            if(head == null)
                return false;
     
            if(p.next == null)
                return false;
     
            while(p.next != null){
                if(head == p.next){
                    return true;
                }
                p = p.next;
            }
     
            return false;
        }
    }

    Result:

    Submission Result: Time Limit Exceeded

    Last executed input: {3,2,0,-4}, tail connects to node index 1

    The problem can be demonstrated in the following diagram:
    linked-list-cycle

    2. Accepted Approach

    Use fast and low pointer. The advantage about fast/slow pointers is that when a circle is located, the fast one will catch the slow one for sure.

    public class Solution {
        public boolean hasCycle(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
     
            if(head == null)
                return false;
     
            if(head.next == null)
                return false;
     
            while(fast != null && fast.next != null){
                slow = slow.next;
                fast = fast.next.next;
     
                if(slow == fast)
                    return true;
            }
     
            return false;
        }
    }
     

    Problem:

    A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

    Return a deep copy of the list.

    1. Some Thoughts

    We can solve this problem by doing the following steps:

    1. copy every node, i.e., duplicate every node, and insert it to the list
    2. copy random pointers for all newly created nodes
    3. break the list to two

    2. First Attempt (Wrong)

    What is wrong with the following code?

    /**
     * Definition for singly-linked list with a random pointer.
     * class RandomListNode {
     *     int label;
     *     RandomListNode next, random;
     *     RandomListNode(int x) { this.label = x; }
     * };
     */
    public class Solution {
        public RandomListNode copyRandomList(RandomListNode head) {
     
            if(head == null)
                return null;
     
            RandomListNode p = head;
     
            //copy every node and insert to list
            while(p != null){
                RandomListNode copy = new RandomListNode(p.label);
                copy.next = p.next;
                p.next = copy;
                p = copy.next;
            }
     
            //copy random pointer for each new node
            p = head;
            while(p != null){
                p.next.random = p.random.next;//p.random can be null, so need null checking here!    
                p = p.next.next;
            }
     
            //break list to two 
            p = head;
            while(p != null){
                p.next = p.next.next;
                p = p.next;//point to the wrong node now!
            }
     
            return head.next;
        }
    }

    The code above seems totally fine. It follows the steps designed. But it has run-time errors. Why?

    The problem is in the parts of copying random pointer and breaking list.

    3. Correct Solution

    public RandomListNode copyRandomList(RandomListNode head) {
     
    	if (head == null)
    		return null;
     
    	RandomListNode p = head;
     
    	// copy every node and insert to list
    	while (p != null) {
    		RandomListNode copy = new RandomListNode(p.label);
    		copy.next = p.next;
    		p.next = copy;
    		p = copy.next;
    	}
     
    	// copy random pointer for each new node
    	p = head;
    	while (p != null) {
    		if (p.random != null)
    			p.next.random = p.random.next;
    		p = p.next.next;
    	}
     
    	// break list to two
    	p = head;
    	RandomListNode newHead = head.next;
    	while (p != null) {
    		RandomListNode temp = p.next;
    		p.next = temp.next;
    		if (temp.next != null)
    			temp.next = temp.next.next;
    		p = p.next;
    	}
     
    	return newHead;
    }

    The break list part above move pointer 2 steps each time, you can also move one at a time which is simpler, like the following:

    while(p != null && p.next != null){
        RandomListNode temp = p.next;
        p.next = temp.next;
        p = temp;
    }

    4. Correct Solution Using HashMap

    From Xiaomeng’s comment below, we can use a HashMap which makes it simpler.

    public RandomListNode copyRandomList(RandomListNode head) {
    	if (head == null)
    		return null;
    	HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
    	RandomListNode newHead = new RandomListNode(head.label);
     
    	RandomListNode p = head;
    	RandomListNode q = newHead;
    	map.put(head, newHead);
     
    	p = p.next;
    	while (p != null) {
    		RandomListNode temp = new RandomListNode(p.label);
    		map.put(p, temp);
    		q.next = temp;
    		q = temp;
    		p = p.next;
    	}
     
    	p = head;
    	q = newHead;
    	while (p != null) {
    		if (p.random != null)
    			q.random = map.get(p.random);
    		else
    			q.random = null;
     
    		p = p.next;
    		q = q.next;
    	}
     
    	return newHead;
    }

     合并两个有序列表

    Problem:

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

    Key to solve this problem

    The key to solve the problem is defining a fake head. Then compare the first elements from each list. Add the smaller one to the merged list. Finally, when one of them is empty, simply append it to the merged list, since it is already sorted.

    Java Solution

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
     
            ListNode p1 = l1;
            ListNode p2 = l2;
     
            ListNode fakeHead = new ListNode(0);
            ListNode p = fakeHead;
     
            while(p1 != null && p2 != null){
              if(p1.val <= p2.val){
                  p.next = p1;
                  p1 = p1.next;
              }else{
                  p.next = p2;
                  p2 = p2.next;
              }
     
              p = p.next;
            }
     
            if(p1 != null)
                p.next = p1;
            if(p2 != null)
                p.next = p2;
     
            return fakeHead.next;
        }
    }

    合并多个排序列表

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

    
    

    Thoughts

    
    

    The simplest solution is using PriorityQueue. The elements of the priority queue are ordered according to their natural ordering, or by a Comparator provided at queue construction time (in this case).

    
    

    Java Solution

    
    
    import java.util.ArrayList;
    import java.util.Comparator;
    import java.util.PriorityQueue;
     
    //  Definition for singly-linked list.
    class ListNode {
    	int val;
    	ListNode next;
     
    	ListNode(int x) {
    		val = x;
    		next = null;
    	}
    }
     
    public class Solution {
    	public ListNode mergeKLists(ArrayList<ListNode> lists) {
    		if (lists.size() == 0)
    			return null;
     
    		//PriorityQueue is a sorted queue
    		PriorityQueue<ListNode> q = new PriorityQueue<ListNode>(lists.size(),
    				new Comparator<ListNode>() {
    					public int compare(ListNode a, ListNode b) {
    						if (a.val > b.val)
    							return 1;
    						else if(a.val == b.val)
    							return 0;
    						else 
    							return -1;
    					}
    				});
     
    		//add first node of each list to the queue
    		for (ListNode list : lists) {
    			if (list != null)
    				q.add(list);
    		}
     
    		ListNode head = new ListNode(0);
    		ListNode prev = head;
     
    		while (q.size() > 0) {
    			ListNode temp = q.poll();
    			prev.next = temp;
     
    			//keep adding next element of each list
    			if (temp.next != null)
    				q.add(temp.next);
     
    			prev = prev.next;
    		}
     
    		return head.next;
    	}
    }
    从排序列表中删除重复的

    Given a sorted linked list, delete all duplicates such that each element appear only once.

    
    

    For example,

    
    
    Given 1->1->2, return 1->2.
    Given 1->1->2->3->3, return 1->2->3.
    
    
    

    Thoughts

    
    

    The key of this problem is using the right loop condition. And change what is necessary in each loop. You can use different iteration conditions like the following 2 solutions.

    
    

    Solution 1

    
    
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            if(head == null || head.next == null)
                return head;
     
            ListNode prev = head;    
            ListNode p = head.next;
     
            while(p != null){
                if(p.val == prev.val){
                    prev.next = p.next;
                    p = p.next;
                    //no change prev
                }else{
                    prev = p;
                    p = p.next; 
                }
            }
     
            return head;
        }
    }
    
    

    Solution 2

    
    
    public class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            if(head == null || head.next == null)
                return head;
     
            ListNode p = head;
     
            while( p!= null && p.next != null){
                if(p.val == p.next.val){
                    p.next = p.next.next;
                }else{
                    p = p.next; 
                }
            }
     
            return head;
        }
    }
    分区列表

    Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

    
    

    You should preserve the original relative order of the nodes in each of the two partitions.

    
    

    For example,
    Given 1->4->3->2->5->2 and x = 3,
    return 1->2->2->4->3->5.

    
    

    Naive Solution (Wrong)

    
    

    The following is a solution I write at the beginning. It contains a trivial problem, but it took me a long time to fix it.

    
    
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
        public ListNode partition(ListNode head, int x) {
            if(head == null) return null;
     
            ListNode fakeHead1 = new ListNode(0);
            ListNode fakeHead2 = new ListNode(0);
            fakeHead1.next = head;
     
            ListNode p = head;
            ListNode prev = fakeHead1;
            ListNode p2 = fakeHead2;
     
            while(p != null){
                if(p.val < 3){
                    p = p.next;
                    prev = prev.next;
                }else{
                    prev.next = p.next;
                    p2.next = p;
                    p = prev.next;
                    p2 = p2.next;
                }
            }
     
            p.next = fakeHead2.next;
            return fakeHead1.next;
        }
    }
    
    

    Correct Solution

    
    

    The problem of the first solution is that the last node’s next element should be set to null.

    
    
    public class Solution {
        public ListNode partition(ListNode head, int x) {
            if(head == null) return null;
     
            ListNode fakeHead1 = new ListNode(0);
            ListNode fakeHead2 = new ListNode(0);
            fakeHead1.next = head;
     
            ListNode p = head;
            ListNode prev = fakeHead1;
            ListNode p2 = fakeHead2;
     
            while(p != null){
                if(p.val < x){
                    p = p.next;
                    prev = prev.next;
                }else{
     
                    p2.next = p;
                    prev.next = p.next;
     
                    p = prev.next;
                    p2 = p2.next;
                } 
            }
     
            // close the list
            p2.next = null;
     
            prev.next = fakeHead2.next;
     
            return fakeHead1.next;
        }
    }
    
    
    
    
    

    LRU缓存

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

    
    

    get(key) – Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
    set(key, value) – Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

    
    

    Java Solution

    
    

    The key to solve this problem is using a double linked list which enables us to quickly move nodes.

    
    

    LRU-Cache

    
    
    import java.util.HashMap;
     
    public class LRUCache {
    	private HashMap<Integer, DoubleLinkedListNode> map 
    		= new HashMap<Integer, DoubleLinkedListNode>();
    	private DoubleLinkedListNode head;
    	private DoubleLinkedListNode end;
    	private int capacity;
    	private int len;
     
    	public LRUCache(int capacity) {
    		this.capacity = capacity;
    		len = 0;
    	}
     
    	public int get(int key) {
    		if (map.containsKey(key)) {
    			DoubleLinkedListNode latest = map.get(key);
    			removeNode(latest);
    			setHead(latest);
    			return latest.val;
    		} else {
    			return -1;
    		}
    	}
     
    	public void removeNode(DoubleLinkedListNode node) {
    		DoubleLinkedListNode cur = node;
    		DoubleLinkedListNode pre = cur.pre;
    		DoubleLinkedListNode post = cur.next;
     
    		if (pre != null) {
    			pre.next = post;
    		} else {
    			head = post;
    		}
     
    		if (post != null) {
    			post.pre = pre;
    		} else {
    			end = pre;
    		}
    	}
     
    	public void setHead(DoubleLinkedListNode node) {
    		node.next = head;
    		node.pre = null;
    		if (head != null) {
    			head.pre = node;
    		}
     
    		head = node;
    		if (end == null) {
    			end = node;
    		}
    	}
     
    	public void set(int key, int value) {
    		if (map.containsKey(key)) {
    			DoubleLinkedListNode oldNode = map.get(key);
    			oldNode.val = value;
    			removeNode(oldNode);
    			setHead(oldNode);
    		} else {
    			DoubleLinkedListNode newNode = 
    				new DoubleLinkedListNode(key, value);
    			if (len < capacity) {
    				setHead(newNode);
    				map.put(key, newNode);
    				len++;
    			} else {
    				map.remove(end.key);
    				end = end.pre;
    				if (end != null) {
    					end.next = null;
    				}
     
    				setHead(newNode);
    				map.put(key, newNode);
    			}
    		}
    	}
    }
     
    class DoubleLinkedListNode {
    	public int val;
    	public int key;
    	public DoubleLinkedListNode pre;
    	public DoubleLinkedListNode next;
     
    	public DoubleLinkedListNode(int key, int value) {
    		val = value;
    		this.key = key;
    	}
    }
     
  • 相关阅读:
    Linux中查找当前目录下占用空间最大的前10个文件
    Redis的优势和特点
    java中final,finally,finalize三个关键字的区别
    消息队列介绍
    Redis的应用场景
    Spring中@Autowired注解与@Resource注解的区别
    多版本并发控制(MVCC)
    Linux查看CPU和内存使用情况
    进程调度算法
    一致性Hash算法
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/3682188.html
Copyright © 2020-2023  润新知