二叉树高频题
迭代(非递归)方法做二叉树的三种遍历
/**
* 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;
}