學演算法不外乎是為了要解決一個特定範疇/模式的問題,了解在限制條件下,如何用合適的方式來找出需要的答案。
今天要來談談解題之前,我個人認為應該要有的觀念。
自動導航是什麼意思?就是看到題目/聽到題目,直接、馬上要開始寫程式碼,你第一個直覺這是 A 演算法,直接拿出背好的模式,準備套上去。我的個人建議是,以 Leetcode 為例,看你個人習慣直接用打字或紙筆(iPad之類的電子形式也行),先好好閱讀完題目的全部內容,一邊寫下幾個重要要素:輸入、輸出、限制。確定自己明確理解題目裡幾個參數之間的關係,能用簡單的方式稍微畫一下更好。也許線上競賽的時候時間很重要,但其他像是練習、面試的場合,我覺得能把自己的思路說清楚(也讓自己明白),比省那一點時間重要的多。
走過一次題目的流程後,拿題目的幾個範例來驗證一次自己的思路,確定題目想問的,是你所想的。Leetcode 現在也能自己輸入測資,如果題目測資不足以讓你確認自己想法,自己寫個測資,簡單的測試一下結果,先了解題目,才準備下筆。
我指的輸入是 public type functionA(paramA, paramB) 裡面的 paramA, paramB,輸出是指 type 這邊回傳的型別。觀察輸入輸出是能夠縮減你演算法選擇的方式之一,陣列 / 鏈表操作方式不同,在考量時間後就能刪去一些選項。
輸入輸出的型別也是要考量的點,如常見的 int 就要考慮會不會加減乘除的運算中觸碰到型別上限,透過觀察測資限制跟型別,會發現更多要注意的點。
指的是有時候在一些時候,如做比大小的時候,會需要一個初始值,初始值的設計應該是 Int32.MaxValue,或應該是 0,應該是 -1?這個要小心一點閱讀題目,確認題目的測資範圍,比如你想讓初始值為一個不可能被用到的值,後面方便覆蓋跟判斷特例,但卻沒注意到該數值有可能在題目中碰觸到,就會造成自己的運算盲點。
還有像是做區間判斷的時候的比較符號使用,左閉右開,左閉右閉,大於等於,小於等於,或者不要等於,都要確認符號的使用上不要造成可能答案的遺漏。
限制泛指題目說到的不能有重複答案,不能利用額外空間,嘗試在 O(n) 時間內解出來,需依序排列等等,一般來說在想到演算法的時候都會提示這類演算法擅長的地方,這些限制其實能夠有效幫你篩出記憶中合適做這類事情的演算法。
看完題目之後,可以以抽象的方式列一下你要做的事,把步驟寫出來。有一類題目是模擬題,沒有特別困難的算法,但要你依照題目的題意寫出過程,這類題目如果沒有把步驟先列好,而是寫一步算一步,很容易會有缺漏。抽象方式列完步驟,在依據個別步驟去做方法實現,可以幫助你從更高的層面來檢視自己想的方法是不是有問題。
寫到比較複雜的題目的時候,可以邊想邊把自己的思考過程寫下來,草草紀錄也沒關係,用於事後,不管你是自己解出來,或是去找了人家的答案來知道到底怎麼做,都可以回頭看:為什麼自己那時候想不到。這麼做是幫助你找出自己的思考盲點,是沒有列出正確的選項,漏考慮了限制,還是邊際值的考慮不合理?個人認為這是刷題路上非常重要的一個步驟,每個人都有自己比較容易漏掉的地方,清楚自己目前容易看漏的點,才能夠讓思考變的足夠全面。
寫在算法之前的這篇,很多也是我自己會有的盲點,看一忘二,看二忘三,總有遺漏,還不能做到盡善盡美。一如我最後一點提到的,怎麼發現自己的遺漏,會是這段練習路上非常重要的自我覺察。從今天開始在寫題目的時候慢一點,讓題目寫得更加紮實吧。