1. 给定一颗二叉树,按层打印它。例如:
输入:
1
2 3
4 5
输出:12345
使用队列来实现,首先让根节点进队列,然后根节点出队,同时让它的左右孩子节点依次进队列,依次类推,直到队列为空。
2.有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树,并且把每一层的节点存在一个数组中。类似的有些题目可能要求打印完一层节点后需要换行。
这道题其实就是上道题的变种,难点在于怎样确定每层的最后一个节点,其实每层最后一个节点即为最新进入队列的节点。
实现代码如下:
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
class TreePrinter {
public:
vector<vector<int> > printTree(TreeNode* root) {
queue<TreeNode*> que;
TreeNode* last = root;//last指向当前层最后一个节点
TreeNode* nlast = nullptr;//nlast指向下一层最后一个节点
vector<vector<int>> res;
vector<int> res1;
if (!root) return res;
que.push(root);
while (!que.empty()) {
TreeNode* p = que.front();
if (p->left){
que.push(p->left);
nlast = p->left;
}
if (p->right) {
que.push(p->right);
nlast = p->right;
}
res1.push_back(p->val);
if (p == last){
cout << endl;
last = nlast;
res.push_back(res1);
res1.clear();
}
que.pop();
}
return res;
}
};
3.递归方式打印一颗二叉树的前序,中序和后序遍历,并把结果放在一个二维数据中。
代码实现如下:
class TreeToSequence {
public:
void preorder(TreeNode*p, vector<int> &sub_res)//前序遍历
{
if (p == nullptr) return;
sub_res.push_back(p->val);
preorder(p->left, sub_res);
preorder(p->right, sub_res);
}
void inorder(TreeNode*p, vector<int> &sub_res)//中序遍历
{
if (p == nullptr) return;
inorder(p->left, sub_res);
sub_res.push_back(p->val);
inorder(p->right, sub_res);
}
void postorder(TreeNode*p, vector<int> &sub_res)//后序遍历
{
if (p == nullptr) return;
postorder(p->left, sub_res);
postorder(p->right, sub_res);
sub_res.push_back(p->val);
}
vector<vector<int> > convert(TreeNode* root) {
vector<vector<int> > res;
res.resize(3);
preorder(root, res[0]);
inorder(root, res[1]);
postorder(root, res[2]);
return res;
}
};
4.非递归方式打印一颗二叉树的前序,中序和后序遍历,并把结果放在一个二维数据中。
可以 使用栈来实现,代码如下:
class TreeToSequence {
public:
void preorder(TreeNode* p,vector<int> &v)
{
stack<TreeNode *> s;
TreeNode* cur=NULL;
s.push(p);
while(!s.empty()) {
cur=s.top();
v.push_back(cur->val);
s.pop();
if(cur->right!=NULL) s.push(cur->right);
if(cur->left!=NULL) s.push(cur->left);
}
}
void preorder2(TreeNode* p,vector<int> &v)
{
stack<TreeNode*> s;
TreeNode* cur = p;
while (cur || !s.empty()) {
while (cur) {
v.push_back(cur->val);
s.push(cur);
cur = cur->left;
}
if (!s.empty()) {
cur = s.top();
s.pop();
cur = cur->right;
}
}
}
void inorder(TreeNode* p,vector<int> &v)
{
stack<TreeNode*> s;
TreeNode* cur=p;
while(!s.empty()||cur!=NULL) {
if(cur!=NULL) {
s.push(cur);
cur=cur->left;
}
else {
cur=s.top();
v.push_back(cur->val);
cur=cur->right;
s.pop();
}
}
}
//使用两个栈实现
void postorder(TreeNode* p,vector<int> &v)
{
stack<TreeNode*> s1,s2;
TreeNode* cur=NULL;
s1.push(p);
while(!s1.empty()){
cur=s1.top();
s2.push(cur);
s1.pop();
if(cur->left!=NULL) s1.push(cur->left);
if(cur->right!=NULL) s1.push(cur->right);
}
while(!s2.empty()){
v.push_back(s2.top()->val);
s2.pop();
}
}
//使用一个栈来实现
void postorder2(TreeNode* p,vector<int> &v)
{
stack<TreeNode*> s;
s.push(p);
TreeNode* cur=NULL;
TreeNode* new_pop=NULL;
while(!s.empty()){
cur=s.top();
if (cur->left && new_pop != cur->left && new_pop != cur->right) {
s.push(cur->left);
} else if (cur->right && new_pop != cur->right) {
s.push(cur->right);
} else {
v.push_back(cur->val);
new_pop = cur;
s.pop();
}
}
}
vector<vector<int> > convert(TreeNode* root) {
vector<vector<int> > res;
res.resize(3);
if (!root) return res;
preorder(root, res[0]);
inorder(root, res[1]);
postorder(root, res[2]);
return res;
}
};
5.二叉树的序列化,前序遍历二叉树,如果遇到空节点则加个#!,否则节点值后加个!。
方法一,递归方式
class TreeToString {
public:
void preorder(TreeNode* root, string& str)
{
if (root == NULL) {
str += "#!";
return ;
}
str += (to_string(root->val) + "!");
preorder(root->left, str);
preorder(root->right, str);
}
string toString(TreeNode* root) {
string str;
preorder(root, str);
return str;
}
};
方法二,非递归方式
class TreeToString {
public:
string toString(TreeNode* root) {
string str;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()) {
TreeNode* p = st.top();
st.pop();
if (p == nullptr) {
str += "#!";
} else {
str += to_string(p->val) + "!";
st.push(p->right);//可以压空节点
st.push(p->left);
}
}
return str;
}
};
方法三,非递归方式
class TreeToString {
public:
string toString(TreeNode* root) {
string str;
stack<TreeNode*> s;
TreeNode* cur = root;
while (cur || !s.empty()) {
while (cur) {
str += to_string(cur->val) + "!";
s.push(cur);
cur = cur->left;
}
str += "#!";
if (!s.empty()) {
cur = s.top();
s.pop();
cur = cur->right;
}
}
str += "#!";
return str;
}
};
6.反序列化5中的二叉树。
TreeNode* setup_tree(std::vector<std::string>&vec, int &i)
{
if (vec[i] == "#") {
++i;
return NULL;
}
TreeNode* root = NULL;
root = new TreeNode(std::stoi(vec[i]));
//root = (TreeNode*)malloc(sizeof(struct TreeNode));
//root->val = std::stoi(vec[i]);
++i;
root->left = setup_tree(vec, i);
root->right = setup_tree(vec, i);
return root;
}
int main()
{
char str[] = "12!3!4!#!#!#!5!#!#!";
char *buf;
char *token = strtok_s(str, "!", &buf);
std::vector<std::string> vec;
while (token != NULL) {
std::string tmp_str(token);
vec.push_back(tmp_str);
token = strtok_s(NULL, "!", &buf);
}
int i = 0;
TreeNode* root = setup_tree(vec, i);
std::string after_str;
preorder(root, after_str);
cout << after_str;
return 0;
}
构造二叉树setup_tree需要返回一个root节点,而不是传一个指针进去。
7.判断一颗二叉树是否为平衡二叉数。
class CheckBalance {
public:
bool check(TreeNode* root) {
// write code here
bool res=true;
checkchild(root,1,res);
return res;
}
int checkchild(TreeNode* p,int level,bool& res)
{
if(NULL==p)
return level;
int lh=checkchild(p->left,level+1,res);
if(!res) return level;
int rh=checkchild(p->right,level+1,res);
if(!res) return level;
if(abs(lh-rh)>1)
res=false;
return max(lh,rh);
}
};
改写后序遍历,当发现左子树或右子树不为平衡二叉树时,则终止遍历。
8.判断是否为完全二叉树
class CheckCompletion {
public:
bool chk(TreeNode* root) {
// write code here
if(root==NULL) return true;
queue<TreeNode*> q;
q.push(root);
bool flag=false;
while(!q.empty()) {
TreeNode* cur=q.front();
q.pop();
if((cur->left==NULL&&cur->right!=NULL)||(flag&&(cur->left!=NULL||cur->right!=NULL))) return false;
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
if (cur->left == nullptr|| cur->right == nullptr) flag=true;
}
return true;
}
};
改写层序遍历,对于当前节点如果没有左孩子但是有右孩子,那么不是完全二叉树。如果当前节点左孩子或右孩子不存在那么以后的节点必须都为叶子节点。
9.判断是否为满二叉树。
只需要把8中返回false的条件改为以下即可:
if((cur->left==NULL&&cur->right!=NULL)||(cur->left!=NULL&&cur->right==NULL)||
(flag&&(cur->left!=NULL||cur->right!=NULL))) return false;
10.判断是否为二叉搜索树。
class Solution {
public:
bool isValidBST(TreeNode* root) {
TreeNode *cur = root;
stack<TreeNode*> st;
int last_val = 0;
bool flag = true;
while (!st.empty() || cur != NULL) {
if (cur) {
st.push(cur);
cur = cur->left;
} else {
TreeNode*p = st.top();
if (flag) last_val = p->val;
else {
int cur_val = p->val;
if (last_val < cur_val) last_val = cur_val;
else return false;
}
cur = p->right;
st.pop();
flag = false;
}
}
return true;
}
};
改写中序遍历,中序遍历节点值从小到大即为二叉搜索树。
11.求二叉树中node节点的后续节点。只给出node节点,不给出root根节点。
二叉树节点的结构如下,相比于一般的二叉树,这颗二叉树多了一个parent字段,该字段指向父节点。
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
struct TreeNode *parent;
TreeNode(int x) :
val(x), left(NULL), right(NULL) ,parent(NULL) {
}
};
方法一:
(1)根据parent节点往上找到root根节点。
(2)中序遍历找出后续节点
方法二:
(1)如果node节点有右子树,那么后续节点就是node节点最左边的节点。
(2)node节点没有右子树,如果node节点是他的父节点的左孩子,那么后续节点就是他的父节点。如果是他的父节点的右孩子,那么往上移动一个节点,判断该节点是否是他父节点的左孩子,如果是那么这个节点的父节点就是node节点的后续节点,如果向上移动一直到空节点都有找到满足此条件的节点,那么说明node节点不存在后续节点。
12.一张纸从下往上叠n次,打开后折痕在上的记为up,折痕在下的记为down,从上往下记录折痕并返回。
class FoldPaper {
public:
vector<string> foldPaper(int n) {
// write code here
vector<string> vs;
checknode(vs,n,"down");
return vs;
}
void checknode(vector<string> &vs,int n,string val)
{
if(n==0) return ;
checknode(vs,n-1,"down");
vs.push_back(val);
checknode(vs,n-1,"up");
}
};
改写中序遍历,无需构造折痕二叉树,只需模拟中序遍历过程即可。
13.二叉搜索中有个节点值调换了位置,找出这两个节点并返回。
class FindErrorNode {
public:
vector<int> findError(TreeNode* root) {
stack<TreeNode*> s;
TreeNode* cur=root;
vector<int> v,res;
bool flag = true;
int last = 0;
int cur_val = 0;
int times = 0;
while(!s.empty()||cur!=NULL) {
if(cur!=NULL) {
s.push(cur);
cur=cur->left;
} else {
cur=s.top();
v.push_back(cur->val);
if (flag) last = cur->val;
else cur_val = cur->val;
if (!flag && cur_val < last && !times) {
res.push_back(last);
res.push_back(cur_val);
++times;
} else if (!flag && cur_val < last && times){
res.pop_back();
//if (res[0] < cur_val) {
//res.push_back(cur_val);
//} else {
//int tmp = res[0];
//res.clear();
res.push_back(cur_val);
//res.push_back(tmp);
//}
break;
}
s.pop();
cur=cur->right;
if (!flag) last = cur_val;
flag = false;
}
}
if (res[0] > res[1]) swap(res[0], res[1]);
return res;
}
};
中序遍历,不需要把整个中序遍历结果存下来,发现不是升序节点记录一下即可。
14.求一颗二叉树中的最大距离。
class LongestDistance {
public:
int postOrder(TreeNode * root, int &res) {
if (!root) {
return 0;
}
int lmax = postOrder(root->left, res);
int rmax = postOrder(root->right, res);
res = max(lmax+rmax+1, res);
return (max(lmax,rmax)+1);
}
int findLongest(TreeNode* root) {
int res = 0;
postOrder(root, res);
return res;
}
};
改写后序遍历,统计左右子树上的距离,然后不断记录最大距离。
15.找出最大二叉搜索子树.
class MaxSubtree {
public:
TreeNode* getMax(TreeNode* root) {
if(root==NULL)
return NULL;
int numnode,maxnode,minnode;
return get_max(root,numnode,maxnode,minnode);
}
TreeNode* get_max(TreeNode* p,int &numnode,int &maxnode,int &minnode)
{
if(NULL==p) {
maxnode=-9999999;
minnode=9999999;
numnode=0;
return NULL;
}
int lmax,lmin,lnum;
TreeNode* lnode=get_max(p->left,lnum,lmax,lmin);
int rmax,rmin,rnum;
TreeNode* rnode=get_max(p->right,rnum,rmax,rmin);
maxnode=max(rmax,p->val);
minnode=min(lmin,p->val);
if(lmax<p->val&&rmin>p->val&&lnode==p->left&&rnode==p->right) {
numnode=lnum+rnum+1;
return p;
}
if(lnum>rnum) {
numnode=lnum;
return lnode;
}
else {
numnode=rnum;
return rnode;
}
}
};
改写后序遍历过程。
总结
使用队列可以实现bfs广度优先,使用栈可以 实现dfs深度优先。适当借助一些额外的变量比如last当前栈顶节点,nlast最新弹出的节点等,会起到意想不到的结果。很多与二叉树相关的题目其实把遍历算法修改一下即可解题。