九章演算法筆記 1.Introducing Algorithm Interview & Coding Style
Implement strStr
cs3k.com
http://www.lintcode.com/problem/strstr/
Returns the position of the first occurrence of string target in string source, or -1 if target is not part of source.
遇到這道題和麵試官說什麼,有什麼要確認的麼?cs3k.com
對效能有要求麼?
–面試常見錯誤1:我知道有個演算法叫KMP
–不應該扯KMP的原因,而應該從最簡單的演算法開始:
1.演算法往往沒那麼難
2.用那麼難得演算法,寫出不來咋辦
3.用簡單的演算法,即使不能寫全,寫到完美,至少能得一部分分
4.所以我們要寫一個O(m+n)的兩個for的傻叉演算法就可以了
面試常見錯誤2:Coding Style cs3k.com
–命名風格 s1, s2 神碼鬼?
要用source, target
–if(){
}
一行也要用花括號, c++詳見google coding style
–什麼時候加空格?
1.二元運算子左右各一個space
2.++ — 不加space
3.分號右加左不加
常見錯誤3:沒考慮corner case cs3k.com
要體現自己細心靠譜,考慮各種面試官都不一定能想到的情況:
1.檢測空(字串,集合….)
2.檢測下標越不越界:
凡是返回下標的,都要檢查越界不,萬一返回-1呢?會不會返回大於n呢?
3.要有縮排
–如果問比O(n^2)更好的演算法
Rabin-Karp演算法:基於hash function的原理
–主動給出Test case而不是uni test????區別
null和空的情況啦
你真的會面試麼? cs3k.com
–白紙上要寫一遍
lintcode上面的N/A小於3是比較好的,自己動腦debug,不要提交錯了再改
–吃透幾道題?
看答案寫出來的,過一週到兩週要自己再做一遍
–面試不會就說不會麼?
面試官是coworker, 程式設計師要有自己google然後搞定的能力
–面試官為難你?
追問是正常的,不要覺得面試官是傻逼
為什麼要面試演算法?演算法面試難麼? cs3k.com
演算法難,演算法面試不難,因為考察的範圍很窄.
–為啥這些演算法沒啥用還考?為啥不寫project呢?
1.45分鐘內完成一段程式碼
2.主要考察寫程式碼的能力,所以會考不太難想的演算法,但是實現不太容易的演算法
3.主要不是想題,而是寫程式:像貪心法這種沒有太大通用性的,考得很少
刷題的經驗:cs3k.com
–總結類似題目,一道變一類
–給一類題目弄出個模板程式
排列與組合cs3k.com
搜尋的複雜度
–構造每個答案的時間 * 答案的個數
子集這道題為:
=O( n * 2^n)
–permutation 排列
=O(n! * n)
有時答案個數不確定的時候,假設S是所有答案的個數
= O(S * n)
問時間複雜度,基本可以和麵試官交流就ok了
找子集問題cs3k.com
http://www.lintcode.com/en/problem/subsets/
Given a set of distinct integers, return all possible subsets.
Example
If S = [1,2,3]
, a solution is:[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
–從空集出發,怎樣構造?
一層一層去想的,去搜索的,這種叫做寬度優先搜尋
–深搜有什麼好處呢?
1.寬搜需要記錄上一層的狀態,而深搜只需要記錄上一個狀態。
2.如果面試官要求打印出來就行,又因為print是不佔用空間的,所以深搜空間更優。
深搜
DFS即深度優先搜尋,本質是回溯,是back tracking。
是後悔的過程,是撞了南牆後悔,再回到之前的狀態。
其中{1,3}之後不能加2,是因為不能重複,因為{1,2, 3}和{1, 3, 2}一樣
–避免重複不應該採用的方法為:
記錄所有狀態,然後回去查查,如果重了,就不加。
因為這樣的話,時間複雜度從O(n!)變成O(n^n)
–應該採用的方法為選代表:
{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,1,2} {3,2,1}
選代表的原則是:看臉,看誰帥誰順眼選誰
{1,2,3}明顯比較順眼,因為有規則的增序,所以最後的路徑就是如圖上的粉箭頭的指向。
遞迴cs3k.com
遞迴的本質是由大化小
–遞迴需要主要的問題:
遞迴是不是太深?在找子集問題上,不會太深,因為解一共有(2^n)個,如果n太大,解根本解不完。所以n一定是一個很小的數,n不大,遞迴也就不會太深。
–開始new一個空的子集
並且所有函式先解決corner case,做異常檢測。
此題即解決0和null的問題。
–這道題還需要排個序,因為題目有要求
–遞迴的是一個function叫func(A)自己呼叫自己,我們在這裡構造一個helper()函式
這個helper的引數呢,需要subset和可加的nums以及傳答案的results以及為了避免重複而傳進去的最後一個數的index(可以不傳,這裡傳進去比較直觀)
遞迴三要素
1.遞迴的定義:
接受什麼引數,返回什麼值,做了什麼事兒
這裡是找到所有以subset開始的集合,然後丟到result裡面
ps:一行不要超過80到100個字元,所以引數多要分行
2.遞迴的拆解,怎麼拆成更小的?
{1} 拆成 {1, 2} {1, 3} {1, 4}
ps:soft copy 要注意,soft copy是reference
subset = [];
results.add(subset);
results.add(subset);
results.add(subset);
subset = 1;
最後results: [1] [1] [1]
所以results.add{new一個出來的deep copy}
–函式呼叫後全部刪除:
四大步驟:1. add{new一個空的出來} 2.加一個數 3.呼叫自己的小任務 4.刪這個數
3.遞迴出口(什麼時候不忘下遞迴了,可以直接找到答案退出)
–怎麼看一個題目是深搜
有all possible字樣的
寫程式不是從上往下寫,先寫思路。
重點是bug free和速度
課後小視訊之Subsets IIcs3k.com
看到all 字樣,很容易是深搜
如何去重呢? 錯誤的想法是找到所有的答案,然後再看重複與否, 去掉.
比如[1,1,1,1,1,1] 只有7個子集,但是有寫2^6個答案, 浪費時間
應該開始就不加入重複的
helper應該是private的
[1,2,2′]什麼時候我們應該避免重複呢?
遇到2’的時候,如果2’之前有2,而且2沒有在已有的集合中,此時加2′,就會重複
課後小視訊之字串查詢之 Rabin Karp 演算法 cs3k.com
這種演算法的本質是用hash function把字串之間的比較由O(m)長的時間變為O(1)
hash function
雜湊表就是個大陣列, hash function是個把字串, float, double, int都轉換到陣列一個位置的mapping.
ABCDE
= (a * 31^4 + b * 31^3 + c * 31^2 + d * 31 + e) % 10^6
其中31是個經驗值, hash size 10^6, 越大越不容易重, 越小越容易衝突, 所以選一個不越界的大的值
如果用int的話, 保證 31 * 10^6不越界.
如果用10^8的話, 就需要long int 了
key到value唯一, 反之不成立. abc 只等於123, cde也只等與123, 但是123對應兩個string
開始要處理null和length=0
22行的power要邊乘邊模,防止越界.
42行負數的情況最好不要直接模, 因為不同的語言負數的模結果不同,所以手動if一下
48行, hash code一樣串不一定一樣,hash code 不一樣的串一定不一樣, false positive, 所以再比較一次