Leetcode刷题笔记(部分非原创)(1-20题)

1. Two Sum

    leetcode链接: https://oj.leetcode.com/problems/two-sum/

    最基础的一道题,方法很多,用HashMap存pair是一种(HashSet和HashMap的方法在这里原理是一样的)。也可以sort the whole array first,then use two pointers, one start from the left side, the other from the right side. if array[left]+array[right]>target, move the right index to the left by 1, 小于的话同理. 这里给一个HashMap的方法。

 1 public int[] twoSum(int[] numbers, int target) {
 2    HashMap<Integer, Integer>  map = new HashMap<Integer, Integer>();
 3    int[] result = new int[2];
 4    for(int i = 0; i < numbers.length; i++) {
 5      if(map.containsKey(numbers[i])) {
 6        int index = map.get(numbers[i]);
 7        result[0] = index + 1;
 8        result[1] = i + 1;
 9        break;  
10      } else {
11        map.put(target - numbers[i], i);
12      }   
13    } 
14    return result;          
15 }    

2. Median of Two sorted arrays

     leetcode链接: https://oj.leetcode.com/problems/median-of-two-sorted-arrays/

     首先,比较暴力的方法是统计两个数组的长度(奇数长度有一个median,偶数长度是最中间的两个数的均值),然后两个数组各设置一个index, 谁小,谁的index++,直到两个index走的总长度达到了median所需的长度。这个是O(n)的复杂度。进一步想,数组里找某个数的问题很容易往binary search上面考。这题也不例外。只不过这道题对于边界的控制需要小心,一下写出bug free的代码还是挺有难度的。

    关键的是把这个问题转化为第k小的问题。只不过最终找的这个k其实是median。转化为第k小的问题有一个好处,比较好设计成一个recursion的函数。recursion,第一步想清楚base case, 这个recursion里面,第k小的数、这个k是可能会变化的。变化到最后,剩下的某个数组为空,或者k==1的时候,就找到了我们想要的结果,recursion就走到头了。第二步想清楚recursion rule。每次判断剩余的A和B两个数组各自的median,通过比较他们的大小,我们要么舍弃A的前半段和B的后半段,要么舍弃A的后半段和B的前半段。每一个情况又分为两种,如果舍弃的是某个数组的后半段,我们要找的仍然是剩下的元素中间的第k小的数,k不变。如果舍弃的是某个数组的前半段,那么我们的k就要发生变化了,传进下一层recursion的k参数就要发生变化。综上,我们实际上每层recursion会分四种情况讨论。具体的我在下面的代码里做了注释。

 1 public double findMedianSortedArrays(int[] A, int[] B) {
 2      int lengthA = A.length;
 3      int lengthB = B.length;
 4      if((lengthA + lengthB) % 2 == 0) {
 5          double r1 = (double)findM(A, 0, lengthA, B, 0, lengthB, (lengthA + lengthB) / 2);
 6          double r2 = (double)findM(A, 0, lengthA, B, 0, lengthB, (lengthA + lengthB) / 2 + 1);
 7          return (r1 + r2) / 2;            
 8     } else {
 9          return findM(A, 0, lengthA, B, 0, lengthB, (lengthA + lengthB) / 2 + 1);
10     }
11 }    
12 
13 public int findM(int[] A, int startA, int endA, int[] B, int startB, int endB, int k) {
14 //转换为第k小数的问题
15     int n = endA - startA;
16     int m = endB - startB;
17     if(n <= 0) {//corner case & base case of recursion
18          return B[startB + k - 1];
19     }     
20     if(m <= 0) {//corner case & base case of recursion
21          return A[startA + k - 1]
22     }
23     if(k == 1) {//corner case & recursion case of recursion
24          return A[startA] < B[startB] ? A[startA] : B[startB];
25     }
26     int midA = (startA + endA) / 2;
27     int midB = (startB + endB) / 2;
28     if(A[midA] <= B[midB]) {// the target will appear at the first half of A, or the second half of B
29          if(n / 2 + m / 2 + 1 >= k) {// if in second half of B, k will not change, because we will not cut any number smaller than target
30               return findM(A, startA, endA, B, startB, midB, k);
31          } else {// if in the first half of A, we will cut all the number in the first half, the next step is: find the (k-n/2-1)th smallest number
32               return findM(A, mid + 1, endA, B, startB, endB, k - n / 2 - 1);
33      } else {
34          if(n / 2 + m / 2 + 1 >= k) {
35               return findM(A, startA, midA, B, startB, endB, k);     
36           } else {
37               return findM(A, startA, endA, B, midB + 1, endB, k - m / 2 -1);
38          }
39     }
40 }           

3. Longest Substring Without Repeating Characters

     leetcode链接: https://oj.leetcode.com/problems/longest-substring-without-repeating-characters/

      首先能直观的想到暴力的方法,两个指针标记子串的头和尾,hashset来检查是否有重复,两层for循环头和尾,hashset检测到重复以后退出尾部指针的循环,比较与global的大小并更新。但是这里时间复杂度是O(n^2)。一个更好的办法是开辟一个256的数组,作用其实就是个字典表

     

 1 public int lengthOfLongestSubstring(String s) {
 2      int[] charMap = new int[256];
 3      Arrays.fill(charMap, -1);
 4      int i = 0;
 5      int maxLen = 0;
 6      for(int j = 0; j < s.length(); j++) {
 7           if(charMap[s.charAt(j)] >= i) {
 8                i = charMap[s.charAt(j)] + 1;  
 9           }      
10           charMap[s.charAt(j)] = i;
11           maxLen = Math.max(j - i + 1, maxLen);
12     }  
13     return maxLen;
14 }

 4. Add Two Numbers

      leetcode链接: https://oj.leetcode.com/problems/add-two-numbers/

      这个题思路很直观,两个链表各一个指针用来读值。另外需要考虑几件事,1. 当某个链表读到头的时候怎么办,2. 这种合并链表的题最好设置dummyHead,3. 最后的最后,如果依然有进位,别把它忘了。其他倒是没啥了。O(m+n)的复杂度

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    int carry = 0;
    ListNode newHead = new ListNode(0);
    ListNode p1 = l1;
    ListNode p2 = l2;
    ListNode 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;
}

