二叉树 | 1. 遍历方式

参考
1. 代码随想录

提纲:
在这里插入图片描述

0. 基础知识(参考:二叉树理论基础篇

二叉树的种类

在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树。

  • 满二叉树
  • 完全二叉树
    除了最底层节点可能没填满外,其余每层节点数都达到最大值,
    并且最下面一层的节点都集中于左边。
  • 二叉搜索树
    若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
    若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
    它的左、右子树也分别为二叉排序树
  • 平衡二叉搜索树
    它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,
    并且左右两个子树都是一棵平衡二叉树。

C++中
堆 的底层实现是完全二叉树
map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,
unordered_map、unordered_set底层实现是哈希表。

二叉树的存储方式

二叉树可以链式存储,也可以顺序存储。

  • 链式存储方式就用指针
    一般我们都是用链式存储二叉树

  • 顺序存储的方式就是用数组
    如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2

二叉数的定义

struct TreeNode {
    
    
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {
    
    }
};

1. 二叉树的遍历方式

二叉树主要有两种遍历方式:

  • 深度优先遍历:先往深走,遇到叶子节点再往回走。
    前序遍历(递归法,迭代法)中左右
    中序遍历(递归法,迭代法)左中右
    后序遍历(递归法,迭代法)左右中
    在这里插入图片描述
  • 广度优先遍历:一层一层的去遍历。
    层次遍历(迭代法)

2. 递归法

2.1 前序

class Solution {
    
    
public:
    void traversal(TreeNode* cur, vector<int>& vec) {
    
    
        if (cur == NULL) return;
        vec.push_back(cur->val);    // 中
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
    }
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

2.2 中序

void traversal(TreeNode* cur, vector<int>& vec) {
    
    
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    vec.push_back(cur->val);    // 中
    traversal(cur->right, vec); // 右
}

2.3 后序

void traversal(TreeNode* cur, vector<int>& vec) {
    
    
    if (cur == NULL) return;
    traversal(cur->left, vec);  // 左
    traversal(cur->right, vec); // 右
    vec.push_back(cur->val);    // 中
}

3. 迭代法

在这里插入图片描述
在这里插入图片描述

3.1 前序

在这里插入图片描述

class Solution {
    
    
public:
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        vector<int> 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);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return result;
    }
};

3.2 后序

在这里插入图片描述

class Solution {
    
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        vector<int> 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); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        /*-------- 调换 --------*/
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};

3.3 中序

在这里插入图片描述

class Solution {
    
    
public:
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
    
    
            if (cur != NULL) {
    
     // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
    
    
                cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

4. 统一迭代法

在这里插入图片描述
理解:

  • top() = 5进来,被弹出,stack: 空
  • 进行一次右中左,stack: 6, 5, N, 4
  • top() = 4 进来,被弹出, stack: 6, 5, N,
  • 进行一次右中左,stack: 6, 5, N,2,4,N, 1
  • top() = 1 进来,被弹出, stack: 6, 5, N,2,4,N,
  • 进行一次右中左,stack: 6, 5, N,2,4,N, 1,N
  • 进行到else分支:
  • N 弹出,1 节点取中间val, 并弹出
    stack: 6, 5, N,2,4,N
    res: 1
  • N 弹出,4 节点取中间val, 并弹出
    stack: 6, 5, N,2
    res: 1,4
  • 再次进入if 分支,因为top() != NULL, 进行一次弹出并右中空左
    stack: 6, 5, N,2
    stack: 6, 5, N,
    stack: 6, 5, N,2, N
  • N 弹出,2 节点取中间val, 并弹出
    stack: 6, 5, N
    res: 1,4, 2
  • N 弹出,5 节点取中间val, 并弹出
    stack: 6,
    res: 1,4, 2, 5
  • 再次进入if 分支,因为top() != NULL, 进行一次弹出并右中空左
    stack: 6
    stack:
    stack: 6, N
  • N 弹出,6 节点取中间val, 并弹出
  • Res: 1,4, 2, 5,6

4.1 中序: 左中右

class Solution {
    
    
public:
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        stack<TreeNode*> 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();
                result.push_back(node->val); // 加入到结果集
            }
        }
        return result;
    }
};

