複雜邏輯題目的好幫手:註釋和斷言
阿新 • • 發佈:2022-04-15
前言
最近在刷題的時候,遇到一些邏輯比較複雜的題,往往會遇到困難,經常寫不出來。即使在有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); } }