Labels

Showing posts with label Math. Show all posts
Showing posts with label Math. Show all posts

Sunday, March 22, 2015

Sqrt(x)

Implement int sqrt(int x).
Compute and return the square root of x.

Naive Way: Return the square root of an integer as an integer. I use binary search. First I need to find maximum possible square root, which is (int)Math.sqrt(Integer.MAX_VALUE). And when it happens sqrt(x) is this value, can't use i^2 <= x <(i+1)^2 to end the loop since (i+1)^2 will overflow. List this case separately.

 public class Solution {  
   static final int MAX_SQRT = (int)Math.sqrt(Integer.MAX_VALUE);  
   public int sqrt(int x) {  
     int high = MAX_SQRT, low = 0;  
     while(low <= high){  
       int middle = (high+low)/2;  
       if(middle*middle <= x && middle == MAX_SQRT) return middle;  
       if(middle*middle <= x && (middle+1)*(middle+1) > x) return middle;  
       if(middle*middle > x)  
         high = middle-1;  
       else  
         low = middle+1;  
     }  
     return low;  
   }  
 }  

Improved Way: There is a mathematical method Newton's Method. I can't understand what is stated in the link. There are too mathematical. I understand from other one's algorithm newtons-iterative-method-in-c . In the answer of that question. Newton's method was explained.


Saturday, March 21, 2015

Spiral Matrix II

Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.
For example,
Given n = 3,
You should return the following matrix:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
] 
 
Naive Way: Use what I write in Spiral Matrix. Key point is current row/column 's fill in length get minus by one every two steps.
I comment what each parameter represents. Easy to understand and good looking.

 public class Solution {  
   public int[][] generateMatrix(int n) {  
     int[][] m = new int[n][n];  
     int len = n; // current row/column 's fill in length  
     int index = 0; // direction controller  
     int number = 1; // increasing numbers  
     int x = 0, y = -1; // current position  
     // fill in the matrix  
     while(len > 0){  
       if(index%2==1) len--;  
       switch(index++%4){  
         case 0:  
           for(int i = 0;i < len;i++) m[x][++y] = number++;  
           break;  
         case 1:  
           for(int i = 0;i < len;i++) m[++x][y] = number++;  
           break;  
         case 2:  
           for(int i = 0;i < len;i++) m[x][--y] = number++;  
           break;  
         case 3:  
           for(int i = 0;i < len;i++) m[--x][y] = number++;  
           break;  
         default:  
         break;  
       }  
     }  
     return m;  
   }  
 }  


Insert Interval

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).
You may assume that the intervals were initially sorted according to their start times.
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].


Naive Way: This question contains no fancy algorithm as far as I know. Just find where the new interval should be merged into. I try to implement it naively in one pass. But there are so many edge cases I never thought about. I list some of them.
  • Empty set of intervals [].
  • New interval doesn't have overlap with any of the intervals in the set.
  • New interval is before first interval of the set.
  • New interval is after last interval of the set.