5. Longest Palindromic Substring

     leetcode链接: https://oj.leetcode.com/problems/longest-palindromic-substring/

    这道题思路仍然很直接,循环扫描每个字符,每次从中心开花,找palindrome。但是有几点需要注意:

    1. palindrome分为两种,一种是中心有一个元素,然后两边元素满足palindrome,另一种是中心有两个元素,这两个元素相等,然后以他俩为中心两边palindrome

    2. corner case:写辅助函数的时候,是否在palindrome为1的时候合法?len2里面,(s, i, i+1),i+1这个参数貌似越界了,真的越界了么,会throw exception么?可以自己把特殊情况带进去,在演草纸上划拉一下,验证下。

    3. 审题。题目需要返回的结果是个最长palindrome的substring,而不是int length。所以我们需要在找到更大长度的时候更新start和end这两个index。

public String longestPalindrome(String s) {
    int start = 0;
    int end = 0;
    for(int i = 0; i < s.length(); i++) {
         int len1 = expandAroundCenter(s, i, i);
         int len2 = expandAroundCenter(s, i, i + 1);
         int len = Math.max(len1, len2);
         if(len > end - start) {
              start = i - (len - 1) / 2;
              end = i + len / 2;
         }
    }
    return s.substring(start, end + 1);
}
public int expandAroundCenter(String s, int left, int right) {
    int L = left;
    int R = right;
    while(L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
          L--;
          R++;
    }  
    return R - L - 1;
}

 6. ZigZag Conversion

    leetcode链接: https://oj.leetcode.com/problems/zigzag-conversion/

     其实是挺没意思的一道题。纯math。在纸上画一个5 row的情况。找到规律即可。注意数组下标的操作不要越界,算清楚。

