二叉树高频面试算法题

迭代(非递归)方法做二叉树的三种遍历
/**
 * 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) {}
 * };
 */
class Solution {
public:
    // (1) 二叉树前序遍历的 非递归实现
    vector<int> preorderTraversal(TreeNode* root) {
        if(!root) return vector<int>();
        stack<TreeNode*> stk; // 用来存储每个父节点,以便于访问完左子树后,自此切换到右子树
        vector<int> ans;
        while(root || !stk.empty()) {
            if (root) {            // 访问深度优先访问左子树
                ans.push_back(root->val);
                stk.push(root);
                root = root->left;
            } else {   // 左子树到底了,从stk中抽出最下层左子树的父节点,然后切换到其右子树
                root = stk.top();
                stk.pop();
                root = root->right;
            }
        }
        return ans;
    }
    // (2)二叉树中序遍历的 非递归 版本
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> stk;
        vector<int> ans;
        while(root || !stk.empty()) {
            if (root) {
                stk.push(root);       // (1)
                root = root->left;
            } else {
                root = stk.top();
                //前序遍历其他啥也不用变,只需把这一行移到(1)处上一行
                ans.push_back(root->val);
                stk.pop();
                root = root->right;
            }
        }
        return ans;
    }
    // (3)二叉树的后序遍历 的 非递归版本 (迭代)两种
      // 方法 1
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> stk;
        vector<int> ans;
        TreeNode *prev = nullptr;
        while(!stk.empty() || root) {
            if (root) {
                stk.push(root);
                root = root->left;
            } else {
                root = stk.top();
                stk.pop();
                if (root->right == nullptr || root->right == prev) {
                    ans.push_back(root->val);
                    prev = root;
                    root = nullptr;
                } else {
                    stk.push(root);
                    root = root->right;
                }
            }
        }
        return ans;
    }
    // 方法 2 用了两个栈
    vector<int> postorderTraversal2stack(TreeNode* root) {
        if(!root) return vector<int>();
        stack<TreeNode*> stk, output;
        vector<int> ans;
        stk.push(root);
        while(!stk.empty()) {
            TreeNode *tmp = stk.top();
            stk.pop();
            output.push(tmp);
            if (tmp->left)  stk.push(tmp->left);
            if (tmp->right) stk.push(tmp->right);
        }
        while (!output.empty()) {
            ans.push_back(output.top()->val);
            output.pop();
        }
        return ans;
    }
};
二叉树根节点到叶子节点的所有路径和

牛客网题目
在这里插入图片描述

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
class Solution {
public:
    int sumNumbers(TreeNode* root) {
        if(!root) return 0;
        dfs(root);
        return ans;
    }
private:
    int ans = 0, cur = 0;
    // 递归法前序遍历
    void dfs(TreeNode* root) {
        if(!root) return;
        cur = cur*10 + root->val;
        // 以下表述当前节点是叶节点,
        if(!root->left && !root->right) ans = ans + cur;
        dfs(root->left);  // 选择左子树
        dfs(root->right);  // 选择右子树
        cur /=10; // 撤销选择
    } 
};
从前序与中序遍历序列构造二叉树

Leetcode题目 :根据一棵树的前序遍历与中序遍历构造二叉树。

class Solution {
public:
    // 先根据前序遍历获知 root 的数值,然后去中序遍历数组中搜索得到root在中序数组中的下标,得知左子树的节点数量和右字数的节点数量,由此可以获知左子树和右子树的中序前序遍历数组在root中序前序遍历数组中的位置。由此,可用递归。
    unordered_map<int, int> inorder_index; //哈希表方便搜索中序遍历数组中rootval的下标
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.empty()||inorder.empty()) return NULL;
        for(int i = 0; i < inorder.size(); ++i) inorder_index[inorder[i]] = i; //填满inorder_index
        return getTree(preorder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
    }
    //getTree 根据 preoder[pre_l] ~ preorder[pre_r] 的前序遍历,和 inorder[in_l] ~ inorder[in_r] 的中序遍历,获得对应的二叉树。
    TreeNode* getTree(vector<int>& preorder, vector<int>& inorder, int pre_l, int pre_r, int in_l, int in_r) {
        if(pre_l > pre_r || in_l > in_r) return NULL; //注意不能等于,因为等于表示是叶子节点。
        int rootindex_pre = pre_l;  // 先得到 root 在前序遍历中的位置
        int rootindex_in = inorder_index[preorder[rootindex_pre]]; // 然后得到 root 在中序遍历中的位置
        int pre_l_newleft = rootindex_pre + 1;  // 左子树前序遍历的左边界
        int pre_r_newleft = pre_l_newleft + (rootindex_in - in_l - 1); //左子树前序遍历的右边界
        int pre_l_newright = pre_r_newleft + 1;  // 右子树前序遍历的左边界
        int pre_r_newright = pre_r;  // 右子树前序遍历的右边界
        TreeNode *root = new TreeNode(preorder[pre_l]);
        root->left = getTree(preorder, inorder, pre_l_newleft, pre_r_newleft, in_l, rootindex_in-1);
        root->right = getTree(preorder, inorder, pre_l_newright, pre_r_newright, rootindex_in+1, in_r);
        return root;
    }
};
从上到下打印二叉树(每层按顺序打印,之字形打印)
  • Leetcode题目 : 同一层的节点按从左到右的顺序打印,每一层打印到一行。
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(!root) return vector<vector<int>>();
        queue<TreeNode *> tq;
        vector<vector<int>> ans;
        tq.push(root);
        while(!tq.empty()) {
            int n = tq.size();
            vector<int> tmp;
            for (int i = 0; i < n; ++i) {
                TreeNode *cur = tq.front();
                tmp.push_back(cur->val);
                tq.pop();
                if(cur->left) tq.push(cur->left);
                if(cur->right) tq.push(cur->right);
            }
            ans.push_back(tmp);
        }
        return ans;
    }
  • Leetcode题目 : 之字形打印二叉树,第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if (root==NULL) return res;
        bool flag = true; //从左向右打印为true,从右向左打印为false
        deque<TreeNode*> q;
        q.push_back(root);
        while (!q.empty()) {
            int n = q.size();
            vector<int> out;
            TreeNode* node;
            while (n>0) {
                if (flag) { // 前取后放:从左向右打印,所以从前边取,后边放入
                    node = q.front();
                    q.pop_front();
                    if (node->left)
                        q.push_back(node->left);  // 下一层顺序存放至尾
                    if (node->right)
                        q.push_back(node->right);
                } else  {   //后取前放: 从右向左,从后边取,前边放入
                    node = q.back();
                    q.pop_back();
                    if (node->right)
                        q.push_front(node->right);  // 下一层逆序存放至首
                    if (node->left)
                        q.push_front(node->left);
                }
                out.push_back(node->val);
                n--;
            }
            flag = !flag;
            res.push_back(out);
        }
        return res;
    }