4.2 前序: 中左右

			/*------ 前序:中左右,  代码顺序:右左中 ------*/
                if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈)
                
                if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
                
                st.push(node);                          // 添加中节点
                st.push(NULL); 							// 中节点访问过,但是还没有处理,加入空节点做为标记。
            /*-------------------------------------------*/

4.3 后序: 左右中

			/*------ 前序:左右中,  代码顺序:中右左 ------*/
                st.push(node);                          // 添加中节点
                st.push(NULL); 							// 中节点访问过,但是还没有处理,加入空节点做为标记。
                
                if (node->right) st.push(node->right);  // 添加右节点(空节点不入栈)
                
                if (node->left) st.push(node->left);    // 添加左节点(空节点不入栈)
            /*-------------------------------------------*/

5. 层序遍历

题目:

102.二叉树的层序遍历(广度优先模板)

class Solution {
    
    
public:
   vector<vector<int>> levelOrder(TreeNode* root) {
    
    
       queue<TreeNode*> que;
       if(root!=NULL) que.push(root);
       vector<vector<int>> result;
       // 返回值 是二维数组

       while(!que.empty()){
    
    
           int size = que.size();  // 固定size
           vector<int> vec;

           for(int i=0; i<size; i++){
    
    
               TreeNode* node = que.front();
               que.pop();
               vec.push_back(node->val);
               if (node->left) que.push(node->left);
               if (node->right) que.push(node->right);
           }
           result.push_back(vec);

       }
       return result;

   	}
};

107.二叉树的层次遍历II

	while(!que.empty()){
    
    
         ······
  		}
    	reverse(result.begin(),result.end());
    	return result;

199.二叉树的右视图

	while (!que.empty()) {
    
    
           int size = que.size();
           for (int i = 0; i < size; i++) {
    
    
               TreeNode* node = que.front();
               que.pop();
               if (i == (size - 1)) result.push_back(node->val); 
                // 将每一层的最后元素放入result数组中
               if (node->left) que.push(node->left);
               if (node->right) que.push(node->right);
            }
     }

637.二叉树的层平均值

	while(!que.empty()){
    
    
            int size = que.size();
            // vector<double> vec;
            // 不增加vector, 直接定义sum变量
            double sum=0;

            for(int i=0;i<size;i++){
    
    
                TreeNode* node = que.front();
                que.pop();
                // vec.push_back(node->val / double(size));
                sum += node->val;
                if(node->left) que.push(node -> left);
                if(node->right) que.push(node -> right);
            }
            // double temp=0;
            // for(int i=0;i<size;i++){
    
    
            //     temp+=vec[i];
            // }
            // result.push_back(temp);
            result.push_back(sum/size);
        }

429.N叉树的层序遍历

	for(int i=0; i<size; i++){
    
    
		Node* node=que.front();
    	que.pop();
    	res.push_back(node->val);
       	// 转到孩子节点
        for(int i=0; i< node->children.size();i++){
    
    
            if(node->children[i])   que.push(node->children[i]);
        }
 	}

515.在每个树行中找最大值

	 while(!que.empty()){
    
    
            int size = que.size();
            int temp = INT_MIN;			// 定义负无穷大,正无穷大为INT_MAX
            for(int i=0; i<size; i++){
    
    
                TreeNode* node =que.front();
                temp =  node->val > temp ? node->val: temp ;
                que.pop();

                if(node->left)   que.push(node->left);
                if(node->right)  que.push(node->right);
            }
            result.push_back(temp);
	 }

116.填充每个节点的下一个右侧节点指针

在这里插入图片描述
理解:

  • 最顶层
    i=0
    que: 1进入后,弹出
    nodePre: 1
    node:1
    que:2,3进入,
    最后1 -> next =NULL
  • 第二层
    i=0
    que: 2,3, 弹出2,
    nodePre: 2
    node: 2
    que: 4,5进入
    i=1
    que: 3,4,5,弹出 3
    node: 3
    nodePre->next = node; 即 2-> next =3
    nodePre: 3
    que:4,5 ,同时,6,7进入
    最后3 -> next =NULL
  • 第三层
    i=0
    que: 4,5,6,7, 弹出4,
    nodePre: 4
    node: 4
    que:5,6,7
    i=1
    que: 5,6,7 弹出 5
    node: 5
    nodePre->next = node; 即 4-> next = 5
    nodePre: 5
    que:6,7
    i=2
    que: 6,7 弹出 6
    node: 6
    nodePre->next = node; 即 5-> next = 6
    nodePre: 6
    que:7
    i=3
    que: 7 弹出 7
    node: 7
    nodePre->next = node; 即 6-> next = 7
    nodePre: 7
    que:空
    最后,7 -> next =NULL
