4,449 人赞同了该回答

    先贴一个自己的刷题的进度吧:

    如何刷 LeetCode 的? - 知乎 - 图1

    到目前为止,一共刷了 428 题。

    通过刷题,拿了很多公司(IBM, Google, Amazon, Microsoft, Zenefits, Splunk)的面试以及 offer。这 428 题不是简单的刷一遍就过去的,而是反复练习,直到代码最优,解法最优(有时候甚至觉得自己的代码精简到一个符号都无法减少的地步)。所以有时候面试官问问题,问题还没说完,我就知道应该如何表述自己的心路历程,然后慢慢地给出最优解。

    而这一切的关键就在于:做笔记!

    下面是我的笔记截图:

    如何刷 LeetCode 的? - 知乎 - 图2

    对于遇到的每个题目,事后我都做上标记:普通题目,难题、好题。此外,每个题目都分为以下几个步骤做好详细的笔记:

    1. 原题目
    2. 自己的第一遍解法
    3. 网上好的解法
    4. 自己可以改进的地方
    5. 进一步精简优化自己的代码直至代码简无可简(这是非常关键的一步,到达这一步,才会发现获得能力的提升远远要超过简单地把题目解出来
    6. 获得的思考(或者学习到的地方,可以是算法、数据结构或者 Java 的特性—例如 Stream 等等)

    每一个题目都经过至少一遍这样的迭代。这样几遍下来,我对于每个题目都有了更加深刻的理解,大部分的题目我都有自信能够写出最优解甚至代码都是最优化的(至少比论坛回复里面的最高票答案还要精简)。

    举个例子,Two Sum问题。

    我最早的解法是暴力搜索。当时的代码 (C++) 是这样的:

    1. class Solution {
    2. public:
    3. vector<int> twoSum(vector<int> &numbers, int target) {
    4. // Start typing your C/C++ solution below
    5. // DO NOT write int main() function
    6. vector<int> index(2, 0);
    7. if (numbers.empty() || numbers.size() == 1)
    8. return index;
    9. for (int i = 0; i < numbers.size()-1; ++i) {
    10. // j should start from i+1
    11. for (int j = i+1; j < numbers.size(); ++j) {
    12. if (numbers[i] + numbers[j] == target) {
    13. index.clear();
    14. index.push_back(i+1);
    15. index.push_back(j+1);
    16. break;
    17. }
    18. }
    19. }
    20. return index;
    21. }
    22. }

    这个解法不仅复杂度高,而且代码冗长繁琐。

    后来看了网上高票答案的解法,知道了用 hashmap 来做,于是写出了优化的代码 (Java):

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

    再后来,对代码进行了一些细节的简化:

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

    至此,代码几乎达到最精简状态。(中间有略去几次迭代)总之,不断地学习别人的代码,改进自己的代码,不断地锤炼自己的代码,直至算法最优化,代码最简洁!潜移默化中,不仅对题目解法有了更深刻的理解(什么是最优解),而且也知道如何用最简洁的代码实现这个最优解。

    再举个极端的例子吧,179. Largest Number,这个题目我最后精简成的代码如下:

    public String largestNumber(int[] nums) {
    
        return Arrays.stream(nums)
    
        .mapToObj(String::valueOf)
    
        .sorted((s1, s2) -> (s2 + s1).compareTo(s1 + s2))
    
        .reduce((s1, s2) -> s1.equals("0") ? s2 : s1 + s2).get();
    
    }
    

    我本人不是算法高手,算是勤能补拙类型。这样长期坚持下来,慢慢地感觉自己编程能力提升了很多。不仅面试的时候得心应手,而且在工作中提交 code review 的时候,往往有自信说自己的代码是简单,干净与优雅的。

    编辑于 2018-12-19

    赞同 4449198 条评论