二叉搜索树与双向链表

牛客网题目 : 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if(!pRootOfTree) return NULL;
        stack<TreeNode*> stk;
        TreeNode *pre = NULL;
        TreeNode *root = pRootOfTree;
        TreeNode *head;
        bool isfirst = true;
        // 以下是中序遍历递归循环
        while(root || !stk.empty()) {
            if (root) {
                stk.push(root);    // 树节点入栈
                root = root->left; // 深入到最左的节点
            } else { // root为NULL,表示已经到最左叶子节点的子节点(NULL)了
                root = stk.top();  //(1)取 栈顶 节点
                if (isfirst) {  // 如果是第一个,也就是最左叶子节点,也就是最小值节点
                    head = root;  // 记下第一个节点作为链表的头节点
                    pre = root;   // 前一个节点更新为 root,
                    isfirst = false; 
                } else {
                    pre->right = root; // 前一个节点的下一个节点设为当前节点
                    root->left = pre;  // 当前节点的前一个节点设为前一个节点
                    pre = root;        // 更新前一个节点为当前节点
                }
                stk.pop();  // 删除刚刚处理过的栈顶元素
                root = root->right; // 中序遍历右子树
            }
        }
        return head;
    }
};
二叉树的序列化和反序列化

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Codec {
public:
    // 基本思路同 层序遍历,但注意 Null也要处理
    // 注意 ostringstream 和 istringstream 用法
    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        if(!root) return "";
        ostringstream os;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
            TreeNode *tmp = que.front();
            que.pop();
            if(tmp) {
                os << tmp->val << " ";
                que.push(tmp->left);
                que.push(tmp->right);
            } else {
                os << "null ";
            }
        }
        return os.str();
    }
    // 先根据字符串将一系列的树节点建立并保存到 vector
    //   然后将 vector 中的节点 连成二叉树 ,
    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data.empty()) return NULL;
        vector<TreeNode *> vect;
        istringstream in(data);
        string tmp;
        while(in >> tmp) {
            if(tmp == "null") vect.push_back(NULL);
            else {
                int val = stoi(tmp);
                vect.push_back(new TreeNode(val));
            }
        }
        int pos = 1;
        int n = vect.size();
        for(int i = 0; i < n; ++i) {
            if (!vect[i]) continue;
            vect[i]->left = vect[pos++];
            if(pos<n) {
                vect[i]->right = vect[pos++];
            }
        }
        return vect[0];
    }
};

二叉搜索树的第 k 小的节点 (或者第 k 大的节点)

牛客网题目 : 给定一棵二叉搜索树,请找出其中的第k小的结点。

注意:找第k小的节点是从小往大找,按左->中->右(中序遍历)找。而找第k大的节点就反过来就行,从大往小找,按右->中->左的顺序找,即将以下代码的 left 和 right 的位置互换即可。

TreeNode* KthNode(TreeNode* pRoot, int k) {
        if(!pRoot) return NULL;
        stack<TreeNode *> stk;
        TreeNode *r = pRoot;
        int count = k;
        while( !stk.empty() || r ) {
            if ( r ) {
                stk.push(r);
                r = r->left;
            } else {
                r = stk.top();
                stk.pop();
                count--;
                if(count==0) return r;
                r = r->right;
            }
        }
        return NULL;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值