After including all these edge cases. My code is a little tedious. Use a new List to hold result, which requires O(n) space.

 /**  
  * 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 List<Interval> insert(List<Interval> intervals, Interval newInterval) {  
     List<Interval> rslt = new ArrayList<Interval>();  
     // edge case  
     if(intervals.size()==0) rslt.add(newInterval);  
     // merge intervals  
     int begin = -1, end = -1;  
     for(int i = 0;i < intervals.size();i++){  
       // if no overlapping with newInterval at all  
       if(intervals.get(i).start > newInterval.end && i==0)  
         rslt.add(newInterval);  
       // if current interval overlap with newInterval, merge them  
       if(overlap(intervals.get(i), newInterval)){  
         if(begin==-1) begin = Math.min(intervals.get(i).start, newInterval.start);  
         end = Math.max(intervals.get(i).end, newInterval.end);  
         // when last interval overlaps  
         if(i==intervals.size()-1){  
           Interval mergedInterval = new Interval(begin, end);  
           rslt.add(mergedInterval);  
         }  
       }else{  
         if(begin!=-1){  
           // if overlap ends, add newly merged interval  
           Interval mergedInterval = new Interval(begin, end);  
           rslt.add(mergedInterval);  
           begin = -1;  
         }  
         rslt.add(intervals.get(i));  
       }  
       // if no overlapping with newInterval at all  
       if(intervals.get(i).end < newInterval.start && (i!=intervals.size()-1 && intervals.get(i+1).start > newInterval.end || i==intervals.size()-1))  
         rslt.add(newInterval);  
     }  
     return rslt;  
   }  
   private boolean overlap(Interval a, Interval b){  
     if(a.start <= b.start)  
       return a.end >= b.start;  
     else  
       return b.end >= a.start;  
   }  
 }  

Improved Way: There are much better and concise Solutions in Discuss Board. I steal one from inplace-and-concise-lines-java-solution-for-your-reference.
Its idea is to find the correct position for new interval, merge if necessary.

 public class Solution {  
   public List<Interval> insert(List<Interval> intervals, Interval newInterval) {  
     Iterator<Interval> iter = intervals.iterator();  
     int index = 0;  
     while(iter.hasNext()){  
       Interval cur = iter.next();  
       if(cur.end < newInterval.start){ index++; continue;}  
       if(cur.start > newInterval.end) break;  
       newInterval.start = Math.min(newInterval.start, cur.start);  
       newInterval.end = Math.max(newInterval.end, cur.end);  
       iter.remove();  
     }  
     intervals.add(index, newInterval);  
     return intervals;  
   }  
 }  

Wednesday, March 18, 2015

Spiral Matrix

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].

Naive Way: In order to traversal the matrix in spiral order, I need to keep two lengths, one is the current length of row that need to be traversal, one is the current length of column that need to be traversal. Also use a variable to control direction. Like 0-> going left, 1-> going down, 2-> going right, 3-> going up.

 public class Solution {  
   public List<Integer> spiralOrder(int[][] matrix) {  
     List<Integer> rslt = new ArrayList<Integer>();  
     if(matrix.length==0) return rslt;  
     int rowLen = matrix[0].length, colLen = matrix.length-1;  
     int x = 0, y = -1;  
     int dir = 0;  
     while(rowLen >= 0 && colLen >= 0){  
       switch(dir){  
         case 0:  
           for(int i = 0;i < rowLen;i++) rslt.add(matrix[x][++y]);  
           rowLen--;  
           break;  
         case 1:  
           for(int i = 0;i < colLen;i++) rslt.add(matrix[++x][y]);  
           colLen--;  
           break;  
         case 2:  
           for(int i = 0;i < rowLen;i++) rslt.add(matrix[x][--y]);  
           rowLen--;  
           break;  
         case 3:  
           for(int i = 0;i < colLen;i++) rslt.add(matrix[--x][y]);  
           colLen--;  
           break;  
       }  
       dir = ++dir%4;  
     }  
     return rslt;  
   }  
 }  

Multiply Strings

Given two numbers represented as strings, return multiplication of the numbers as a string.
Note: The numbers can be arbitrarily large and are non-negative.

Naive Way: The algorithm for this problem should be simple, which is direct multiplication. However, multiply string will have a lot of corner cases. List all corner cases that I can think of.
  • -/+ at the front
  • "." fraction number in the middle
  • extra zeros at tail
And then, when I was running the program, I found extra corner cases such as,
  • result "00" should be "0"
  • result "0.0" should be "0"
I write two sub functions. One if multiply a string by an integer. The other is plus two String. Because to multiply two strings, I need to multiply a String by one digit each time, and then sum up the results. I also keep a map to record calculated products to speed up the process.

 public class Solution {  
   public String multiply(String num1, String num2) {  
     String rslt = "0";  
     Map<Integer, String> map = new HashMap<Integer, String>();  
     // Eliminate "." for num1 and num2, put them into new String. Get "." position. Get -/+  
     int pointPosition = 0;  
     boolean negative = false;  
     StringBuilder n1 = new StringBuilder();  
     StringBuilder n2 = new StringBuilder();  
     for(int i = 0;i < num1.length();i++)   
       if(num1.charAt(i)=='.') pointPosition+= num1.length()-1-i;  
       else if(num1.charAt(i) =='-') negative = !negative;  
       else if(num1.charAt(i)!='+') n1.append(num1.charAt(i));  
     for(int j = 0;j < num2.length();j++)  
       if(num2.charAt(j)=='.') pointPosition+= num2.length()-1-j;  
       else if(num2.charAt(j) =='-') negative = !negative;  
       else if(num2.charAt(j)!='+') n2.append(num2.charAt(j));  
     // multiply one digit each time  
     for(int j = n2.length()-1;j >= 0;j--){  
       int digit = (int)(n2.charAt(j)-'0');  
       if(digit!=0){  
         String product;  
         if(map.containsKey(digit))  
           product = map.get(digit);  
         else  
           product = multiply(n1.toString(), digit);  
         map.put(digit, product);  
         if(!product.equals("0"))  
           for(int u = 0;u < n2.length()-1-j;u++) product += "0";  
         rslt = plus(rslt, product);  
       }  
     }  
     // add "."  
     if(pointPosition > rslt.length()-1)  
       for(int u = 0;u < pointPosition - (rslt.length()-1);u++)  
         rslt = "0" + rslt;  
     if(pointPosition!=0)  
       rslt = rslt.substring(0, rslt.length()-pointPosition) + "." + rslt.substring(rslt.length()-pointPosition, rslt.length());  
     // get rid of tail zeros when it's fraction number   
     if(pointPosition!=0)  
       while(rslt.length() > 1 && (rslt.charAt(rslt.length()-1) == '0' || rslt.charAt(rslt.length()-1) == '.'))  
         rslt = rslt.substring(0, rslt.length()-1);  
     // add sign  
     if(negative) rslt = "-"+rslt;  
     return rslt;  
   }  
   public String multiply(String num1, int num2){  
     int i = num1.length();  
     int carry = 0;  
     StringBuilder str = new StringBuilder();  
     while(--i >= 0){  
       int product = (int)(num1.charAt(i)-'0') * num2 + carry;  
       int remain = product%10;  
       carry = product/10;  
       str.append((char)('0'+remain));  
     }  
     if(carry!=0) str.append((char)('0'+carry));  
     return str.reverse().toString();  
   }  
   public String plus(String num1, String num2) {  
     int carry = 0;  
     StringBuilder rslt = new StringBuilder();  
     int i = num1.length()-1, j = num2.length()-1;  
     while(i >= 0 && j >= 0){  
       int value = (int)(num1.charAt(i)-'0') + (int)(num2.charAt(j)-'0') + carry;  
       carry = value/10;  
       value = value%10;  
       rslt.append((char)(value+'0'));  
       i--;  
       j--;  
     }  
     while(i >= 0){  
       int value = (int)(num1.charAt(i)-'0') + carry;  
       carry = value/10;  
       value = value%10;  
       rslt.append((char)(value+'0'));  
       i--;  
     }  
     while(j >= 0){  
       int value = (int)(num2.charAt(j)-'0') + carry;  
       carry = value/10;  
       value = value%10;  
       rslt.append((char)(value+'0'));  
       j--;  
     }  
     if(carry!=0) rslt.append((char)(carry+'0'));  
     return rslt.reverse().toString();  
   }  
 }  

Tuesday, March 17, 2015

First Missing Positive

Given an unsorted integer array, find the first missing positive integer.
For example,
Given [1,2,0] return 3,
and [3,4,-1,1] return 2.
Your algorithm should run in O(n) time and uses constant space.

Naive Way: Constant space here is a big constrain. The key here is by swapping. Since the length of the array is fixed. There can be at most array.length positive integers. Find the correct position for each valid positive integer by swapping. That will achieve constant space.

 public class Solution {  
   public int firstMissingPositive(int[] A) {  
     int i = 0;  
     while(i < A.length){  
       if(A[i] == i+1 || A[i] <= 0 || A[i] > A.length) i++;  
       else if(A[A[i]-1] != A[i]) swap(A, i, A[i]-1);  
       else i++;  
     }  
     i = 0;  
     while(i < A.length && A[i] == i+1) i++;  
     return i+1;  
   }  
   private void swap(int[] A, int i, int j){  
     int temp = A[i];  
     A[i] = A[j];  
     A[j] = temp;  
   }  
 }  

Saturday, March 14, 2015

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, abcd)
  • 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) 
 
 
Naive Way:  Use O(n^2) space list to store all 2sum Nodes. Use a map to map a 2sum to its corresponding index in 2sum list. go through each pair again to find valid four sum pairs.

 public class Solution {  
   class TwoSum{  
     int child1, child2;  
     int val;  
     TwoSum(int v, int c1, int c2){  
       this.val = v;  
       this.child1 = c1;  
       this.child2 = c2;  
     }  
   }  
   public List<List<Integer>> fourSum(int[] num, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Set<List<Integer>> set = new HashSet<List<Integer>>();  
     List<TwoSum> twoSums = new ArrayList<TwoSum>();  
     Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
     // edge case  
     if(num.length < 4) return rslt;  
     // construct two sum list  
     for(int i = 0;i < num.length-1;i++)  
       for(int j = i+1;j < num.length;j++)  
         twoSums.add(new TwoSum(num[i]+num[j], i, j));  
     // sort two sum list  
     Comparator<TwoSum> comparator= new Comparator<TwoSum>(){  
       public int compare(TwoSum a, TwoSum b){  
         return a.val > b.val?1:(a.val < b.val?-1:0);  
       }  
     };  
     Collections.sort(twoSums, comparator);  
     // map two sum value with corresponding begin index in two sum list  
     int cur = 0;  
     map.put(twoSums.get(cur).val, cur);  
     for(int i = 1;i < twoSums.size();i++){  
       if(twoSums.get(i).val==twoSums.get(cur).val) continue;  
       cur = i;  
       map.put(twoSums.get(i).val, cur);  
     }  
     // find four sum  
     for(int i = 0;i < num.length-1;i++){  
       for(int j = i+1;j < num.length;j++){  
         int rest = target - num[i] - num[j];  
         if(map.containsKey(rest)){  
           int u = map.get(rest);  
           while(u < twoSums.size() && twoSums.get(u).val == rest){  
             int a = i, b = j, c = twoSums.get(u).child1, d = twoSums.get(u).child2;  
             if(a!=b && a!=c && a!=d && b!=c && b!=d && c!= d){  
               List<Integer> list = new ArrayList<Integer>();  
               list.add(num[a]);  
               list.add(num[b]);  
               list.add(num[c]);  
               list.add(num[d]);  
               Collections.sort(list);  
               set.add(list);  
             }  
             u++;  
           }  
         }  
       }  
     }  
     rslt.addAll(set);  
     return rslt;  
   }  
 }  

This is a pretty inefficient way. It doesn't quite take use of sorting.

If I get two sums as a list of number, can I apply finding two sum on that O(n^2) list using two pointer trick? It fails on the case when two sum sequence is [-4, -4, 4, 4] with target 0. Because both -4 needs to be matched with either 4 once. I cannot simply skip it after use a two sum number.

Can I sort the array first. And then, keep two pointers at begin and end. Each time, go through [begin, end] using two pointer trick like 3sum?

 public class Solution {  
   public List<List<Integer>> fourSum(int[] num, int target) {  
     List<List<Integer>> rslt = new ArrayList<List<Integer>>();  
     Arrays.sort(num);  
     for(int i = 0;i < num.length-3;i++){  
       if(i==0 || i> 0 && num[i]!=num[i-1]){  
       for(int j = i+1;j < num.length-2;j++){  
         if(j==i+1 || j > i+1 && num[j]!= num[j-1]){  
         int rest = target - num[i] - num[j];  
         int low = j+1, high = num.length-1;  
         while(low < high){  
           int sum = num[low] + num[high];  
           if(sum == rest){  
             List<Integer> list = new ArrayList<Integer>();  
             list.add(num[i]);  
             list.add(num[j]);  
             list.add(num[low]);  
             list.add(num[high]);  
             rslt.add(list);  
             while(high > low && num[high] == num[--high]);  
             while(high > low && num[low] == num[++low]);  
           }  
           else if(sum > rest)  
             high--;  
           else  
             low++;  
         }  
         }  
       }  
       }  
     }  
     return rslt;    
   }  
 }  

This method can be generalized to k-sum.

Palindrome Number

Determine whether an integer is a palindrome. Do this without extra space.

Naive Way: At first, I try to use division(/) to get highest digit and mod(%) to get lowest digit and eliminate both digit once manipulated, but fail at case 10021. And then, I turn to a more brute force way. Write a sub function that get a particular digit from candidate integer. Keep compare mirror digits.

 public class Solution {  
   static final int MAX_DIGIT = 10;  
   public boolean isPalindrome(int x) {  
     // negative integer is not palindrome  
     if(x < 0) return false;  
     int low = 0, high = MAX_DIGIT;  
     // get highest digit  
     while(x < Math.pow(10,high)) high--;  
     // validate each digit pair  
     while(low < high) if(getDigit(low++, x)!=getDigit(high--, x)) return false;  
     return true;  
   }  
   private int getDigit(int n, int x){  
     // edge case, x doesn't have n digit  
     if(Math.pow(10, n) > x) return -1;  
     // edge case, n == base  
     if(n==(int)Math.pow(10,MAX_DIGIT) && x >= n) return x/n;  
     // general case  
     return (x%(int)Math.pow(10,n+1) - x%(int)Math.pow(10,n))/(int)Math.pow(10,n);  
   }  
 }  

After viewing some solution in Discuss, I found that there are some one who handles 10021 case in a correct way. I developed my own based on previous attempt.

 public class Solution {  
   static final int base = 1000000000;  
   public boolean isPalindrome(int x) {  
     // negative integer is not palindrome  
     if(x < 0) return false;  
     int i = base;  
     while(i > x) i/= 10;  
     while(i > 1){  
       if(x/i != x%10) return false;  
       x -= x/i * i;  
       x /= 10;  
       i /= 100;  
     }  
     return true;  
   }  
 }  


Improved Way: I didn't think of reverse the integer and than compare. And I think reverse an integer may incur a lot more edge case. But if you are able to deal with edge case gracefully, that's a good method. See o-1-space-o-lgn-time-java-solution-no-overflow-risk

Friday, March 13, 2015

Reverse Integer

Reverse digits of an integer.
Example1: x = 123, return 321
Example2: x = -123, return -321 

Have you thought about this?
Here are some good questions to ask before coding. Bonus points for you if you have already thought through this!
If the integer's last digit is 0, what should the output be? ie, cases such as 10, 100.
Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, then the reverse of 1000000003 overflows. How should you handle such cases?
For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.

Naive Way:  My method is to keep getting the least valuable digit from x. This method is a good approach when I implements it. But the edge case really bothers me. I manually add edge overflow cases in three parts, which is pretty bad.

 public class Solution {  
   public int reverse(int x) {  
     // edge case  
     if(x==Integer.MIN_VALUE) return 0;  
     // negative number, convert from positive  
     if(x < 0) return -reverse(-x);  
       
     int y = 0;  
     for(int i = 10;x!=0;i *= 10){  
         
       // overflow  
       if(y > Integer.MAX_VALUE/10) return 0;  
         
       y *= 10;  
       y += (x%i)/(i/10);  
       x -= x%i;  
         
       // overflow  
       if(i==1000000000 && x!=0){  
         if(y > Integer.MAX_VALUE/10) return 0;  
         y *= 10;  
         y += x/i;  
         x = 0;  
       }  
     }  
     return y;  
   }  
 }  

Improved Way: A better way is to include as many as edge case in the main loop. Then I try to get the most valuable digit first. It turns out that it's hard to deal with edge case also.

 public class Solution {  
   static final int base = 1000000000;  
   public int reverse(int x) {  
     // edge case, overflow  
     if(x == Integer.MIN_VALUE) return 0;  
     // negative value  
     if(x < 0) return -reverse(-x);  
       
     int y = 0, highest = 1;  
     for(int i = base;i != 0;i/= 10){  
       // note down highest digit  
       if(x >= i && highest==1) highest = i;  
         
       // edge case, overflow  
       if(x/i > 2 && highest == base && i==1) return 0;  
       if(y > Integer.MAX_VALUE - x/i*(highest/i)) return 0;  
         
       y += x/i*(highest/i);  
       x -= x/i*i;  
     }  
     return y;  
   }  
 }  


Using Double or Long can simply deal with overflow, just check the result after reverse. But I don't think that's what the question is asking.


And my first approach is a correct thinking, but not being correctly manipulated. Here is a gentle way to deal with overflow edge case.

 public class Solution {  
   public int reverse(int x) {  
     // edge case  
     if(x==Integer.MIN_VALUE) return 0;  
     // negative number, convert from positive  
     if(x < 0) return -reverse(-x);  
       
     int y = 0;  
     while(x != 0){  
         
       // edge case, overflow  
       if(y > Integer.MAX_VALUE/10) return 0;  
         
       y *= 10;  
       y += x%10;  
       x /= 10;  
     }  
     return y;  
   }  
 }  

Friday, March 6, 2015

Candy

There are N children standing in a line. Each child is assigned a rating value.
You are giving candies to these children subjected to the following requirements:
  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.
What is the minimum candies you must give?

Naive Way: I don't like the idea of rating children. This question is hard. Really Hard! To generate candy assignment in one pass is unaffordable. I turn to two pass. And it becomes more clear after I assign each children 1 candy at first.

Below solution requires two pass(three pass including sum up), O(n) space.

 public class Solution {  
   public int candy(int[] ratings) {  
     // initialize  
     int candy[] = new int[ratings.length];  
     int sum = 0;  
     Arrays.fill(candy,1); // crucial step!  
     // forward pass, assign candy for increasing ones  
     for(int i = 1;i < ratings.length;i++)  
       if(ratings[i] > ratings[i-1])  
         candy[i] = candy[i-1]+1;  
     // backforward pass, assign candy for decreasing ones  
     for(int i = ratings.length-2;i >= 0;i--)  
       if(ratings[i] > ratings[i+1]){  
         if(i-1 >= 0 && ratings[i-1] <= ratings[i])  
           candy[i] = Math.max(candy[i+1]+1, candy[i]);  
         else  
           candy[i] = candy[i+1] + 1;  
       }  
     // sum up candy  
     for(int i = 0;i < candy.length;i++)  
       sum += candy[i];  
     return sum;  
   }  
 }  

Improved Way: And this question of course can be solved in one pass. An good code with explanation is here

Wednesday, March 4, 2015

Valid Sudoku

Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules.
The Sudoku board could be partially filled, where empty cells are filled with the character '.'.


A partially filled sudoku which is valid.
Note:
A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.

Naive Way: The statement "A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated. " makes this question much easier as we just need to check validation  for each empty cell.

 public class Solution {  
   public boolean isValidSudoku(char[][] board) {  
     for(int i = 0;i < 9;i++)  
       for(int j = 0;j < 9;j++)  
         if(board[i][j] != '.')  
           if(!isValidCell(board, i, j))  
             return false;  
     return true;  
   }  
   private boolean isValidCell(char[][] board, int x, int y){  
     // its block  
     int center_x = x/3 * 3 + 1;  
     int center_y = y/3 * 3 + 1;  
     for(int i = -1;i <= 1;i++)  
       for(int j = -1;j <= 1;j++)  
         if(board[center_x+i][center_y+j]==board[x][y] && center_x+i!=x && center_y+j!=y)  
           return false;  
     // its row  
     for(int i = 0;i < 9 && i!=x;i++)  
       if(board[i][y]==board[x][y])  
         return false;  
     // its column  
     for(int j = 0;j < 9 && j!=y;j++)  
       if(board[x][j]==board[x][y])  
         return false;  
     return true;  
   }  
 }  

Using a HashSet may help reduce the run time, but it's at most 9*9=81 cells. Can be regarded as O(1) I think. And accessing array is of high efficiency in Java.

Friday, February 20, 2015

ZigZag Conversion

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


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

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

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

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

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

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

Tuesday, February 17, 2015

Plus One


Plus One



Given a non-negative number represented as an array of digits, plus one to the number.
The digits are stored such that the most significant digit is at the head of the list.


 



Naive Way: 有没有可能面试官问这道题。



 



 public class Solution {
    public int[] plusOne(int[] digits) {
        int[] rslt;
        boolean carry = true;
        for(int i = digits.length-1;i >=0;i--){
            int v = digits[i]+(carry?1:0);
            carry = v > 9;
            v %= 10;
            digits[i] = v;
        }
        if(carry){
            rslt = new int[digits.length+1];
            rslt[0] = 1;
            for(int i = 1;i < rslt.length;i++) rslt[i] = digits[i-1];
        }else{
            rslt = digits;
        }       
        return rslt;
    }
}

Next Permutation


Next Permutation



 


Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,31,3,2
3,2,11,2,3
1,1,51,5,1



Naive Way: 审题:1.recursion 2. in-place
是在一个数组上 in-place 进行的,那么就只有 覆盖原来的(overlap) 和 交换两个数(swap) 。
通过探讨它的原理我发现可以这样找,

1.从后往前遍历,应该是递增(从后往前)的,找到第一个非递增。

2.那个点就是要升位一个的点 t,在刚才来的路上找到比该数大且最接近的数 s,用该数 s 替代它(升位)。

3.后面的部分需要是从前往后看递增的,而之前来的路上一切都是递减的,所以倒过来写就可以了,相当于两头交换。

4.但是还有一个数要处理的就是 t 这个数本身,可以在交换好后,从 s 所在位置往后遍历一遍插入对应的位置。

实际做的时候发现第4步是多余的,因为交换了 t 和 s 的位置后,并不会打乱原来递减的顺序。还有一点就是找 s 的位置一定要找相同差值下最后面的位置,比如两个3,就要取后面的3,因为要保持后半部分的递减性。

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

public class Solution {
    public void nextPermutation(int[] num) {
        int t,s;
        int i = num.length-1;
        while(i > 0 && num[i-1] >= num[i]) i--;
        if(i > 0){
            t = i-1;
            // search for nearest value larger than num[t] in num[i~length]
            s = i;
            for(int j = i;j < num.length;j++)
                if(num[j] > num[t] && num[j]-num[t] <= num[s]-num[t])
                    s = j;
            // swap position t and s
            num[t] = num[s] + num[t];
            num[s] = num[t] - num[s];
            num[t] = num[t] - num[s];
        }
        swapHeadAndTail(num, i, num.length-1);
        return;
    }
    
    private void swapHeadAndTail(int[] num, int begin, int end){
        while(begin < end){
            num[begin] = num[end] + num[begin];
            num[end] = num[begin] - num[end];
            num[begin] = num[begin] - num[end];
            begin++;
            end--;
        }
        return;
    }
}


以下是我第一次写的,应该当时不会写,是抄的。

public class Solution {
    public void nextPermutation(int[] num) {
        int i = num.length-1;
        int j;
        while(i > 0){
            if(num[i] > num[i-1])
                break;
            i--;
        }
        if(i != 0){
            for(j = i;j < num.length;j++)
                if(num[j] <= num[i-1])
                    break;
            swap(num, i-1, j-1);
        }
           
        reverse(num, i, num.length-1);
    }
   
    private void reverse(int[] A, int begin, int end){
        for(int i = begin;i <= (end+begin)/2;i++)
            swap(A,i,end+begin-i);
    }
   
    private void swap(int[] A, int a, int b){
        int temp = A[a];
        A[a] = A[b];
        A[b] = temp;
    }
       
}

Sunday, February 8, 2015

Integer to Roman


Integer to Roman



 


Given an integer, convert it to a roman numeral.
Input is guaranteed to be within the range from 1 to 3999.

Naive Way:还是先写几个例子。
Integer    Roman
1             I
2             II
3             III
4             IV
5             V
6             VI
7             VII
8             VIII
9             IX
10            X
11            XI
14            XIV
15            XV
18            XVIII
19            XIX
21            XXI

可以采用不断取最高位的方法,一旦余数是9,4倍下一级的,就要单独处理。

我的做法是,每一次除当前位的同时都除以下一位看是不是9。考虑到45/5 = 9,45/10 = 4...5这种情况,4的优先级比9高。

public class Solution {
    private static final char[] syms = {'I','V','X','L','C','D','M'};
    private static final int[] val = {1,5,10,50,100,500,1000}; 
    public String intToRoman(int num) {
        String s = "";
        int i = val.length-1;
        while(num!=0){
            int cur = num/val[i];
            int low = i > 0?num/val[i-1]:0;
            if(cur!=0){
                if(cur==4){
                    s += syms[i];
                    s += syms[i+1];
                    num %=val[i];
                }else if(low==9){
                    s += syms[i-1];
                    s += syms[i+1];
                    num %=val[i-1];
                }else{
                    for(int j =0;j < cur;j++)
                        s += syms[i];
                    num %=val[i];
                }
            }
            i--;
        }
        return s;
    }
}


Improved Way:discuss里的方法真是千奇百怪啊,各种数组。下面有三种方法是我觉得最好玩的。



 




一、 来自leetcode的 jakwings用户。[[1 5], [10 50]]这样的排成二维数组,巧妙将4和9化为同一等级。



 


class Solution {
public:
    vector<vector<string>> table = {
        {"I", "V"},
        {"X", "L"},
        {"C", "D"},
        {"M", "~V"}  // ~V = 1000 * V
    };
    string intToRoman(int num) {
        if (num <= 0) return "";
        vector<int> digits;
        for (int t = num; t != 0; t /= 10) {
            digits.push_back(t % 10);
            int i = digits.size() + 1;
            if (table.size() < i) {
                table.push_back({'~' + table[i-3][0], '~' + table[i-3][1]});
            }
        }
        string result;
        for (int i = digits.size() - 1; i >= 0; i--) {
            int x = digits[i];
            switch (x) {
            case 0: break;
            case 9: result += table[i][0] + table[i+1][0]; break;
            case 4: result += table[i][0] + table[i][1]; break;
            default:
                if (x >= 5) result += table[i][1];
                for (int j = x % 5; j > 0; j--) result += table[i][0];
            }
        }
        return result;
    }
};

 



二、来自leetcode用户 flytosky ,将所有所有可能出现的单个都列了一遍,因为本来1~10也才10个数,到1000也才30个数。这个方法我觉得最牛了。



 


string intToRoman(int num) {
    string M[] = {"", "M", "MM", "MMM"};
    string C[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
    string X[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
    string I[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
    return M[num/1000] + C[(num%1000)/100] + X[(num%100)/10] + I[num%10];
}
 
 


 三、来自leetcode用户magicknife,也是discuss里少有的用recursive去做的,将4, 9也看成一个符号。这样每一个10位就有1 4 5 9 四种符号。



 



 


public class Solution {

    private static LinkedHashMap<Integer, String> numToRoman = new LinkedHashMap<Integer, String>();
    static {
        numToRoman.put(1000, "M");
        numToRoman.put(900, "CM");
        numToRoman.put(500, "D");
        numToRoman.put(400, "CD");
        numToRoman.put(100, "C");
        numToRoman.put(90, "XC");
        numToRoman.put(50, "L");
        numToRoman.put(40, "XL");
        numToRoman.put(10, "X");
        numToRoman.put(9, "IX");
        numToRoman.put(5, "V");
        numToRoman.put(4, "IV");
        numToRoman.put(1, "I");
    }
    public String intToRoman(int num) {
        for (Integer i : numToRoman.keySet()) {
            if (num >= i) {
                return numToRoman.get(i) + intToRoman(num - i);
            }
        }
        return "";
    }
}

 






 
 

Roman to Integer



Roman to Integer



 


Given a roman numeral, convert it to an integer.
Input is guaranteed to be within the range from 1 to 3999.

Naive Way:先写几个例子:
Roman   Integer
I              1

II          2



III         3



IV         4



V          5 



VI        6



VII       7



VIII      8



IX         9



X         10



L          50



C         100



D         500



M        1000



 



可以用一个数组先存字母。



一个级别低的字母出现在级别高的左边就是减,右边就是加。 






使用了一个Map,便于比较等级,算法复杂度O(n),spaceO(1)。






public class Solution {
    private static final char[] syms = {'I','V','X','L','C','D','M'};
    private static final int[] val = {1,5,10,50,100,500,1000};
    public int romanToInt(String s) {
        int rlst = 0;
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        for(int i = 0;i < syms.length;i++)
            map.put(syms[i], i);
        for(int i = 0;i < s.length()-1;i++)
            rlst += map.get(s.charAt(i)) < map.get(s.charAt(i+1))?-val[map.get(s.charAt(i))]:val[map.get(s.charAt(i))];
        rlst += val[map.get(s.charAt(s.length()-1))];
        return rlst;
    }
}



 



 



Improved Way:看了别人的算法,发现自己的两个数组跟这个map相当冲突。



应该直接把字母map成value,这样就保存等级,又保存value了。



 



public class Solution {
    private static final char[] syms = {'I','V','X','L','C','D','M'};
    private static final int[] val = {1,5,10,50,100,500,1000};
    public int romanToInt(String s) {
        int rlst = 0;
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        for(int i = 0;i < syms.length;i++)
            map.put(syms[i], val[i]);
        for(int i = 0;i < s.length()-1;i++)
            rlst += map.get(s.charAt(i)) < map.get(s.charAt(i+1))?-map.get(s.charAt(i)):map.get(s.charAt(i));
        rlst += map.get(s.charAt(s.length()-1));
        return rlst;
    }
}




Saturday, February 7, 2015

Pow(x, n)


Pow(x, n)



Implement pow(x, n).

Naive Way: brute force是O(n)。很明显x^5 = x^2 * x^2 * x 是缩小时间复杂度的关键。这里还要记得有负数次幂的情况。经观察和我第一次做时看别人做法的记忆,7=4+2+1 是核心。
x^7 = x^4 + x^2 + x^1。
那么先将[x^1,x^2,x^4...x^(logn)]罗列出来,
第一次n=7, 7/4 = 1...3 说明有一个x^4,
第二次n=3,    3/2 = 1...1 说明有一个x^2,
第三次n=1,    1/1 = 1...0 说明有一个x^1。
要注意如果商是0,说明没有对应项的乘因子,就不乘或者乘1.0。

这样就变成了不断取最高位的算法。算法复杂度是O(logn),space是O(logn)

public class Solution {
    public double pow(double x, int n) {
        if(n==0 || x==1.0){return 1.0;}
        if(x==-1.0){return n%2==0?1.0:-1.0;}
        if(n < 0){return 1.0/pow(x,-n);}
        int len = (int)Math.floor(Math.log(n)/Math.log(2));
        double rlst = 1.0;
        double[] carry = new double[len+1];
        for(int i = 0;i <= len;i++)
            carry[i] = i==0?x:Math.pow(carry[i-1],2);
        while(n!=0){
            int num = n/(int)Math.pow(2,len);
            rlst *= num==0?1.0:carry[len]*num;
            n %= (int)Math.pow(2,len--);
        }
        return rlst;
    }
}


Improved Way:x^7 = x^4 + x^2 + x^1的这个信息,其实就藏在7这个数的比特位中,7 = 0x0111,
只需要用比特运算就可以提取对应位了。并且,因为这样不需要从高往低乘,可以从低往高乘,那么低位的乘因子乘过以后就不会再用了,不需要一直存着,可以通过与比特位递进同步平方乘因子,达到O(1)space的效果。

这种方法也太牛了,居然只用O(1) run time 和O(1) space。

public class Solution {
    public double pow(double x, int n) {
        if(n < 0){return 1.0/(n==Integer.MIN_VALUE?x*pow(x,-(n+1)):pow(x,-n));}
        double rlst = 1.0;
        while(n!=0){
            if((n & 1) == 1){
                rlst*= x;
            }
            x*=x;
            n = n >> 1;
        }
        return rlst;
    }
}

Friday, February 6, 2015

3Sum


3Sum



 


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, abc)
  • 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) 
 
 
Naive Way:这已经是我第三次做这道题了。 brute force是O(n^3),然后较好的做法是O(n^2)。
经查证,还没有方法能在小于O(n^2)的时间内解决3sum问题。实际在OJ上做,发现即使是同样的O(n^2)
的run time, 也会有超时。

第一种做法, 一开始不排序,先用一个map记录每个数的出现次数,遍历O(n^2)求两两之和,再再map中
看看有没有剩下的那个数,这样的做法需要在形成list的时候排序,并且得有一个set控制duplicate。
但是这样的做法超时了。

public class Solution {

    public List<List<Integer>> threeSum(int[] num) {

        Set<List<Integer>> set = new HashSet<List<Integer>>();

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

        List<List<Integer>> output = new ArrayList<List<Integer>>();

        for(int i = 0;i < num.length;i++){

            if(map.containsKey(num[i]))

                map.put(num[i], map.get(num[i])+1);

            else

                map.put(num[i], 1);

        }

        for(int i = 0;i < num.length-1;i++){

            for(int j = i+1;j < num.length;j++){

                int a = num[i];

                int b = num[j];

                int c = -num[i]-num[j];

                if(map.containsKey(c)){

                    int count = 1;

                    count += a==c?1:0;

                    count += b==c?1:0;

                    if(count <= map.get(c)){

                        List<Integer> list = new ArrayList<Integer>();

                        int arr[] = new int[3];

                        arr[0] = a;

                        arr[1] = b;

                        arr[2] = c;

                        Arrays.sort(arr);

                        list.add(arr[0]);

                        list.add(arr[1]);

                        list.add(arr[2]);

                        if(!set.contains(list))

                            set.add(list);

                    }

                }

            }

        }

        output.addAll(set);

        return output;

    }

    

}

 
 
Improved Way: 
第二种做法,也是比较牛一点的做法,就是wiki上给的做法,但是这里要求去除重复,要做出修改。

public class Solution {
    public List<List<Integer>> threeSum(int[] num) {
        List<List<Integer>> rlst = new ArrayList<List<Integer>>();
        Arrays.sort(num);
        for(int u = 0;u < num.length-2;u++){
            if(u==0 || (u > 0 && num[u]!=num[u-1])){
            int i = u+1;
            int j = num.length-1;
            while(i < j){
                int sum = num[i]+num[j]+num[u];
                if(sum==0){
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(num[u]);
                    list.add(num[i]);
                    list.add(num[j]);
                    rlst.add(list);
                    while(i < j){
                        if(num[j]!=num[j-1])
                            break;
                        j--;
                    }
                    while(i < j){
                        if(num[i]!=num[i+1])
                            break;
                        i++;
                    }
                    i++;
                    j--;
                }else if(sum > 0){
                    j--;
                }else{
                    i++;
                }
            }
            }
        }
        return rlst;
    }
}


最后我发现我第一次的做法,是可以通过的O(n^2)非常非常的厉害,我觉得,居然想到了用正数负数。

public class Solution {
    List<List<Integer>> output;
    HashSet<List<Integer>> visited;
    public List<List<Integer>> threeSum(int[] num) {
        output = new ArrayList<List<Integer>>();
        visited = new HashSet<List<Integer>>();
        //Arrays.sort(num);
        Map<Integer, Integer> positive = new HashMap<Integer, Integer>();
        Map<Integer, Integer> negative = new HashMap<Integer, Integer>();
        int numOfZeros = 0;
        // construct the mapping
        for(int i = 0;i < num.length;i++){
            if(num[i] == 0)
                numOfZeros++;
            if(num[i] > 0)
                if(positive.containsKey(num[i]))
                    positive.put(num[i], positive.get(num[i])+1);
                else
                    positive.put(num[i],1);
            if(num[i] < 0)
                if(negative.containsKey(num[i]))
                    negative.put(num[i], negative.get(num[i])+1);
                else
                    negative.put(num[i],1);
        }
       
        // generate results
        // two positive + one negative
        for(Map.Entry<Integer, Integer> a:positive.entrySet())
            for(Map.Entry<Integer, Integer> b:positive.entrySet())
                if(a.getKey() != b.getKey() || a.getValue() >= 2)
                    if(negative.containsKey(-a.getKey()-b.getKey()))
                        addResult(a.getKey(),b.getKey(),-a.getKey()-b.getKey());
                   
       
        // two negative + one positive
        for(Map.Entry<Integer, Integer> a:negative.entrySet())
            for(Map.Entry<Integer, Integer> b:negative.entrySet())
                if(a.getKey() != b.getKey() || a.getValue() >= 2)
                    if(positive.containsKey(-a.getKey()-b.getKey()))
                        addResult(a.getKey(),b.getKey(),-a.getKey()-b.getKey());
       
        // one positive+zero+one negative
        if(numOfZeros > 0)
            for(Map.Entry<Integer, Integer> a:negative.entrySet())
                if(positive.containsKey(-a.getKey()))
                    addResult(0,a.getKey(),-a.getKey());
       
        // three zeros
        if(numOfZeros > 2)
            addResult(0,0,0);
           
        return output;
    }
   
    private void addResult(int a, int b, int c){
        int array[] = {a,b,c};
        Arrays.sort(array);
        List<Integer> result = new ArrayList<Integer>();
        result.add(array[0]);
        result.add(array[1]);
        result.add(array[2]);
        if(!visited.contains(result)){
            visited.add(result);
            output.add(result);
        }
    }
}

 

3Sum Closest


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). 
 
 
Naive Way: 我一开始想了一个binary search 的办法,先把数组排序,每次取一头(i)一尾(j),在中间
找最接近x = target-num[i]-num[j]的数,如果得到的和num[i]+num[j]+num[x]小于target,说明
要增加sum,i++,否则就要减小sum,j--。这个方法只需要O(nlogn)的时间。
 
public class Solution {
    public int threeSumClosest(int[] num, int target) {
        Arrays.sort(num);
        int min = Integer.MAX_VALUE;
        int rlst = target;
        int i = 0, j = num.length-1;
        while(i+1 < j){
            int rest = target-num[i]-num[j];
            int x = binarySearch(num, i+1, j-1, rest);
            int sum = num[i]+num[j]+num[x];
            if(sum==target){return target;}
            if(sum < target)
                i++;
            else
                j--;
            if(Math.abs(sum-target) < min){
                min = Math.abs(sum-target);
                rlst = sum;
            }
        }
        return rlst;
    }   

    private int binarySearch(int num[], int begin, int end, int t){
        int middle = begin;
        if(t > num[end]){return end;}
        if(t < num[begin]){return begin;}
        while(begin < end){
            middle = (begin+end)/2;
            if(num[middle]==t)
                break;
            if(num[middle] < t)
                begin = middle+1;
            if(num[middle] > t)
                end = middle-1;
        }
        return Math.abs(num[middle]-t) >= Math.abs(num[middle+1]-t)?middle+1:middle;
    }
} 


这个方法可以通过OJ,但是这个方法是错的,在Discuss上居然有人跟我想了同一个方法,并且有别人提出了质疑,举出了
[0 5 50 100 140] 150, 这个例子, 这个例子说明了即使当前和小于target,也不一定要增加序数而有可能要减小序数,因为前后两次binary search得到的数会不一样。于是这个O(nlogn)的方法宣告失败。

Improved Way:从头开始想,brute force 需要O(n^3),那么应该在O(n^2)或者O(n^2 logn)内解决。3-sum就可以O(n^2)解决,但是这次不能确切找一个数,用set先存起来没什么用作。根据之前的方案,通过O(n^2)可以遍历所有两个数的组合,那么可以通过O(logn)的 binary search找最接近的数,这样下来算法就是O(n^2 logn),这样的方法我试了试,是可以通过的,而且运行时间并不慢。

public class Solution {
    public int threeSumClosest(int[] num, int target) {
        Arrays.sort(num);
        int min = Integer.MAX_VALUE;
        int rlst = target;
        for(int i = 0;i < num.length-2;i++){
            for(int j = num.length-1; j >= i+2;j--){
                int rest = target-num[i]-num[j];
                int x = binarySearch(num, i+1, j-1, rest);
                int sum = num[i]+num[j]+num[x];
                if(Math.abs(sum-target) < min){
                    min = Math.abs(sum-target);
                    rlst = sum;
                }
            }
        }
        return rlst;
    }
    
    private int binarySearch(int num[], int begin, int end, int t){
        int middle = begin;
        if(t > num[end]){return end;}
        if(t < num[begin]){return begin;}
        while(begin < end){
            middle = (begin+end)/2;
            if(num[middle]==t)
                break;
            if(num[middle] < t)
                begin = middle+1;
            if(num[middle] > t)
                end = middle-1;
        }
        return (begin+end)/2;
    }
}


Discuss中有唯一一种O(n^2)的方法,用了3sum一模一样的策略。原来觉得很厉害,现在觉得好像谁都会了。


public class Solution {
    public int threeSumClosest(int[] num, int target) {
        if(num.length < 3){return 0;}
        int closest = num[0]+num[1]+num[2];
        int dis = Math.abs(closest-target);
        Arrays.sort(num);
        for(int i = 0;i < num.length-2;i++){
            // set two pointers, one from small-end, one from large-end
            int p = i+1;
            int q = num.length-1;
            while(p < q){
                int sum = num[i]+num[p]+num[q];
                if(Math.abs(sum-target) < dis){
                    closest = sum;
                    dis = Math.abs(sum-target);
                }
                if(sum > target)
                    q--;
                if(sum < target)
                    p++;
                if(sum == target)
                    return sum;
            }
        }
        return closest;
    }
}



 



 

Monday, February 2, 2015

Maximum Subarray


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 Way: 一个感觉觉得跟sell stock那题很像,可以用DP吧,先试一试。

基本逻辑为:
opt[i,j] // 表示从i到j的maximum subarray。
才写第一句就发现这样一来就是O(n^2)的复杂度了。但是感觉这一题应该是能在O(n)的时间内算出来的。

一个比较直观的想法就是用指针控制记录有效区段首尾。一旦遇到整数,就有可能要替换最大值,一旦遇到负数,只要当前和仍大于零,就可以将该负数加入有效区段内。这里自己还漏掉了全是负数的情况 ,但这种情况其实很简单,全是负数时最大的和就是其中一个负数,每次取到负数都与最大值进行比较则可将该情况囊括。


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


最后看来一个人的discuss发现跟我的一模一样,就是没有begin 和 end。这才发现begin和end在这段代码中没有作用,删除掉。

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

最终版本应该是这样的:(来自leetcode用户 AlexTheGreat)

public int maxSubArray(int[] A) {
    int max = Integer.MIN_VALUE, sum = 0;
    for (int i = 0; i < A.length; i++) {
        if (sum < 0) 
            sum = A[i];
        else 
            sum += A[i];
        if (sum > max)
            max = sum;
    }
    return max;
}
 
 
Improved Way: 看见leetcode上有人提问这道题用divide and conquer做,
一位大神回答了说divide and conquer是 O(nlogn)。那么就懂如果要
divide and conquer是怎么回事,每次都得对比两边的最大subarray sum和
中间部分有可能的subarray sum,想想都觉得很麻烦,还是先不写了。