在上節(jié)的基礎(chǔ)上终蒂,本節(jié)我們將繼續(xù)匯總一些 LeetCode 有關(guān)二叉樹的題故响。
我們還是使用上節(jié)的程序框架宏蛉,可以手動輸入樣例并測試儿咱,解題的方法不唯一庭砍。
所有題均來自于leetcode。
翻轉(zhuǎn)二叉樹
翻轉(zhuǎn)一棵二叉樹混埠。
這個題需要我們翻轉(zhuǎn)二叉樹怠缸,可以用遞歸來實現(xiàn)。
當(dāng)一個節(jié)點為空钳宪,需不需要交換都無所謂凯旭;當(dāng)其不為空時,交換該節(jié)點的左右子樹即可使套,繼續(xù)遞歸其左右子樹,只要遍歷到每一個節(jié)點就可以了鞠柄,遍歷到該節(jié)點侦高,就換交換其左右子樹,以什么方式遍歷的其實無所謂厌杜。
簡單來說就是奉呛,每一個節(jié)點做好自己的事情:交換該節(jié)點的左右子樹计螺。每個節(jié)點做好這件事,再顧及到每個節(jié)點瞧壮,二叉樹就翻轉(zhuǎn)成功了登馒。
具體的程序如下:
//226 翻轉(zhuǎn)二叉樹
BiTree invertTree(BiTree root){
if(root == NULL){
return NULL;
}
BiTree r = invertTree(root->right);
BiTree l = invertTree(root->left);
root->left = r;
root->right = l;
return root;
}
測試程序:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
tree = invertTree(tree);
levelOrder(tree);
return 0;
}
測試結(jié)果為:
4
2 7
1 3 6 9
# # # # # # # #
4 7 2 9 6 3 1
左葉子之和
計算給定二叉樹的所有左葉子之和。
這個題首先要明確什么是左葉子咆槽,節(jié)點為空陈轿,不操作,若一個節(jié)點是左葉子秦忿,取它的值相加麦射,之和明確什么時候遞歸左子樹,右子樹灯谣,這個問題就不難了潜秋。
具體程序:
// 404 左葉子之和
void sumOfLeftLeaves_helper(BiTree root,int& sum){
if(root == NULL){
sum = sum;
}else{
if(root->left != NULL){
// 左葉子的條件
if(root->left->left == NULL && root->left->right == NULL){
sum += stoi(root->left->val);
}else{
sumOfLeftLeaves_helper(root->left,sum);
}
if(root->right != NULL){
sumOfLeftLeaves_helper(root->right,sum);
}
}
}
}
int sumOfLeftLeaves(BiTree root){
int sum = 0;
sumOfLeftLeaves_helper(root,sum);
return sum;
}
測試程序:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
cout<<sumOfLeftLeaves(tree)<<endl;
return 0;
}
測試結(jié)果:
3
9 20
# # 15 7
# # # #
24
二叉樹的中序遍歷
給定一個二叉樹,返回它的中序遍歷胎许。
這個峻呛,我在之前的博客《二叉樹常見操作的 C++ 實現(xiàn)(一)》,介紹過遞歸與非遞歸的前辜窑,中钩述,后序遍歷以及層次遍歷。
這里谬擦,我們再回顧一下吧切距。
遞歸版
遞歸版的很簡單。
具體程序如下:
// 94 二叉樹的中序遍歷,遞歸版
void inorderTraversal_helper(BiTree root,vector<int>& res){
if(root == NULL){
return ;
}
inorderTraversal_helper(root->left,res);
res.push_back(stoi(root->val));
inorderTraversal_helper(root->right,res);
}
vector<int> inorderTraversal(BiTree root){
vector<int> res;
inorderTraversal_helper(root,res);
return res;
}
測試程序如下:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
vector<int> res = inorderTraversal(tree);
for(auto r : res){
cout<<r<<" ";
}
cout<<endl;
return 0;
}
測試結(jié)果:
1
# 2
3 #
# #
1 3 2
非遞歸版
具體程序如下:
// 94 二叉樹的中序遍歷,非遞歸版
vector<int> inorderTraversal_(BiTree root){
stack<BiTree> m;
vector<int> res;
BiTree rt = root;
while(rt != NULL|| m.size() != 0){
while(rt != NULL){
m.push(rt);
rt = rt->left;
}
rt = m.top();
m.pop();
res.push_back(stoi(rt->val));
rt = rt->right;
}
return res;
}
測試程序:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
vector<int> res = inorderTraversal_(tree);
for(auto r : res){
cout<<r<<" ";
}
cout<<endl;
return 0;
}
測試結(jié)果:
1
# 2
3 #
# #
1 3 2
驗證二叉搜索樹
給定一個二叉樹惨远,判斷其是否是一個有效的二叉搜索樹谜悟。
假設(shè)一個二叉搜索樹具有如下特征:
節(jié)點的左子樹只包含小于當(dāng)前節(jié)點的數(shù)。
節(jié)點的右子樹只包含大于當(dāng)前節(jié)點的數(shù)北秽。
所有左子樹和右子樹自身必須也是二叉搜索樹葡幸。
這個題可以使用遞歸,也可以使用非遞歸方法贺氓。
遞歸
具體程序如下:
// 98 驗證二叉搜索樹,遞歸
bool isValidBST_helper(BiTree root,long l,long r){
if(root == NULL){
return true;
}
if(l >= stoi(root->val) || r <= stoi(root->val)){
return false;
}
if(isValidBST_helper(root->left,l,stoi(root->val)) && isValidBST_helper(root->right,stoi(root->val),r)){
return true;
}
return false;
}
bool isValidBST(BiTree root){
return isValidBST_helper(root,LONG_MIN,LONG_MAX);
}
測試程序:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
cout<<isValidBST(tree)<<endl;
return 0;
}
測試結(jié)果:
2
1 3
# # # #
1
5
1 4
# # 3 6
# # # #
0
非遞歸
具體程序如下:
// 98 驗證二叉搜索樹,非遞歸
bool isValidBST_(BiTree root){
stack<BiTree> sk;
long temp = LONG_MIN;
while(root || sk.size()){
while(root){
sk.push(root);
root = root->left;
}
root = sk.top();
sk.pop();
if(temp >= stoi(root->val)){
return false;
}
temp = stoi(root->val);
root = root->right;
}
return true;
}
測試程序:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
cout<<isValidBST_(tree)<<endl;
return 0;
}
測試結(jié)果為:
2
1 3
# # # #
1
5
1 4
# # 3 6
# # # #
0
二叉樹的層次遍歷
給定一個二叉樹蔚叨,返回其按層次遍歷的節(jié)點值。 (即逐層地辙培,從左到右訪問所有節(jié)點)蔑水。
二叉樹的層次遍歷在之前的博客中也是提到過的,這里我們再回顧一遍吧扬蕊。
關(guān)鍵點是用隊列保存每一層節(jié)點搀别,在本層節(jié)點數(shù)據(jù)域被保存后,用隊列保存下一層節(jié)點尾抑,直到遍歷完每一層歇父。
具體的程序如下:
// 102 二叉樹的層次遍歷,由于本題的代碼模板函數(shù)和原有的函數(shù)重復(fù)了,在原函數(shù)的基礎(chǔ)上增加了下劃線以區(qū)分
vector<vector<int>> levelOrder_(BiTree root){
vector<vector<int>> res;
queue<BiTree> q;
if(root == NULL){
return res;
}
q.push(root);
while(!q.empty()){
//蒂培, ,每一層的節(jié)點數(shù)
int n = q.size();
//用以存儲該層節(jié)點的數(shù)據(jù)域
vector<int> level;
while(n--){
//以此將本層的節(jié)點出隊列
BiTree node = q.front();
q.pop();
//存入level中
level.push_back(stoi(node->val));
if(node->left){
q.push(node->left);
}
if(node->right){
q.push(node->right);
}
}
//保存該層所有節(jié)點
res.push_back(level);
}
return res;
}
測試程序:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
vector<vector<int>> res = levelOrder_(tree);
for(auto r : res){
for(auto _r : r){
cout<<_r<<" ";
}
cout<<endl;
}
return 0;
}
測試結(jié)果為:
3
9 20
# # 15 7
# # # #
3
9 20
15 7
二叉樹的鋸齒形層次遍歷
給定一個二叉樹榜苫,返回其節(jié)點值的鋸齒形層次遍歷护戳。(即先從左往右,再從右往左進(jìn)行下一層遍歷垂睬,以此類推媳荒,層與層之間交替進(jìn)行)。
這個題和上一題很相似羔飞,但是有所不同肺樟,若根節(jié)點算第一層的話,奇數(shù)層是從左到右記錄的逻淌,偶數(shù)層是從右到左記錄的么伯。
那么我們使用一個棧是不夠的,需要有兩個棧卡儒,一個從左到右記錄田柔,一個從右到左記錄,先從左往右骨望,再從右往左進(jìn)行下一層遍歷硬爆,以此類推,層與層之間交替進(jìn)行擎鸠。直到兩個棧都為空了缀磕,遍歷完成。
具體程序如下:
// 103 二叉樹的鋸齒形層次遍歷
vector<vector<int>> zigzagLevelOrder(BiTree root){
vector<vector<int>> res;
if(root == NULL){
return res;
}
//設(shè)置兩個棧劣光,一個從左到右記錄袜蚕,一個從右到左記錄
//即先從左往右,再從右往左進(jìn)行下一層遍歷绢涡,以此類推牲剃,層與層之間交替進(jìn)行
stack<BiTree> left_stack,right_stack;
//奇數(shù)層,從左到右記錄,記錄完后將該層所有節(jié)點的左雄可,右子節(jié)點壓入右棧
//偶數(shù)層凿傅,從右到左記錄,記錄完后將該層所有節(jié)點的右,左子節(jié)點壓入左棧
left_stack.push(root);
while(left_stack.size() != 0 || right_stack.size() != 0){
if(left_stack.size() != 0){
res.push_back(vector<int> ());
while(left_stack.size() != 0){
//得到當(dāng)前節(jié)點
BiTree node = left_stack.top();
left_stack.pop();
res.back().push_back(stoi(node->val));
//左節(jié)點壓入右棧
if(node->left){
right_stack.push(node->left);
}
//右節(jié)點壓入右棧
if(node->right){
right_stack.push(node->right);
}
}
}
if(right_stack.size() != 0){
res.push_back(vector<int> ());
while(right_stack.size() != 0){
//得到當(dāng)前節(jié)點
BiTree node = right_stack.top();
right_stack.pop();
res.back().push_back(stoi(node->val));
//右節(jié)點壓入左棧
if(node->right){
left_stack.push(node->right);
}
//左節(jié)點壓入左棧
if(node->right){
left_stack.push(node->left);
}
}
}
}
return res;
}
測試程序如下:
int main(){
// test
//cout<<"test:......"<<endl;
BiTree tree = levelCreate();
vector<vector<int>> res = zigzagLevelOrder(tree);
for(auto r : res){
for(auto _r : r){
cout<<_r<<" ";
}
cout<<endl;
}
return 0;
}
測試結(jié)果如下:
3
9 20
# # 15 7
# # # #
3
20 9
15 7
全部代碼見 github 数苫,main.cpp 中不帶測試程序聪舒。
本節(jié)內(nèi)容到此結(jié)束,之后將繼續(xù)更新虐急。