public String convert(String s, int nRows) {
    if(s == null || s.length() == 0 || nRows <= 0) {
         return "";
    }
    if(nRows == 1) {
         return s;
    }
    StringBuilder result = new StringBuilder();
    int size = 2 * nRows - 2;
    for(int i = 0; i < nRows; i++) {
         for(int j = i; j < s.length(); j += size) {
               result.append(s.charAt(j));
               if(i != 0 && i != nRows - 1) {
                      int temp = j + size - 2 * i;
                      if(temp < s.length()) {
                            result.append(s.charAt(temp));
                     }
               }
         }
    }
    return result.toString();
}

7. Reverse Integer

    leetcode链接: https://oj.leetcode.com/problems/reverse-integer/

    最直观的方法是把integer变成charArray,然后把charArray给reverse。需要的额外空间会多一些。下面的方法是直接对数字进行处理。注意会有很多corner case,都是计算机语言对于数字存储的细节方面的知识了。

public int reverse(int x) {
     if(x == Integer.MIN_VALUE) {
            return 0;
    }  
    int num = Math.abs(x);
    int result = 0;
    while(num != 0) {
            if(result > (Integer.MAX_VALUE - num % 10) / 10) {
                  return 0;
            }
            result = result * 10 + num % 10;
            num /= 10;
    }
    return x > 0 ? result : -result;
}                

8. String to Integer(atoi)

     leetcode链接:https://oj.leetcode.com/problems/string-to-integer-atoi/

    没什么具体的算法思路,考的仍然是对语言细节的掌握和corner case的敏感度。

public static final int maxDivBy10 = Integer.MAX_VALUE / 10;

public int atoi(String str) {
     int i = 0;
     int n = str.length();
     while(i < n && Character.isWhitespace(str.charAt(i))) {
          i++; //regardless of the heading white space
    } 
     int sigh = 1;
     if(i < n && str.charAt(i) == '+') {
          i++;
    } else if(i < n && str.charAt(i) == '-') {
          sign = -1;
          i++;
    }
    int num = 0;
    while(i < n && Character.isDigit(str.charAt(i))) {
          int digit = Character.getNumericValue(str.charAt(i));
          if(num > maxDivBy10 || num == maxDivBy10 && digit >= 8) {
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
          }
          num = num * 10 + digit;
          i++;
    }
    return sign * num;
}  

9. Palindrome Number

leetcode链接: https://oj.leetcode.com/problems/palindrome-number/

      这个题要求不要有额外的空间。所以转化成字符串比较就行不同了。那就只能每次剥离这个数当前的首尾然后比较。为了取到第一位,我们需要首先生成一个大除数。然后开始循环判断是否palindrome,不要忘了每次大除数要自除100,因为剥离了两个数字。

public boolean isPalindrome(int x) {
     if(x < 0) {
          return false;  
     }
     int div = 1;
     while(x / div >= 10) {
          div *= 10;
     }
     while(x != 0) {
          int left = x / div;
          int right = x % 10;
          if(left  != right) {
                 return false;
          }
          x = (x % div) / 10;
          div /= 100;
    }
    return true;
}

10. Regular Expression Matching

    leetcode链接: https://oj.leetcode.com/problems/regular-expression-matching/

    recursion的方法一层一层判断。需要注意的是base case比较多。这也是由于regular expression匹配的情况比较多导致的。

