牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表

⚓️作者简介:北京某能源高校非科班大四学生。

座右铭:“九层之台,起于垒土” 。所以学习技术须脚踏实地。

这里推荐一款刷题、模拟面试神器,可助你斩获大厂offer:点我免费刷题、模拟面试

文章目录

  • 前言
  • 面试必刷题
    • 1.二叉树的层序遍历
    • 2.之字形打印二叉树
    • 3.二叉搜索树与双向链表

前言

牛客网是一个集笔面试系统、题库、课程教育、社群交流、招聘内推于一体的招聘类网站,更是一个专注于程序员的学习和成长的平台。

在某次浏览博客的过程中,我偶然点进一个链接,注册了牛客账号。一来到牛客首页,我就被其丰富的功能与良好的社区环境所吸引:
在这里插入图片描述
进入题库,更是有最新校招试题与专项练习:在这里插入图片描述
在线编程更是有在线调试功能,可大大提高debug效率:
在这里插入图片描述
问答题下还有超多牛客用户交流:
在这里插入图片描述
总之,牛客是一个专注于程序员的学习和成长的平台,所以我极力推荐大家注册牛客,坚持刷题,充实自己,大厂offer便指日可待。

面试必刷题

 二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。上一节我们讲解了二叉树的前序、中序、后序的递归与非递归形式的遍历,这一节来刷几个进阶点的题目:

1.二叉树的层序遍历

题目:
牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表_第1张图片

示例:
牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表_第2张图片

解题思路
什么是二叉树的层序遍历?简单来说就是一层一层的遍历二叉树,第一次遍历二叉树的根节点,第二次遍历二叉树的两个左右节点,以此类推。

一般来说二叉树的层序遍历要用到队列这种数据结构,每当一个节点出队就要将该节点的孩子入队,这样就可以保证遍历满足层序遍历的要求。当队列为空,这棵树也就遍历完成了。

但对于此题,我们需要返回一个二维向量,每个元素从前向后是二叉树的每一层遍历结果。所以当前一层的节点全部出队并保存入向量中后,这一层的遍历也就结束了,所以我们在遍历一层树前,需要获得前一层节点的数量,用来控制内层循环的次数。

算法流程:

  • 维护一个队列q,一个元素为向量的向量vv保存层遍历的结果,一个向量v保存每层的结果,用来push_back到vv中。
  • 根节点入队。
  • 队不空,记录当前队列长度 n,用来将当前队列元素出队,也就是上一层的遍历结果。
  • 当前节点出队并进入v,左右节点入队,n减一。直至n为0。
  • 将 v push_back到vv中,回到第三步。

C++解题代码:

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector>
     */
    vector<vector<int> > levelOrder(TreeNode* root) {
        // write code here
        vector<vector<int>> vv;
        if(!root) return vv;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int n = q.size();
            vector<int> v;
            while(n){
                TreeNode* node = q.front();
                q.pop();
                v.push_back(node->val);
                --n;
                
                if(node->left)  q.push(node->left);
                if(node->right) q.push(node->right);
            }
            vv.push_back(v);
        }
        return vv;
    }
};

2.之字形打印二叉树

题目:
牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表_第3张图片

示例:
牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表_第4张图片

解题思路

本题也是层序遍历,不过是之字形的层序遍历,我们可以在上题的基础上进行改进:

  • 之字形,有种反序的味道,所以我们选择栈来替代上一题层序遍历中的队列。
  • 维护一个变量 odd 记录这一层是否是奇数层(根节点在第0层),初始化为true,没遍历一层就取反一次。
  • 在循环中维护一个队列,出栈的元素按顺序保存到队列中,队列元素出队,其左右孩子入栈。

算法流程:

  • 根节点入栈。
  • 栈不空,栈中元素依次出栈、入队、push_back到局部向量v中,所有元素出栈后,队列元素出队
    • 若上一层为偶数层,左孩子先进栈,右孩子后进栈;
    • 若上一层为奇数层,右孩子先进栈,左孩子后进栈。这样就可以保证遍历每一层都可以为上一层的反序了。
  • 将 v push_back到vv中,回到第二步。

C++解题代码:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> vv;
        if(!pRoot) return vv;
        stack<TreeNode*> s;
        s.push(pRoot);
        bool odd = true;
        while(!s.empty()){
            odd = !odd;
            int n = s.size();
            vector<int> v;
            queue<TreeNode*> q;
            while(n){
                TreeNode* node = s.top();
                s.pop();
                v.push_back(node->val);
                --n;
                q.push(node);
            }
            n = q.size();
            while(n){
                TreeNode* node = q.front();
                q.pop();
                if(odd){
                    if(node->right) s.push(node->right);
                    if(node->left) s.push(node->left);
                }else{
                    if(node->left) s.push(node->left);
                    if(node->right) s.push(node->right);
                }
                --n;
            }
            vv.push_back(v);
        }
        return vv;
    }
    
};

3.二叉搜索树与双向链表

题目:
牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表_第5张图片

示例:

牛客网面试必刷TOP101之——之字形遍历二叉树和将二叉树转换为双向链表_第6张图片

解题思路:
由于二叉树的中序遍历得到的就是双向链表的序列,所以在中序遍历的基础上我们可以利用双指针改变节点的左右孩子指向,就可以解决本题。

算法步骤:

  • 首先我们要想办法确定链表的头节点, 只要不断向左递归到最后一个节点即可确定
  • 其次我们要确定将前后两个节点连接起来的方案, 我们定义两个指针node和prenode, node的当前节点的指针, prenode是node的前驱节点的指针, 因此只要prenode不为空, 连接方案就是prenode->right = node, node->left = prenode
  • 二叉树中序遍历的顺序是左儿子->根节点->右儿子, 故我们只要按中序遍历的顺序移动prenode和node指针, 就可以将整个二叉树连接为一条双向链表

C++解题代码

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    TreeNode *head, *prenode;    
    
    TreeNode* Convert(TreeNode* pRootOfTree) {
        convert(pRootOfTree);
        prenode = nullptr;
        return head;
    }

private:
    void convert(TreeNode* node){
        if(!node) return;
        
        convert(node->left);
        
        if(!prenode) {
            head = node;
        }
        else{
            prenode->right = node;
            node->left = prenode;
        }
        
        prenode = node;
        
        convert(node->right);
    }
    
};

我希望通过写博客来结束浑浑噩噩的生活,我更希望通过刷题结束人云亦云的思考。刷题不仅仅是刷题,还是我们与自己内心深处的对话。希望我们可以一起在牛客刷题交流,一起收割大厂offer!

你可能感兴趣的:(牛客C++刷题,面试,链表,数据结构,算法)