1. 程式人生 > 其它 >複雜邏輯題目的好幫手:註釋和斷言

複雜邏輯題目的好幫手:註釋和斷言

前言

最近在刷題的時候,遇到一些邏輯比較複雜的題,往往會遇到困難,經常寫不出來。即使在有debug幫助的時候,也往往會出現思慮不周,導致一錯再錯的情況,即好不容易debug通過一個測試用例,然後發現被另一個測試用例卡住。在周賽雙週賽中,這意味著大量的罰時;在工作中,這意味著浪費大量的時間。因此需要有一個方法來輔助思考,完成正確的分類討論。

註釋與斷言的作用

人腦的短時儲存能力很差(記憶體/運存很小),因此在處理複雜問題時往往會顧此失彼,此時就需要工具來輔助記錄思想(外存)。註釋與斷言就是起一個這樣的作用。

如何使用註釋

註釋應該給出關鍵的資訊,即一個迴圈/函式,當進入的時候,關鍵變數的值應該是什麼。當離開的時候,關鍵變數的值應該是什麼。

如何使用斷言

即便使用了註釋,也不能保證寫出的程式碼在正確的地點有正確的值,此時就需要assert來自動檢驗值是否正確。

什麼時候使用註釋與斷言

在刷題/競賽的時候,為了提高效率,可以採用以下策略:

  • 當刷題時發現自己無法完成分類討論的時候
  • 當發現自己寫的程式碼有bug的時候
    而當工作或者開發專案的時候,由於越早發現bug代價越小,因此最好從一開始就使用。

例子

以今天的每日一題LC385.迷你語法分析器為例。

如果使用棧+迭代的方法,那麼迭代變數就不會出問題,但如果使用遞迴的方法,那麼迭代變數還是容易出問題的。這種情況下,使用註釋和斷言就可以幫助思考。

/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * public interface NestedInteger {
 *     // Constructor initializes an empty nested list.
 *     public NestedInteger();
 *
 *     // Constructor initializes a single integer.
 *     public NestedInteger(int value);
 *
 *     // @return true if this NestedInteger holds a single integer, rather than a nested list.
 *     public boolean isInteger();
 *
 *     // @return the single integer that this NestedInteger holds, if it holds a single integer
 *     // Return null if this NestedInteger holds a nested list
 *     public Integer getInteger();
 *
 *     // Set this NestedInteger to hold a single integer.
 *     public void setInteger(int value);
 *
 *     // Set this NestedInteger to hold a nested list and adds a nested integer to it.
 *     public void add(NestedInteger ni);
 *
 *     // @return the nested list that this NestedInteger holds, if it holds a nested list
 *     // Return empty list if this NestedInteger holds a single integer
 *     public List<NestedInteger> getList();
 * }
 */

class Solution {
    public NestedInteger deserialize(String s) {
        char[] cs = s.toCharArray();
        return recur(cs, new int[1]);
    }

    private NestedInteger recur(char[] cs, int[] i) {
        // i[0] must be the start of a token
        if (cs[i[0]] == '[') {
            // this means it's a list
            NestedInteger result = new NestedInteger();
            i[0]++;
            while (cs[i[0]] != ']') {
                // i[0] should be the start of a token
                assert i[0] < cs.length;
                if (cs[i[0]] == ',') {
                    i[0]++; // make sure i[0] is the start of next token
                    continue;
                }
                if (cs[i[0]] == '[' && cs[i[0] + 1] == ']') {
                    // it is a list whose length is 0
                    i[0] += 2; // make sure i[0] is the start of next token
                    result.add(new NestedInteger());
                } else {
                    // it is a list with at least 1 element
                    result.add(recur(cs, i));
                    // the function will make sure i[0] is the start of next token
                }
            }
            // in the end, cs[i[0]] must be ']'
            assert cs[i[0]] == ']';
            i[0]++;  // make sure i[0] is the start of next token
            return result;
        }
        // this is an integer
        int val = 0;
        boolean isNagtive = false;
        if(cs[i[0]] == '-') {
            isNagtive = true;
            i[0]++;
        }
        while(i[0] < cs.length && cs[i[0]] != ',' && cs[i[0]] != ']') {
            assert cs[i[0]] != '[';
            val = val * 10 + cs[i[0]++] - 48;
        }
        // if (i[0] == cs.length) this is the start of next token;
        // if (cs[i[0]] == ',') this is the start of next token;
        // if (cs[i[0]] == ']') this is the end of this token, but should not be process here
        val = isNagtive ? -val : val;
        return new NestedInteger(val);
    }
}