class Solution {
    
    
public:
    Node* connect(Node* root) {
    
    
        // Node* res;
        queue<Node*> que;
        if(root!=NULL)  que.push(root);
        
        while(!que.empty()){
    
    
            int size = que.size();
            // vector<int> temp;
            Node* nodePre;
            Node* node;

            for(int i=0; i<size; i++){
    
    

                // Node* node = que.front();
                // que.pop();
                // res.push_back(node->val);

                // if(i == size-1) res.push_back('#');
                
                if(i==0){
    
    
                    nodePre = que.front();   // 取出一层的头结点
                    que.pop();
                    node = nodePre;
                }else{
    
    
                    node = que.front();
                    que.pop();
                    nodePre -> next = node;
                    nodePre = nodePre->next;
                }

                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);

            }
            nodePre-> next =NULL;
           
        }
        // return res;   
        return root;
    }
};

117.填充每个节点的下一个右侧节点指针II
同上

104.二叉树的最大深度
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。

在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度
可以使用二叉树层序遍历的模板来解决的

class Solution {
    
    
public:
    int maxDepth(TreeNode* root) {
    
    
        //  f(node-> left || node->right)  depth++;  错误的想法
        if(root == NULL ) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
    
    
            int size = que.size();
            depth++; // 记录深度
            for (int i = 0; i < size; i++) {
    
    
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return depth;
    }
};

111.二叉树的最小深度
基本同上

while(!que.empty()){
    
    
            int size = que.size();
            count++;
            for(int i=0; i<size;i++){
    
    
                TreeNode* node = que.front();
                que.pop();
                // 或者 if(!(node->left || node->right ))  return count;
                if (!node->left && !node->right) return count;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
                
            }
        }

翻转二叉树

首先想好,用递归还是迭代,再者,用什么方式遍历

递归:

  • 递归三部曲:
    1.确定递归函数的参数和返回值
    2.确定终止条件
    3.确定单层递归的逻辑

     /* 递归法 */
    class Solution {
          
          
    public:
    	TreeNode* invertTree(TreeNode* root) {
          
          
        	// 首先确定递归的终止条件
        	if(root == NULL ) return root;
       	 	// 前序遍历:中左右
        	swap(root-> left, root->right);
        	invertTree(root->left);
        	invertTree(root->right);
        	
        	// 后序遍历:左右中     
       	 	// invertTree(root->left);
       	 	// invertTree(root->right);
        	// swap(root-> left, root->right);
        	
        	return root;
    	}
    };
    

迭代:

  • 深度优先遍历:

    class Solution {
          
          
    public:
    	TreeNode* invertTree(TreeNode* root) {
          
          
        	if (root == NULL) return root;
        	stack<TreeNode*> st;
        	st.push(root);
        	while(!st.empty()) {
          
          
            	TreeNode* node = st.top();              // 中
            	st.pop();
            	swap(node->left, node->right);
           	 	if(node->right) st.push(node->right);   // 右
            	if(node->left) st.push(node->left);     // 左
        	}
       		return root;
    	}
    };
    /* 统一 写法 */
    class Solution {
          
          
    public:
    TreeNode* invertTree(TreeNode* root) {
          
          
        stack<TreeNode*> 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();
                swap(node->left, node->right);          // 节点处理逻辑
            }
        }
        return root;
    	}
    };
    
  • 广度优先遍历:层序遍历

    class Solution {
          
          
    public:
    	TreeNode* invertTree(TreeNode* root) {
          
          
        	queue<TreeNode*> que;
        	if (root != NULL) que.push(root);
        	while (!que.empty()) {
          
          
           		 int size = que.size();
           		 for (int i = 0; i < size; i++) {
          
          
                	TreeNode* node = que.front();
                	que.pop();
                	swap(node->left, node->right); // 节点处理
                	if (node->left) que.push(node->left);
                	if (node->right) que.push(node->right);
           	 	}
        	}
        	return root;
    	}
    };
    

猜你喜欢

转载自blog.csdn.net/weifengomg/article/details/128809216