?
二叉樹的統(tǒng)一迭代法
此時(shí)我們?cè)?a href="http://wenjunhu.com/outside?redirect=https://mp.weixin.qq.com/s?__biz=MzUxNjY5NTYxNA==&mid=2247490808&idx=2&sn=3fd8757fc1b9311c1de87dab4b6e2881&scene=21#wechat_redirect" target="_blank">二叉樹:一入遞歸深似海,從此offer是路人中用遞歸的方式,實(shí)現(xiàn)了二叉樹前中后序的遍歷。
在二叉樹:聽說(shuō)遞歸能做的,棧也能做!中用棧實(shí)現(xiàn)了二叉樹前后中序的迭代遍歷(非遞歸)。
之后我們發(fā)現(xiàn)迭代法實(shí)現(xiàn)的先中后序,其實(shí)風(fēng)格也不是那么統(tǒng)一,除了先序和后序,有關(guān)聯(lián),中序完全就是另一個(gè)風(fēng)格了,一會(huì)用棧遍歷,一會(huì)又用指針來(lái)遍歷。
實(shí)踐過(guò)的同學(xué),也會(huì)發(fā)現(xiàn)使用迭代法實(shí)現(xiàn)先中后序遍歷,很難寫出統(tǒng)一的代碼,不像是遞歸法,實(shí)現(xiàn)了其中的一種遍歷方式,其他兩種只要稍稍改一下節(jié)點(diǎn)順序就可以了。
其實(shí)針對(duì)三種遍歷方式,使用迭代法是可以寫出統(tǒng)一風(fēng)格的代碼!
重頭戲來(lái)了,接下來(lái)介紹一下統(tǒng)一寫法。
我們以中序遍歷為例,在二叉樹:聽說(shuō)遞歸能做的,棧也能做!中提到說(shuō)使用棧的話,無(wú)法同時(shí)解決訪問(wèn)節(jié)點(diǎn)(遍歷節(jié)點(diǎn))和處理節(jié)點(diǎn)(將元素放進(jìn)結(jié)果集)不一致的情況。
那我們就將訪問(wèn)的節(jié)點(diǎn)放入棧中,把要處理的節(jié)點(diǎn)也放入棧中但是要做標(biāo)記。
如何標(biāo)記呢,就是要處理的節(jié)點(diǎn)放入棧之后,緊接著放入一個(gè)空指針作為標(biāo)記。?這種方法也可以叫做標(biāo)記法。
迭代法中序遍歷
中序遍歷代碼如下:(詳細(xì)注釋)
class?Solution?{
public:
????vector<int>?inorderTraversal(TreeNode*?root)?{
????????vector<int>?result;
????????stack?st;
????????if?(root?!=?NULL)?st.push(root);
????????while?(!st.empty())?{
????????????TreeNode*?node?=?st.top();
????????????if?(node?!=?NULL)?{
????????????????st.pop();?//?將該節(jié)點(diǎn)彈出,避免重復(fù)操作,下面再將右中左節(jié)點(diǎn)添加到棧中
????????????????if?(node->right)?st.push(node->right);??//?添加右節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????????st.push(node);??????????????????????????//?添加中節(jié)點(diǎn)
????????????????st.push(NULL);?//?中節(jié)點(diǎn)訪問(wèn)過(guò),但是還沒(méi)有處理,加入空節(jié)點(diǎn)做為標(biāo)記。
????????????????if?(node->left)?st.push(node->left);????//?添加左節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????}?else?{?//?只有遇到空節(jié)點(diǎn)的時(shí)候,才將下一個(gè)節(jié)點(diǎn)放進(jìn)結(jié)果集
????????????????st.pop();???????????//?將空節(jié)點(diǎn)彈出
????????????????node?=?st.top();????//?重新取出棧中元素
????????????????st.pop();
????????????????result.push_back(node->val);?//?加入到結(jié)果集
????????????}
????????}
????????return?result;
????}
};
看代碼有點(diǎn)抽象我們來(lái)看一下動(dòng)畫(中序遍歷):
中序遍歷迭代(統(tǒng)一寫法)動(dòng)畫中,result數(shù)組就是最終結(jié)果集。
可以看出我們將訪問(wèn)的節(jié)點(diǎn)直接加入到棧中,但如果是處理的節(jié)點(diǎn)則后面放入一個(gè)空節(jié)點(diǎn), 這樣只有空節(jié)點(diǎn)彈出的時(shí)候,才將下一個(gè)節(jié)點(diǎn)放進(jìn)結(jié)果集。
此時(shí)我們?cè)賮?lái)看前序遍歷代碼。
迭代法前序遍歷
迭代法前序遍歷代碼如下:(注意此時(shí)我們和中序遍歷相比僅僅改變了兩行代碼的順序)
class?Solution?{
public:
????vector<int>?preorderTraversal(TreeNode*?root)?{
????????vector<int>?result;
????????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);??//?右
????????????????if?(node->left)?st.push(node->left);????//?左
????????????????st.push(node);??????????????????????????//?中
????????????????st.push(NULL);
????????????}?else?{
????????????????st.pop();
????????????????node?=?st.top();
????????????????st.pop();
????????????????result.push_back(node->val);
????????????}
????????}
????????return?result;
????}
};
迭代法后序遍歷
后續(xù)遍歷代碼如下:(注意此時(shí)我們和中序遍歷相比僅僅改變了兩行代碼的順序)
class?Solution?{
public:
????vector<int>?postorderTraversal(TreeNode*?root)?{
????????vector<int>?result;
????????stack?st;
????????if?(root?!=?NULL)?st.push(root);
????????while?(!st.empty())?{
????????????TreeNode*?node?=?st.top();
????????????if?(node?!=?NULL)?{
????????????????st.pop();
????????????????st.push(node);??????????????????????????//?中
????????????????st.push(NULL);
????????????????if?(node->right)?st.push(node->right);??//?右
????????????????if?(node->left)?st.push(node->left);????//?左
????????????}?else?{
????????????????st.pop();
????????????????node?=?st.top();
????????????????st.pop();
????????????????result.push_back(node->val);
????????????}
????????}
????????return?result;
????}
};
總結(jié)
此時(shí)我們寫出了統(tǒng)一風(fēng)格的迭代法,不用在糾結(jié)于前序?qū)懗鰜?lái)了,中序?qū)懖怀鰜?lái)的情況了。
但是統(tǒng)一風(fēng)格的迭代法并不好理解,而且想在面試直接寫出來(lái)還有難度的。
所以大家根據(jù)自己的個(gè)人喜好,對(duì)于二叉樹的前中后序遍歷,選擇一種自己容易理解的遞歸和迭代法。
其他語(yǔ)言版本
Java:迭代法前序遍歷代碼如下:
class?Solution?{
????public?List?preorderTraversal(TreeNode?root)? {
????????List?result?=?new?LinkedList<>();
????????Stack?st?=?new?Stack<>();
????????if?(root?!=?null)?st.push(root);
????????while?(!st.empty())?{
????????????TreeNode?node?=?st.peek();
????????????if?(node?!=?null)?{
????????????????st.pop();?//?將該節(jié)點(diǎn)彈出,避免重復(fù)操作,下面再將右中左節(jié)點(diǎn)添加到棧中
????????????????if?(node.right!=null)?st.push(node.right);??//?添加右節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????????if?(node.left!=null)?st.push(node.left);????//?添加左節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????????st.push(node);??????????????????????????//?添加中節(jié)點(diǎn)
????????????????st.push(null);?//?中節(jié)點(diǎn)訪問(wèn)過(guò),但是還沒(méi)有處理,加入空節(jié)點(diǎn)做為標(biāo)記。
????????????????
????????????}?else?{?//?只有遇到空節(jié)點(diǎn)的時(shí)候,才將下一個(gè)節(jié)點(diǎn)放進(jìn)結(jié)果集
????????????????st.pop();???????????//?將空節(jié)點(diǎn)彈出
????????????????node?=?st.peek();????//?重新取出棧中元素
????????????????st.pop();
????????????????result.add(node.val);?//?加入到結(jié)果集
????????????}
????????}
????????return?result;
????}
}
迭代法中序遍歷代碼如下:
class?Solution?{
public?List?inorderTraversal(TreeNode?root)? {
????????List?result?=?new?LinkedList<>();
????Stack?st?=?new?Stack<>();
????if?(root?!=?null)?st.push(root);
????while?(!st.empty())?{
????????TreeNode?node?=?st.peek();
????????if?(node?!=?null)?{
????????????st.pop();?//?將該節(jié)點(diǎn)彈出,避免重復(fù)操作,下面再將右中左節(jié)點(diǎn)添加到棧中
????????????if?(node.right!=null)?st.push(node.right);??//?添加右節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????st.push(node);??????????????????????????//?添加中節(jié)點(diǎn)
????????????st.push(null);?//?中節(jié)點(diǎn)訪問(wèn)過(guò),但是還沒(méi)有處理,加入空節(jié)點(diǎn)做為標(biāo)記。
????????????if?(node.left!=null)?st.push(node.left);????//?添加左節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????}?else?{?//?只有遇到空節(jié)點(diǎn)的時(shí)候,才將下一個(gè)節(jié)點(diǎn)放進(jìn)結(jié)果集
????????????st.pop();???????????//?將空節(jié)點(diǎn)彈出
????????????node?=?st.peek();????//?重新取出棧中元素
????????????st.pop();
????????????result.add(node.val);?//?加入到結(jié)果集
????????}
????}
????return?result;
}
}
迭代法后序遍歷代碼如下:
class?Solution?{
???public?List?postorderTraversal(TreeNode?root)? {
????????List?result?=?new?LinkedList<>();
????????Stack?st?=?new?Stack<>();
????????if?(root?!=?null)?st.push(root);
????????while?(!st.empty())?{
????????????TreeNode?node?=?st.peek();
????????????if?(node?!=?null)?{
????????????????st.pop();?//?將該節(jié)點(diǎn)彈出,避免重復(fù)操作,下面再將右中左節(jié)點(diǎn)添加到棧中
????????????????st.push(node);??????????????????????????//?添加中節(jié)點(diǎn)
????????????????st.push(null);?//?中節(jié)點(diǎn)訪問(wèn)過(guò),但是還沒(méi)有處理,加入空節(jié)點(diǎn)做為標(biāo)記。
????????????????if?(node.right!=null)?st.push(node.right);??//?添加右節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????????if?(node.left!=null)?st.push(node.left);????//?添加左節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)?????????
???????????????????????????????
????????????}?else?{?//?只有遇到空節(jié)點(diǎn)的時(shí)候,才將下一個(gè)節(jié)點(diǎn)放進(jìn)結(jié)果集
????????????????st.pop();???????????//?將空節(jié)點(diǎn)彈出
????????????????node?=?st.peek();????//?重新取出棧中元素
????????????????st.pop();
????????????????result.add(node.val);?//?加入到結(jié)果集
????????????}
????????}
????????return?result;
???}
}
迭代法前序遍歷:
class?Solution:
????def?preorderTraversal(self,?root:?TreeNode)?->?List[int]:
????????result?=?[]
????????st=?[]
????????if?root:
????????????st.append(root)
????????while?st:
????????????node?=?st.pop()
????????????if?node?!=?None:
????????????????if?node.right:?#右
????????????????????st.append(node.right)
????????????????if?node.left:?#左
????????????????????st.append(node.left)
????????????????st.append(node)?#中
????????????????st.append(None)
????????????else:
????????????????node?=?st.pop()
????????????????result.append(node.val)
????????return?result
迭代法中序遍歷:
class?Solution:
????def?inorderTraversal(self,?root:?TreeNode)?->?List[int]:
????????result?=?[]
????????st?=?[]
????????if?root:
????????????st.append(root)
????????while?st:
????????????node?=?st.pop()
????????????if?node?!=?None:
????????????????if?node.right:?#添加右節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????????????st.append(node.right)
????????????????
????????????????st.append(node)?#添加中節(jié)點(diǎn)
????????????????st.append(None)?#中節(jié)點(diǎn)訪問(wèn)過(guò),但是還沒(méi)有處理,加入空節(jié)點(diǎn)做為標(biāo)記。
????????????????
????????????????if?node.left:?#添加左節(jié)點(diǎn)(空節(jié)點(diǎn)不入棧)
????????????????????st.append(node.left)
????????????else:?#只有遇到空節(jié)點(diǎn)的時(shí)候,才將下一個(gè)節(jié)點(diǎn)放進(jìn)結(jié)果集
????????????????node?=?st.pop()?#重新取出棧中元素
????????????????result.append(node.val)?#加入到結(jié)果集
????????return?result
迭代法后序遍歷:
class?Solution:
????def?postorderTraversal(self,?root:?TreeNode)?->?List[int]:
????????result?=?[]
????????st?=?[]
????????if?root:
????????????st.append(root)
????????while?st:
????????????node?=?st.pop()
????????????if?node?!=?None:
????????????????st.append(node)?#中
????????????????st.append(None)
????????????????
????????????????if?node.right:?#右
????????????????????st.append(node.right)
????????????????if?node.left:?#左
????????????????????st.append(node.left)
????????????else:
????????????????node?=?st.pop()
????????????????result.append(node.val)
????????return?result
?
?審核編輯 :李倩
評(píng)論
查看更多