二叉树的结构
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
二叉树的深度遍历的三种方式
在开始总结三种遍历方式之前,先将左程云老师提到递归序做一个总结,我理解的递归序就是递归过程中遍历到节点顺序。针对下面这个伪代码:
void recursive(TreeNode*node){
if(node == NULL)
return ;
recursive(node->left);
recursive(node->right);
}
递归过程,是先遍历自己,然后进而遍历左子树,再遍历右子树,调用函数的过程中,return会返回到上一次调用的位置,所以每个节点都会被遇到3次。对应的递归顺序如下:
12444255523666377731
前序遍历
所谓前序遍历是指第一次遇到这个节点的时候就做出相应的动作,比如打印,这样前序遍历的舒顺序是:1->2->4->5->3->6->7(在递归序中取出第一次出现的数字即可)
- 递归实现
void recursive(TreeNOde*node){
if(node == NULL)
return ;
cout<<node->val<<" ";
//左子树
recursive(node->left);
//右子树
recursive(node->right);
}
- 非递归实现
- 非递归实现本质就是自己用代码实现栈这一数学结构,栈是满足【先入后出】的特性为了配合这一特性,我们应该先将右孩子压入栈,再将左孩子压入栈,这样就能实现左孩子先弹出,先处理左子树,右孩子再弹出,再处理右子树的过程,满足先序遍历。
- 同样,先序遍历是指第一次遇到该节点就处理(比如打印)所以每次从栈顶弹出节点我们都要打印,然后再压栈操作,直至栈空。
具体实现
1.先将父节点入栈
2.从栈中弹出一个节点,记为cur
3.打印、处理cur
4.先右节点入栈再左节点入栈(如果存在左右节点的话)
5.重复以上操作,直至栈空
代码
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
if(root != nullptr){
stack<TreeNode*>stk;
stk.push(root);
while(!stk.empty()){
TreeNode*temp = stk.top();
stk.pop();
res.push_back(temp->val);
if(temp->right != nullptr)
stk.push(temp->right);
if(temp->left != nullptr)
stk.push(temp->left);
}
}
return res;
}
};
中序遍历
- 非递归实现
- 中序遍历顺序是 左-头-右
- 循环内部将整棵树的左节点入栈,当没有左节点时弹出栈顶元素
- 并对弹出的栈顶元素做处理,之后将栈顶元素的右子树左同样的处理:将右子树的左节点一直入栈,循环往复
- 相当于整个树按照左边界分解到了
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
if(root != nullptr){
stack<TreeNode*>stk;
TreeNode*tmp = root;
while(!stk.empty() || tmp != nullptr ){
if(tmp != nullptr){
stk.push(temp);
temp = temp -> left;
}
else{
temp = stk.top();
stk.pop();
res.push_back(temp->val);
temp = temp -> right;
}
}
}
return res;
}
};
后序遍历
- 非递归实现
- 后序遍历的处理方式应该是左-右-头,在遍历的过程中,头节点总是最先遇到,我们遇到时不做任何处理,借助收集栈,放入收集栈中,之后利用栈“先入后出”的特性,最后处理头节点
- 先遍历 头节点,之后遍历左节点、右节点,采用循环完成,循环过程中,先将栈顶元素出栈,出栈元素放入收集栈中,之后取出栈顶元素的左节点处理栈,取出栈顶元素的右节点入处理栈,循环往复。
- 对收集栈进行出栈操作。
1.根节点入栈
2.循环内部先将栈顶元素出栈,记为cur了, 直至栈空
3.左节点入栈
4.右节点入栈
5.cur节点放入收集栈
6.处理完成后,收集栈内元素依次出栈
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
if(root != nullptr){
stack<TreeNode*>stk;
stack<TreeNode*>stk1; // collecting
stk.push(root);
while(!stk.empty()){
TreeNode*temp = stk.top();
stk.pop();
stk1.push(temp);
if(temp->left != nullptr)
stk.push(temp->left);
if(temp->right != nullptr)
stk.push(temp->right);
}
while(!stk1.empty()){
TreeNode*cur = stk1.top();
stk1.pop();
res.push_back(cur->Val);
}
}
return res;
}
};
二叉树的宽度优先遍历
BFS
宽度优先遍历表示按层处理二叉树中的每一个结点,下图遍历顺序应为1-2-3-4-5-6-7
实现思路
- 采用队列的方式实现,先入先出,
- 首先将头节点进入队列,之后进入循环,将队列头部元素出队列,出队列时处理该节点
- 左右孩子入队列
- 循环往复,直至队列为空
代码
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
if(root != nullptr){
queue<TreeNode*>que ;
que.push(root);
while(!que.empty()){
TreeNode*temp = que.front();
que.pop();
res.push_back(temp->val);
if(temp->left != nullptr)
que.push(temp->left);
if(temp->right != nullptr)
que.push(temp->right);
}
}
return res;
}
};
BFS获得二叉树最大宽度
二叉树宽度为每一层的结点个数,下面的二叉树中最大宽度为4,可以采用BFS的方式得到二叉树的最大宽度
实现思路:
1. 要想得到每一层的宽度,就需要标记每一个节点所在的层,这样才能统计每一层结点的个数,从而得到一棵二叉树的最大宽度,
2. 可以采用哈希表实现,哈希表中记录当前结点与所在层数的对应关系,规定头节点所在的层为第1层
3. 如果所在的层数与当前统计宽度的层数相同,宽度就加1,
4. 如果不相同,则要更新当前最大宽度、并将统计宽度的层数加1,表示接下来要统计下面一层的宽度
代码
class Solution {
public:
int getMaxWdith(TreeNode* root) {
if(root == nullptr)
return 0;
queue<TreeNode*>que ;
que.push(root);
unordered_map<TreeNode*, int>hash;
hash[root] = 1; //表示root结点对应的层为1
int curlevel = 1; //要统计宽度的层数
int curCounts = 0; // curlevel 统计得到的数量
int maxWidth = -1; //初始化maxwidth = -1
while(!que.empty()){
TreeNode*temp = que.front();
que.pop();
if(hash[temp] == curlevel){
curCounts++;
}
else{
maxWidth = max(maxWidth, curCounts);
curCounts = 0;
curlevel++;
}
if(temp->left != nullptr){
que.push(temp->left);
hash[temp->left] = curlevel+1;
}
if(temp->right != nullptr){
hash[temp->right] = curlevel+1;
que.push(temp->right);
}
}
return maxWidth;
};