public boolean isMatch(String s, String p) {
     if(p.length() == 0) {
          return s.length() == 0;
     }  
     if(p.length() == 1) {
          return (s.length() == 1) && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.');
     }
     if(p.charAt(1) != '*') {
          if(s.length() == 0) {
                return false;
          } else {
                return (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.') && isMatch(s.substring(1), p.substring(1));         
          }
    } else {
          while(s.length() > 0 && (p.charAt(0) == s.charAt(0)) || p.charAt(0) == '.')  {
                if(isMatch(s, p.substring(2))) {
                     return true;
                }
                s = s.substring(1);
          }
          return isMatch(s, p.substring(2));
    }
}
 

 

 总结leetcode的前十道题,个人感觉比较好,值得回味的应该是第2,3,4,10题。

 

11. Container with most water

   leetcode链接:https://leetcode.com/problems/container-with-most-water/

   这道题需要审题。和trap water是有区别的。trap water是高低不平的一排区间,问能存多少水。而这道题只需要考虑两条边界,中间可以认为是空的,可以存水。实际上难度大大降低。只需要左右两个pointer即可。

 1 public int maxArea(int[] height) {
 2      if(height.length <= 1) {
 3            return 0;
 4      }  
 5      int left = 0;
 6      int right = height.length - 1;
 7      int max = 0;
 8      while(left < right) {
 9            max = Math.max(max, (right - left) * Math.min(height[left], height[right]));
10            if(height[left] < height[right]) {
11                  left++;
12            }  else {
13                  right++;
14            }
15     }
16     return max;
17 }

 

14. Longest common prefix

    leetcode链接: https://leetcode.com/problems/longest-common-prefix/

public String longestCommonPrefix(String[] strs) {
     if(strs == null || strs.length == 0) {
          return "";
     }
     String prefix = strs[0];
     for(int i = 1; i < strs.length; i++) {
          int j = 0;
          for(; j < strs[i].length() && j < prefix.length(); j++) {
               if(prefix.charAt(j) != strs[i].charAt(j)) {
                    break;
               }
          }
          prefix = prefix.substring(0, j);
          if(prefix.length() == 0) {
               break;
          }
    }
    return prefix;
}

15. 3 Sum

leetcode链接: https://leetcode.com/problems/3sum/ 

其实这个3Sum的解法很直接,用3个指针追踪三个数凑出所需的数。唯一需要注意的是这道题需要一个deduplication的过程。

这个deduplication用hashset同样可以解决,不过这个解法更精炼一些。

public List<List<Integer>> threeSum(int[] num) {
     List<List<Integer>> result = new ArrayList<List<Integer>>();
     if(num.length < 3 || num == null) {
          return result;
     }  
     Arrays.sort(num);
     for(int i = 0; i < num.length - 2; i++) {
          if(i == 0 || num[i] > num > num[i - 1]) {
                int j = i + 1;
                int k = num.length - 1;
                while(j < k) {
                      if(num[j] + num[k] == -num[i]) {
                            ArrayList<Integer> list = new ArrayList<Integer>();
                            list.add(num[i]);
                            list.add(num[j]);
                            list.add(num[k]);
                            k--;
                            j++;
                            while(j < k && num[k] == num[k + 1]) {
                                 k--;
                            }
                            while(j < k && num[j] == num[j - 1]) {
                                 j++;
                            }
                      } else if(num[j] + num[k] > -num[i]) {
                            k--;
                      } else {
                            j++;
                      }
                }
          }
    }
}     

16. 3Sum Closest

leetcode链接: https://leetcode.com/problems/3sum-closest/

 先排序(很重要), 然后循环三个指针, 如果diff出现比min还小, 更新min。 

 1 public int threeSumClosest(int[] num, int target) {
 2      int result = 0;
 3      int min = Integer.MAX_VALUE;
 4      Arrays.sort(num);
 5      for(int i = 0; i < num.length; i++) {
 6           int j = i + 1;
 7           int k = num.length - 1;
 8           while (j < k) {
 9                 int sum = num[i] + num[j] + num[k];
10                 int diff = Math.abs(target - sum);
11                 if(diff == 0) {
12                       return sum;
13                 }
14                 if(diff < min) {
15                       min = diff;
16                       result = sum;
17                 }
18                 if(sum <= target) {
19                       j++;
20                 } else {
21                       k--;
22                 }
23           } 
24      } 
25      return result; 
26 }

 

 

 

 

 

  顺便推广自己的微店---美国加拿大留学申请咨询 http://weidian.com/s/316012464?wfr=c
 
 
 
 
 
 
 

转载于:https://www.cnblogs.com/jianghewade/p/4290007.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值