C++實(shí)現(xiàn)LeetCode(94.二叉樹(shù)的中序遍歷)
[LeetCode] 94. Binary Tree Inorder Traversal 二叉樹(shù)的中序遍歷
Given a binary tree, return the inorder traversal of its nodes' values.
Example:
Input: [1,null,2,3]
1
\
2
/
3Output: [1,3,2]
Follow up: Recursive solution is trivial, could you do it iteratively?
二叉樹(shù)的中序遍歷順序?yàn)樽?根-右,可以有遞歸和非遞歸來(lái)解,其中非遞歸解法又分為兩種,一種是使用棧來(lái)接,另一種不需要使用棧。我們先來(lái)看遞歸方法,十分直接,對(duì)左子結(jié)點(diǎn)調(diào)用遞歸函數(shù),根節(jié)點(diǎn)訪問(wèn)值,右子節(jié)點(diǎn)再調(diào)用遞歸函數(shù),代碼如下:
解法一:
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
inorder(root, res);
return res;
}
void inorder(TreeNode *root, vector<int> &res) {
if (!root) return;
if (root->left) inorder(root->left, res);
res.push_back(root->val);
if (root->right) inorder(root->right, res);
}
};
下面再來(lái)看非遞歸使用棧的解法,也是符合本題要求使用的解法之一,需要用棧來(lái)做,思路是從根節(jié)點(diǎn)開(kāi)始,先將根節(jié)點(diǎn)壓入棧,然后再將其所有左子結(jié)點(diǎn)壓入棧,然后取出棧頂節(jié)點(diǎn),保存節(jié)點(diǎn)值,再將當(dāng)前指針移到其右子節(jié)點(diǎn)上,若存在右子節(jié)點(diǎn),則在下次循環(huán)時(shí)又可將其所有左子結(jié)點(diǎn)壓入棧中。這樣就保證了訪問(wèn)順序?yàn)樽?根-右,代碼如下:
解法二:
// Non-recursion
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (p || !s.empty()) {
while (p) {
s.push(p);
p = p->left;
}
p = s.top(); s.pop();
res.push_back(p->val);
p = p->right;
}
return res;
}
};
下面這種解法跟 Binary Tree Preorder Traversal 中的解法二幾乎一樣,就是把結(jié)點(diǎn)值加入結(jié)果 res 的步驟從 if 中移動(dòng)到了 else 中,因?yàn)橹行虮闅v的順序是左-根-右,參見(jiàn)代碼如下:
解法三:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (!s.empty() || p) {
if (p) {
s.push(p);
p = p->left;
} else {
p = s.top(); s.pop();
res.push_back(p->val);
p = p->right;
}
}
return res;
}
};
下面來(lái)看另一種很巧妙的解法,這種方法不需要使用棧,所以空間復(fù)雜度為常量,這種非遞歸不用棧的遍歷方法有個(gè)專門(mén)的名字,叫 Morris Traversal,在介紹這種方法之前,先來(lái)引入一種新型樹(shù),叫 Threaded binary tree,這個(gè)還不太好翻譯,第一眼看上去以為是叫線程二叉樹(shù),但是感覺(jué)好像又跟線程沒(méi)啥關(guān)系,后來(lái)看到網(wǎng)上有人翻譯為螺紋二叉樹(shù),但博主認(rèn)為這翻譯也不太敢直視,很容易讓人聯(lián)想到為計(jì)劃生育做出突出貢獻(xiàn)的某世界著名品牌,后經(jīng)熱心網(wǎng)友提醒,應(yīng)該叫做線索二叉樹(shù)。先來(lái)看看維基百科上關(guān)于它的英文定義:
A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node (if it exists), and all left child pointers that would normally be null point to the inorder predecessor of the node.
就是說(shuō)線索二叉樹(shù)實(shí)際上是把所有原本為空的右子節(jié)點(diǎn)指向了中序遍歷順序之后的那個(gè)節(jié)點(diǎn),把所有原本為空的左子節(jié)點(diǎn)都指向了中序遍歷之前的那個(gè)節(jié)點(diǎn)。那么這道題跟這個(gè)線索二叉樹(shù)又有啥關(guān)系呢?由于既不能用遞歸,又不能用棧,那如何保證訪問(wèn)順序是中序遍歷的左-根-右呢。原來(lái)需要構(gòu)建一個(gè)線索二叉樹(shù),需要將所有為空的右子節(jié)點(diǎn)指向中序遍歷的下一個(gè)節(jié)點(diǎn),這樣中序遍歷完左子結(jié)點(diǎn)后,就能順利的回到其根節(jié)點(diǎn)繼續(xù)遍歷了。具體算法如下:
1. 初始化指針 cur 指向 root
2. 當(dāng) cur 不為空時(shí)
- 如果 cur 沒(méi)有左子結(jié)點(diǎn)
a) 打印出 cur 的值
b) 將 cur 指針指向其右子節(jié)點(diǎn)
- 反之
將 pre 指針指向 cur 的左子樹(shù)中的最右子節(jié)點(diǎn)
* 若 pre 不存在右子節(jié)點(diǎn)
a) 將其右子節(jié)點(diǎn)指回 cur
b) cur 指向其左子節(jié)點(diǎn)
* 反之
a) 將 pre 的右子節(jié)點(diǎn)置空
b) 打印 cur 的值
c) 將 cur 指針指向其右子節(jié)點(diǎn)
解法四:
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
if (!root) return res;
TreeNode *cur, *pre;
cur = root;
while (cur) {
if (!cur->left) {
res.push_back(cur->val);
cur = cur->right;
} else {
pre = cur->left;
while (pre->right && pre->right != cur) pre = pre->right;
if (!pre->right) {
pre->right = cur;
cur = cur->left;
} else {
pre->right = NULL;
res.push_back(cur->val);
cur = cur->right;
}
}
}
return res;
}
};
其實(shí) Morris 遍歷不僅僅對(duì)中序遍歷有用,對(duì)先序和后序同樣有用。所以對(duì)二叉樹(shù)的三種常見(jiàn)遍歷順序(先序,中序,后序)就有三種解法(遞歸,非遞歸,Morris 遍歷),總共有九段代碼呀,熟練掌握這九種寫(xiě)法才算初步掌握了樹(shù)的遍歷挖
到此這篇關(guān)于C++實(shí)現(xiàn)LeetCode(94.二叉樹(shù)的中序遍歷)的文章就介紹到這了,更多相關(guān)C++實(shí)現(xiàn)二叉樹(shù)的中序遍歷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++實(shí)現(xiàn)LeetCode(96.獨(dú)一無(wú)二的二叉搜索樹(shù))
- C++實(shí)現(xiàn)LeetCode(95.獨(dú)一無(wú)二的二叉搜索樹(shù)之二)
- C++實(shí)現(xiàn)LeetCode(93.復(fù)原IP地址)
- C++實(shí)現(xiàn)LeetCode(91.解碼方法)
- C++實(shí)現(xiàn)LeetCode(137.單獨(dú)的數(shù)字之二)
- C++實(shí)現(xiàn)LeetCode(136.單獨(dú)的數(shù)字)
- C++實(shí)現(xiàn)LeetCode(187.求重復(fù)的DNA序列)
- C++實(shí)現(xiàn)LeetCode(99.復(fù)原二叉搜索樹(shù))
相關(guān)文章
使用Qt實(shí)現(xiàn)監(jiān)聽(tīng)網(wǎng)頁(yè)是否響應(yīng)并導(dǎo)出Excel表
Qt導(dǎo)出數(shù)據(jù)到excel,方法有很多,下面這篇文章主要給大家介紹了關(guān)于使用Qt實(shí)現(xiàn)監(jiān)聽(tīng)網(wǎng)頁(yè)是否響應(yīng)并導(dǎo)出Excel表的相關(guān)資料,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11
簡(jiǎn)述C語(yǔ)言中system()函數(shù)與vfork()函數(shù)的使用方法
這篇文章主要介紹了簡(jiǎn)述C語(yǔ)言中system()函數(shù)與vfork()函數(shù)的使用方法,是C語(yǔ)言入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-08-08
C++一個(gè)函數(shù)如何調(diào)用其他.cpp文件中的函數(shù)
這篇文章主要介紹了C++一個(gè)函數(shù)如何調(diào)用其他.cpp文件中的函數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
C++11 call_once 和 once_flag的使用與區(qū)別
本文主要介紹了C++11 call_once 和 once_flag的使用與區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
基于C語(yǔ)言實(shí)現(xiàn)計(jì)算生辰八字五行的示例詳解
生辰八字,簡(jiǎn)稱八字,是指一個(gè)人出生時(shí)的干支歷日期;年月日時(shí)共四柱干支,每柱兩字,合共八個(gè)字。這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)計(jì)算生辰八字五行的示例代碼,需要的可以參考一下2023-03-03
VC++實(shí)現(xiàn)選擇排序算法簡(jiǎn)單示例
這篇文章主要介紹了VC++實(shí)現(xiàn)選擇排序算法簡(jiǎn)單示例,代碼簡(jiǎn)潔易懂,有助于讀者對(duì)數(shù)據(jù)結(jié)構(gòu)與算法的學(xué)習(xí),需要的朋友可以參考下2014-08-08
C語(yǔ)言實(shí)現(xiàn)電子郵件地址驗(yàn)證程序
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)電子郵件地址驗(yàn)證程序,利用的是POSIX正則表達(dá)式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-11-11
基于Protobuf C++ serialize到char*的實(shí)現(xiàn)方法分析
本篇文章是對(duì)Protobuf C++ serialize到char*的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05
OpenCV實(shí)現(xiàn)鼠標(biāo)框選并顯示框選區(qū)域
這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)鼠標(biāo)框選并顯示框選區(qū)域,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08

