目录
心得總結
完結了,整整六十幾天基本上每天不是在刷題就是在刷題的路上
要說我最大的感想是甚麼,那就是第一次如此完整的刷題,對於算法的知識點也有了一次總體性的瞭解。
也利用這次機會學習了c++,也認知到自己有哪些地方仍不熟悉。
但長路漫漫仍需砥礪前行,這第一刷只是讓我對於刷題會用到的工具有了初步的認識,但我不認為我目前可以熟練地使用這些工具,比如說KMP算法、二叉數、回溯算法、01背包等等等這些解題思路只能說我有印象,但實際要使用仍需要不斷的去使用去熟練,
如果你正在刷題或者是刷題前要看一下前人的心得,我想告訴你
💡 長路漫漫,你我一起砥礪前行,加油!
各個章節簡易回顧
第一章-數組
- 區間定義要明確,處理規則要統一
- 在這幾次的寫題當中,其實感受很清楚,就是要把定義釐清,這樣對於寫題上其實會更輕鬆,前期思考的時間花的長,後續實作其實跟著思路走很快就出來了
- 雙指針
- 不論是二分法、滑動窗口、快慢指針,就是根據不同題目的調性,去靈活運用兩個指針彼此的關係,回到第一點,只要定義清楚了,其實雙指針的做法很快就可以根據自己的思路進行設計。
第二章-鏈表
- DummyHead非常重要,這個操作可以讓很多事情變得更加容易
- 雙指針非常重要,不論是206.反转链表 還是142.环形链表II,都是解題的關鍵
- 在操作鏈表時,要注意會不會在操作中因為順序或者是沒有儲存到節點,導致操作出現問題,就像24. 两两交换链表中的节点 如果沒有先將cur→next (A)以及 cur→next→next→next(C)的位置先保存,會導致操作步驟出現問題
- 在寫題時,要注意非法index的產生,為了避免這個狀況,在****707.设计链表、****19.删除链表的倒数第N个节点都有做相應的操作
第三章-雜湊表
使用雜湊表的目的就是可以快速判斷一個元素是否存在在集合裡
至於雜湊表中的雜湊函數與碰撞的處理,我在之前的這篇文章中就有做詳細介紹了,就不多做贅述。
主要針對雜湊表三個常見的數據結構來做總結
數組
在數據量比較小的狀態下,我可以使用數組來實現hashtable 而裡面的hashfunction我可以自己設計,就像是有效的字母异位词 可以用"-'a'" (類似hashfunction)來找到對應的hashvalue,並存入count值
Set
在使用set的狀況下,跟數組狀況很像,但數據量太大,使用array會佔用太多內存空間 所以使用set,在unordered_set中,底層會對數值進行hash運算後,找出對應的下標存入我們的值(?這裡我搞不太 懂set的底層邏輯),因為數值不能重複,類似於數組中我們不存count,是固定存"1"
Map
在使用map的狀況,主要是因為我們需要對兩個值進行索引 所以在使用在unordered_map中,使用狀況是,需要進行key-value的對應,可以設定key值是多少,map會對key值進行hash計算,得出hashvalue,並存入key的對應value到hashvalue的位置,
第四章-字符串
- 庫函數的使用
在字符串的學習當中,因為之前都是碰C,所以對於C++的庫函數都很不熟悉,比如最基礎的swap以及reverse都不太熟悉,但是正因為這個事情,所以我比較少去依賴庫函數,常常會想自己做,不過這個過程中,也是真的很有趣,比如說反轉字串,我學到了還可以用位運算進行字串反轉,真的是驚到我了
- 雙指針法
在學習反轉字串的時候,雙指針法真的很好用,其實廣義來說KMP也是用到兩個指針,來進行變換,讓我了解到,不是有越多工具越好,有時可以把一項精典工具用精,就可以很容易的去處理
- 花式反轉
在反轉字符串ll 以及翻轉字符串的單詞,以及左旋轉字符串,都讓我感到很好玩,原來程式可以這麼花式,並且我在跟其他群組的小夥伴討論左旋轉字符串時,我還了解到這個如果要深究還可以牽涉到某個數論,真的非常有趣
- KMP
我不敢說我自己已經了解了,在學習KMP的過程當中,其實碰到很多困難,但就是靜下心來去畫圖,重複看好幾片影片以及小夥伴提供的資料去輔助,我感覺到後面寫起來是知道為甚麼這一段CODE要這麼寫,而不是照著模板,這樣的學習真的很讓人滿足
- 總結
字符串就像卡哥說的,想法很簡單,但實現起來其實有很多眉眉角角要進行處理,雙指針法好好用,KMP法要想辦法更加了解其精隨,學習字符串的過程中,感覺到最多的不是困難,而是有趣,尤其是自己想盡辦法思考出來的思路,假設有一點點跟提解接近,那就會很開心了。
第五章-棧與隊列
Stack
- 理論
先進後出,想像羽球桶,最先放進去的最後拿到
- 經典問題
在處理Stack的問題時,常常會感覺就是在玩對對碰,只是每次的對對碰規則都有一點點差異比如說
- 括號匹配
- 碰到匹配的括號就消除
- 字符串去重
- 碰到一樣的字元就消除
- 逆波蘭表達式
- 每碰到一個運算符,就要將前兩個數值處理成一個放回Stack,由兩個消成一個
玩起來都蠻有趣的,並且在卡哥的總結也有提到linux系統當中"cd"這個command也是一個stack經典應用
還有當我們遇到系統崩潰時,stack也會依序pop出系統資訊協助我們debug,stack真的隱身在我們的身邊當中
Queue
- 理論
先進先出,想嚇成珍珠奶茶的大吸管,先進到吸管的珍珠一定先進入嘴巴
- 特殊Queue形式
- 單調Queue
- 維護Queue只是遞增或是遞減,就像在239. 滑動窗口的最大值所應用的,也因為這樣單調的特性,可以讓我們在維護時非常單純
- Priority Queue
- 本質就是一個樹,透過最大值在最上面或最小值在最上面,來讓我們可以快速地找出前幾大或前幾小的元素,就像347. 前k個高頻元素
- 但比較好玩的是,如果是最大值在最上面,求到的就是前幾小的數值,因為假設只維護k個,那最大的數值就會先被pop出去,反之亦然。
- 單調Queue
總結
Stack 跟 Queue 在C++的學習當中,終於有點窺見這個資料結構的好玩之處,之前因為都使用C,所以需要對於各種結構自己手做,抗性就比較大,但C++可以讓我先抽離最底層,從上層的視角去對這兩個資料結構進行操作,操作完真的感覺很有趣,思考的過程也被多次啟發
第六章-二叉樹
在二叉樹章節當中,我不敢說自己完全清晰了,但對於之前的我來說,已經是個巨大的進步,之前看到二叉樹只在前中後序遍歷就停下腳步了,沒有想過自己可以把這個部分完整的跑過一次,對於二叉樹的概念也有比較清晰的感覺了,只是接下來還是要對一些項目進行釐清
- 二叉樹
- 前中後序的迭代法與統一法
- 翻轉與對稱
- 回朔
- 前中後序
- 高度與深度
- 搜索樹是單調遞增 並使用中序遍歷
- 最小公共節點
- 最小公共節點 by BST
- 普通二叉樹的刪除
第七章-回溯
感想
回溯算法整體做完後,其實在做的過程中基本上都離不開回溯法的模板,大致都大同小異
其實更多的是對於資料結構的考察熟悉程度,以及如何去看到問題的狀態。
最後想像遞迴的過程中如何嵌套循環與如何想像這個樹形結構長甚麼模樣。
回溯法模板
void backtracking(參數) {
if (終止條件) {
存放結果;
return;
}
for(選擇: 本層有多少元素){
處理節點;
backtracking(路徑,選擇列表) \\\\遞迴
回溯,撤銷處理結果
}
return;
}
對於回溯的算法,有幾個卡哥提到的回溯算法的本質問題
- 如何理解回溯法的搜索过程? for循環式本層搜索、遞迴是縱向搜索(用遞迴來嵌套for循環)
- 什么时候用startIndex,什么时候不用? 集合之間是否會在意互相影響,如果會,就要使用startIndex
- 如何去重?如何理解"树枝去重"与"树层去重"? 樹枝可以想像在遞迴過程中,我跟上一層是否一致,樹層可以理解為我在for迴圈中,前後是否一致,使用used會更好理解
- 去重的几种方法? 兩種 一種是used作法另一種是startIndex作法
- 如何理解二维递归? 可以想像原本一次只要在一行中進行操作,但在解數獨這個題目當中,要操作的就不僅僅只是一行,而是要遞迴整個棋盤,可以想像要輸出一個九宮格,一次只輸出一個數字,我們無法做出九宮格,因為一層for迴圈只能控制一個維度,需要兩個for迴圈去控制兩個維度。
有些問題卡在自己對於C++的數據結構不熟悉,有些題目卡在觀念不熟悉,但在一刷時可以完整地做完這些題目,很明顯感覺到自己的思考更加清晰了。
第八章-貪心
贪心理论基础
回顧貪心算法,真的沒有固定的解法,可能有類似的套路,比如說重疊區間,或者是子序和,但整體還是一個思路上的轉換
貪心很簡單,只是常識嗎
貪心算法很簡單,主要體現在代碼上,但難點主要是思路上的轉換,說簡單也不簡單
貪心算法有沒有套路
沒有套路!沒有套路!沒有套路
真的是要讓自己的視野打開,多寫多練習,讓自己的腦袋瘋狂運轉,會越來越好的
怎麼辨認出貪心算法
其實任何情況下只要能推導出局部最優在堆疊到全局最優的題目都可以是貪心算法,但有些問題當然可以套用其他的解題技巧幫忙,貪心算法我認為就像是心法,它沒有招式但所以我們只能意會,很奇妙的章節。
第九章-動態規劃
動態規劃五部曲
-
定義DP數組以及下標的含意
如果想不清楚dp数组的具体含义,递归公式从何谈起,甚至初始化的时候就写错了。
dp[i][j] i 代表甚麼意思,j代表甚麼意思
dp[i] i 代表甚麼意思,元素代表甚麼意思
-
遞推公式
-
根據遞推公式,確定DP數組如何初始化
例如**动态规划:不同路径还不够,要有障碍! (opens new window)**在这道题目中,初始化才是重头戏
-
確定遍歷順序
如果看过背包系列,特别是完全背包,那么两层for循环先后顺序绝对可以搞懵很多人,反而递归公式是简单的。
-
打印dp數組
至于推导dp数组的重要性,动规专题里几乎每篇Carl都反复强调,当程序结果不对的时候,一定要自己推导公式,看看和程序打印的日志是否一样。
誤區
遞推公式不該是過於關注的點,它只是解題的一部分,別讓自己在解題時處於黑盒狀態
動態規劃與貪心算法的差別
動態規劃每一個狀態一定是由上一個狀態推導出來,而貪心則是從局部選最優堆疊成全局最優
靈魂三問
- 这道题目我举例推导状态转移公式了么?
- 我打印dp数组的日志了么?
- 打印出来了dp数组和我想的一样么?
第十章-單調棧
單調棧,就是當要尋找一個元素左邊或者右邊第一個比自己大的元素位置,就可以考慮使用單調棧。
其本質是使用空間換時間,在遍歷的過程中使用棧來記錄右邊比當前元素高或低,優點就是数組只需要遍歷一次
使用單調棧時需要明確
- 單調棧裡存放的元素是甚麼
- 單調棧是遞增還是遞減,(順序方面因人而異,以我來說,就是跟著卡哥的順序,從棧頂到棧底,逐步遞增或遞減)