6-4 二叉树的非递归遍历 (25分)_本周小结!(二叉树)
以后每周加上一個(gè)本周小結(jié)怎么樣?
?本周小結(jié)
發(fā)現(xiàn)大家周末的時(shí)候貌似都不在學(xué)習(xí)狀態(tài),周末的文章瀏覽量和打卡情況照工作日差很多呀,可能是本周日是工作日了,周六得好好放松放松,哈哈,理解理解,但我還不能不更啊,還有同學(xué)要看呢。
所以呢,「周日我做一個(gè)針對(duì)本周的打卡留言疑問以及在刷題群里的討論內(nèi)容做一下梳理吧。」,這樣也有助于大家補(bǔ)一補(bǔ)本周的內(nèi)容,消化消化。
「注意這個(gè)周末總結(jié)和系列總結(jié)還是不一樣的(二叉樹還遠(yuǎn)沒有結(jié)束),這個(gè)總結(jié)是針對(duì)留言疑問以及刷題群里討論內(nèi)容的歸納。」
周一
本周我們開始講解了二叉樹,在關(guān)于二叉樹,你該了解這些!中講解了二叉樹的理論基礎(chǔ)。
有同學(xué)會(huì)把紅黑樹和二叉平衡搜索樹弄分開了,其實(shí)紅黑樹就是一種二叉平衡搜索樹,這兩個(gè)樹不是獨(dú)立的,所以C++中map、multimap、set、multiset的底層實(shí)現(xiàn)機(jī)制是二叉平衡搜索樹,再具體一點(diǎn)是紅黑樹。
對(duì)于二叉樹節(jié)點(diǎn)的定義,C++代碼如下:
struct?TreeNode?{????int?val;
????TreeNode?*left;
????TreeNode?*right;
????TreeNode(int?x)?:?val(x),?left(NULL),?right(NULL)?{}
};
對(duì)于這個(gè)定義中TreeNode(int x) : val(x), left(NULL), right(NULL) {} 有同學(xué)不清楚干什么的。
這是構(gòu)造函數(shù),這么說吧C語言中的結(jié)構(gòu)體是C++中類的祖先,所以C++結(jié)構(gòu)體也可以有構(gòu)造函數(shù)。
構(gòu)造函數(shù)也可以不寫,但是new一個(gè)新的節(jié)點(diǎn)的時(shí)候就比較麻煩。
例如有構(gòu)造函數(shù),定義初始值為9的節(jié)點(diǎn):
TreeNode*?a?=?new?TreeNode(9);沒有構(gòu)造函數(shù)的話就要這么寫:
TreeNode*?a?=?new?TreeNode();?a->val?=?9;?
a->left?=?NULL;
a->right?=?NULL;??
在介紹前中后序遍歷的時(shí)候,有遞歸和迭代(非遞歸),還有一種牛逼的遍歷方式:morris遍歷。
morris遍歷是二叉樹遍歷算法的超強(qiáng)進(jìn)階算法,morris遍歷可以將非遞歸遍歷中的空間復(fù)雜度降為O(1),感興趣大家就去查一查學(xué)習(xí)學(xué)習(xí),比較小眾,面試幾乎不會(huì)考。我其實(shí)也沒有研究過,就不做過多介紹了。
周二
在二叉樹:一入遞歸深似海,從此offer是路人中講到了遞歸三要素,以及前中后序的遞歸寫法。
文章中我給出了leetcode上三道二叉樹的前中后序題目,但是看完二叉樹:一入遞歸深似海,從此offer是路人,依然可以解決n叉樹的前后序遍歷,在leetcode上分別是
- 589 . N叉樹的前序遍歷
- 590 . N叉樹的后序遍歷
大家可以再去把這兩道題目做了。
周三
在二叉樹:聽說遞歸能做的,棧也能做!中我們開始用棧來實(shí)現(xiàn)遞歸的寫法,也就是所謂的迭代法。
細(xì)心的同學(xué)發(fā)現(xiàn)文中前后序遍歷空節(jié)點(diǎn)是入棧的,其實(shí)空節(jié)點(diǎn)入不入棧都差不多,但感覺空節(jié)點(diǎn)不入棧確實(shí)清晰一些,符合文中動(dòng)畫的演示。
前序遍歷空節(jié)點(diǎn)不入棧的代碼:(注意注釋部分,和文章中的區(qū)別)
class?Solution?{public:
????vector?preorderTraversal(TreeNode*?root)?{
????????stack?st;
????????vector?result;if?(root?==?NULL)?return?result;
????????st.push(root);while?(!st.empty())?{
????????????TreeNode*?node?=?st.top();???????????????????????//?中
????????????st.pop();
????????????result.push_back(node->val);if?(node->right)?st.push(node->right);???????????//?右(空節(jié)點(diǎn)不入棧)if?(node->left)?st.push(node->left);?????????????//?左(空節(jié)點(diǎn)不入棧)
????????}return?result;
????}
};
后序遍歷空節(jié)點(diǎn)不入棧的代碼:(注意注釋部分,和文章中的區(qū)別)
class?Solution?{public:
????vector?postorderTraversal(TreeNode*?root)?{
????????stack?st;
????????vector?result;if?(root?==?NULL)?return?result;
????????st.push(root);while?(!st.empty())?{
????????????TreeNode*?node?=?st.top();
????????????st.pop();
????????????result.push_back(node->val);if?(node->left)?st.push(node->left);?//?相對(duì)于前序遍歷,這更改一下入棧順序?(空節(jié)點(diǎn)不入棧)if?(node->right)?st.push(node->right);?//?空節(jié)點(diǎn)不入棧
????????}
????????reverse(result.begin(),?result.end());?//?將結(jié)果反轉(zhuǎn)之后就是左右中的順序了return?result;
????}
};
在實(shí)現(xiàn)迭代法的過程中,有同學(xué)問了:遞歸與迭代究竟誰優(yōu)誰劣呢?
從時(shí)間復(fù)雜度上其實(shí)迭代法和遞歸法差不多(在不考慮函數(shù)調(diào)用開銷和函數(shù)調(diào)用產(chǎn)生的堆棧開銷),但是空間復(fù)雜度上,遞歸開銷會(huì)大一些,因?yàn)檫f歸需要系統(tǒng)堆棧存參數(shù)返回值等等。
遞歸更容易讓程序員理解,但收斂不好,容易棧溢出。
這么說吧,遞歸是方便了程序員,難為了機(jī)器(各種保存參數(shù),各種進(jìn)棧出棧)。
「在實(shí)際項(xiàng)目開發(fā)的過程中我們是要盡量避免遞歸!因?yàn)轫?xiàng)目代碼參數(shù)、調(diào)用關(guān)系都比較復(fù)雜,不容易控制遞歸深度,甚至?xí)R绯觥!?/strong>
周四
在二叉樹:前中后序迭代方式的寫法就不能統(tǒng)一一下么?中我們使用空節(jié)點(diǎn)作為標(biāo)記,給出了統(tǒng)一的前中后序迭代法。
此時(shí)又多了一種前中后序的迭代寫法,那么有同學(xué)問了:前中后序迭代法是不是一定要統(tǒng)一來寫,這樣才算是規(guī)范。
其實(shí)沒必要,還是自己感覺哪一種更好記就用哪種。
但是「一定要掌握前中后序一種迭代的寫法,并不因?yàn)槟撤N場景的題目一定要用迭代,而是現(xiàn)場面試的時(shí)候,面試官看你順暢的寫出了遞歸,一般會(huì)進(jìn)一步考察能不能寫出相應(yīng)的迭代。」
周五
在二叉樹:層序遍歷登場!中我們介紹了二叉樹的另一種遍歷方式(圖論中廣度優(yōu)先搜索在二叉樹上的應(yīng)用)即:層序遍歷。
看完這篇文章,去leetcode上怒刷五題,文章中 編號(hào)107題目的樣例圖放錯(cuò)了(原諒我匆忙之間總是手抖),但不影響大家理解。
只有同學(xué)發(fā)現(xiàn)leetcode上“515. 在每個(gè)樹行中找最大值”,也是層序遍歷的應(yīng)用,依然可以分分鐘解決,所以就是一鼓作氣解決六道了,哈哈。
「層序遍歷遍歷相對(duì)容易一些,只要掌握基本寫法(也就是框架模板),剩下的就是在二叉樹每一行遍歷的時(shí)候做做邏輯修改。」
周六
在二叉樹:你真的會(huì)翻轉(zhuǎn)二叉樹么?中我們把翻轉(zhuǎn)二叉樹這么一道簡單又經(jīng)典的問題,充分的剖析了一波,相信就算做過這道題目的同學(xué),看完本篇之后依然有所收獲!
「文中我指的是遞歸的中序遍歷是不行的,因?yàn)槭褂眠f歸的中序遍歷,某些節(jié)點(diǎn)的左右孩子會(huì)翻轉(zhuǎn)兩次。」
如果非要使用遞歸的中序的方式寫,也可以,如下代碼就可以避免節(jié)點(diǎn)左右孩子翻轉(zhuǎn)兩次的情況:
class?Solution?{public:
????TreeNode*?invertTree(TreeNode*?root)?{
????????if?(root?==?NULL)?return?root;
????????invertTree(root->left);?????????//?左
????????swap(root->left,?root->right);??//?中
????????invertTree(root->left);?????????//?注意?這里依然要遍歷左孩子,因?yàn)橹虚g節(jié)點(diǎn)已經(jīng)翻轉(zhuǎn)了
????????return?root;
????}
};
代碼雖然可以,但這畢竟不是真正的遞歸中序遍歷了。
但使用迭代方式統(tǒng)一寫法的中序是可以的。
代碼如下:
class?Solution?{public:
????TreeNode*?invertTree(TreeNode*?root)?{
????????stack?st;if?(root?!=?NULL)?st.push(root);while?(!st.empty())?{
????????????TreeNode*?node?=?st.top();if?(node?!=?NULL)?{
????????????????st.pop();if?(node->right)?st.push(node->right);??//?右
????????????????st.push(node);??????????????????????????//?中
????????????????st.push(NULL);if?(node->left)?st.push(node->left);????//?左
????????????}?else?{
????????????????st.pop();
????????????????node?=?st.top();
????????????????st.pop();
????????????????swap(node->left,?node->right);??????????//?節(jié)點(diǎn)處理邏輯
????????????}
????????}return?root;
????}
};
為什么這個(gè)中序就是可以的呢,因?yàn)檫@是用棧來遍歷,而不是靠指針來遍歷,避免了遞歸法中翻轉(zhuǎn)了兩次的情況,大家可以畫圖理解一下,這里有點(diǎn)意思的。
總結(jié)
「本周我們都是講解了二叉樹,從理論基礎(chǔ)到遍歷方式,從遞歸到迭代,從深度遍歷到廣度遍歷,最后再用了一個(gè)翻轉(zhuǎn)二叉樹的題目把我們之前講過的遍歷方式都串了起來。」
下周依然是二叉樹,大家加油!
在留言區(qū)留下你的思路吧!
-------end-------
我將算法學(xué)習(xí)相關(guān)的資料已經(jīng)整理到了Github :https://github.com/youngyangyang04/leetcode-master,里面還有l(wèi)eetcode刷題攻略、各個(gè)類型經(jīng)典題目刷題順序、思維導(dǎo)圖看一看一定會(huì)有所收獲,如果給你有幫助給一個(gè)star支持一下吧!
另外因?yàn)楣娞?hào)改版,時(shí)間線被打亂,一些精彩文章大家可能錯(cuò)過了。如果感覺這里的文章對(duì)你有幫助,趕緊給「代碼隨想錄」加一個(gè)星標(biāo)吧,方便第一時(shí)間閱讀文章。往期精彩回顧二叉樹:你真的會(huì)翻轉(zhuǎn)二叉樹么?二叉樹:層序遍歷登場!二叉樹:前中后序迭代方式的寫法就不能統(tǒng)一一下么?二叉樹:聽說遞歸能做的,棧也能做!二叉樹:一入遞歸深似海,從此offer是路人關(guān)于二叉樹,你該了解這些!「代碼隨想錄」期待你的關(guān)注!每天8:35準(zhǔn)時(shí)推送一道經(jīng)典算法題目,推送的每道題目都不是孤立的,而是由淺入深,環(huán)環(huán)相扣,幫你梳理算法知識(shí)脈絡(luò),輕松學(xué)算法!
刷題可以加我微信!右邊為個(gè)人微信,添加時(shí)備注:「簡單自我介紹」+「組隊(duì)刷題」我就知道你[在看]總結(jié)
以上是生活随笔為你收集整理的6-4 二叉树的非递归遍历 (25分)_本周小结!(二叉树)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python数学公式pdf文件的转换_p
- 下一篇: 集水井盖板图集07fj02_【干货】住宅