通過設計的東西就是算法,學習算法就是學習設計。跟熟手技工同樣,算法強的有效手段,就是訓練啦,平常或工做中遇到的問題,是一個訓練,而主動去刷題,是另外一個訓練。算法
這裏就刷leetcode的題目,leecode是一個能夠進行算法答題的網站,地址是:https://leetcode.com/,前不久還出了中文版,地址是:https://leetcode-cn.com/c#
這一次,先來一個題目:
數組
對於這個題目,天然的想法,就是順着題意來,也就是判斷J中的字母在S中出現的次數。若是能判斷J中一個字母出現的次數,那全部字母均可以這樣判斷--重用的套路就出來了。數據結構
因此,按天然的想法,把J中一個字母出現次數的判斷做爲一個標準做業,使用以前小程屢次提到的「重用」的基礎套路,把各個字母出現的次數累加起來,就能夠把問題解決掉。機器學習
可是,通常天然的想法,並非高效的算法。學習
這個題目,不該該簡單地套用「重用」的套路去解決問題,你要思考更高效的設計。網站
爲了設計一個好的算法,你可能要先設計或選擇一個合適的數據結構。小刀砍樹,或大炮打蚊子,都是不合適的,但若是沒有武器裸手拆炸彈也是不合適的。這裏有一個套路,就是爲了解決一個問題,先弄一個武器出來。設計
本文介紹一個基本的算法設計套路,就是爲了解決問題而設計一個特別的數據結構。 這個套路,處處可見。爲了設計一個算法,先設計一個數據結構。 數據結構,就是數據的組織結構,必定會有本身的組織特色。code
對於這個題目,使用這個套路,那就是,要設計一個怎麼樣的數據結構,才能讓J中的字母快速地知道本身出現的次數呢(而不是跟S中的字母一個一個地配對)?blog
根據S的特色(只能是字母,區分大小寫),能夠設計一個這樣的數據結構:一個數組,數組的長度能覆蓋全部的字母的值,也就是以字母的值做爲索引必定能找到一個位置,而這個位置上的值,就是這個字母出現的次數。
若是有這樣的數組,那要找出J中字母出現的次數就很簡單,以字母的值爲索引,對應值就是出現的次數。
這樣的數組,怎麼構建起來呢?
以'z'+1的值,做爲數組的長度,加1是由於數組的索引從0開始,要讓'z'爲索引時也能找到一個位置。
數組初始時,每一個元素的值都是0。
而後,開始遍歷S,以字母值爲索引,直接找到位置,再把位置值+1。
遍歷完S後,這個數據結構就創建起來了,並且,數據結構的狀態也創建起來了,就好像通過了機器學習同樣,已是一個可用的狀態,好比下面的截圖所示:
最後,使用這個構建好的數據結構,遍歷J中的字母,直接查到這個字母出現的次數,問題便可解決。
這裏設計了一個數組來構建出一個數據結構,但這只是一個示例,只是想說明「先設計一個數據結構」的套路,至因而什麼樣的數據結構,是有得選擇的,好比對於這個問題,也能夠設計出一個hash表,用key表示S中的字母,而value爲字母出現的次數,同樣能夠優雅地解決問題。
因此,重要的是套路,而不是具體的表現,除非具體的表現已經成了一個關鍵的問題。
以上,介紹了怎麼根據特定的問題,構建一個合適的數據結構,重點是要有構建數據結構的意識。
最後,小程給出兩個實現代碼以及leecode的反饋,並結束本文的主要內容:
// c code #include <stdlib.h> #include <string.h> #include <stdio.h> int numJewelsInStones(char* J, char* S) { int arrlen = 'z'+1; char* arr = (char*)malloc(arrlen); memset(arr, 0, arrlen); for (int i = 0; i < strlen(S); i ++) { arr[S[i]] += 1; } int cnt = 0; for (int j = 0; j < strlen(J); j ++) { cnt += arr[J[j]]; } free(arr); return cnt; } int main(int argc, char *argv[]) { char *S = "aAAbbbb", *J = "aA"; int cnt = numJewelsInStones(J, S); printf("cnt=%d\n", cnt); return 0; }
把以上實現代碼提交到leecode,能夠看到這樣的反饋(執行速度很快,打敗了100%的c代碼提交,主因在於用了更多的空間):
// c# code public int NumJewelsInStones(string J, string S) { int result = 0; if (string.IsNullOrEmpty(J) || string.IsNullOrEmpty(S)) return result; var kv = new Dictionary<string, int>(); var sArr = S.ToArray(); var jArr = J.ToArray(); //S去重,統計字母出現次數 int i = 0, v = 1; while (i < sArr.Count()) { if (kv.ContainsKey(sArr[i].ToString())) { kv[sArr[i].ToString()] += v; i++; continue; } kv.Add(sArr[i].ToString(), v); i++; } //統計寶石數 foreach (var kvp in kv) { if (!J.Contains(kvp.Key)) continue; result += kvp.Value; } return result; }
把以上的實現提交到leecode,獲得反饋以下:
總結一下,本文介紹了算法設計的一個常規套路,即爲了解決問題而先設計一個數據結構。你在遇到一個問題時,應該先給本身留一點時間,進行必定的設計,而在設計算法時,應該先問問本身:是否是先設計一個數據結構呢?