一、二叉树
1、用递归方法实现二叉树的先序、中序、后序遍历
class
TreeToSequence
{
public
:
void
preOrder(TreeNode*
root,vector<int
>
&pre) {
if
(!root)
return
;
pre.push_back(root->val);
preOrder(root->left,pre);
preOrder(root->right,pre);
}
void
inOrder(TreeNode*
root,vector<int
>
&in) {
if
(!root)
return
;
inOrder(root->left,in);
in.push_back(root->val);
inOrder(root->right,in);
}
void
postOrder(TreeNode*
root,vector<int
>
&post) {
if
(!root)
return
;
postOrder(root->left,post);
postOrder(root->right,post);
post.push_back(root->val);
}
vector<vector<
int
>
> convert(TreeNode* root) {
vector<vector<
int
>
> vRecur;
vector<
int
>
pre,in,post;
preOrder(root,pre);
inOrder(root,in);
postOrder(root,post);
vRecur.push_back(pre);
vRecur.push_back(in);
vRecur.push_back(post);
return
vRecur;
}
};
2、用非递归方法实现二叉树的先序、中序、后序遍历
class TreeToSequence {
public:
vector<int> preOrder(TreeNode* root) {
vector<int> pre;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode* temp = s.top();
pre.push_back(temp->val);
s.pop();
if (temp->right)
s.push(temp->right);
if (temp->left)
s.push(temp->left);
}
return pre;
}
vector<int> inOrder(TreeNode* root) {
vector<int> in;
stack<TreeNode*> s;
TreeNode* temp = root;
while (temp || !s.empty()){
while (temp) {
s.push(temp);
temp = temp->left;
}
temp = s.top();
s.pop();
in.push_back(temp->val);
temp = temp->right;
}
return in;
}
vector<int> postOrder(TreeNode* root) {
vector<int> post;
stack<TreeNode*> s;
TreeNode *temp = root,*previsited = NULL;
while (temp || !s.empty()){
while (temp) {
s.push(temp);
temp = temp->left;
}
temp = s.top();
if (!temp->right || temp->right == previsited) {
post.push_back(temp->val);
s.pop();
previsited = temp;
temp = NULL;
}
else
temp = temp->right;
}
return post;
}
vector<vector<int> > convert(TreeNode* root) {
vector<vector<int> > vNonRecur;
if (!root)
return vNonRecur;
vNonRecur.push_back(preOrder(root));
vNonRecur.push_back(inOrder(root));
vNonRecur.push_back(postOrder(root));
return vNonRecur;
}
};
3、按照层次打印二叉树,且不同层的数据要打印在不同行。可把不同层的数据存在不同的数组中模拟打印过程。
解法:①二叉树的层序遍历要用到队列。队头结点出队则接着将其左右子节点入队。
②不同层打印在不同行就要考虑在合适的节点处换行。定义两个指针curLast和nexLast分别记录正在打印的行的最右结点和下一行的最右结点。nexLast跟踪入队结点,则当打印结点是当前层最右结点curLast时紧接着的入队结点也是下一行的最右结点;若当前打印的结点为curLast则换行,接着将下一行最右结点指针nexLast赋值给curLast。
class TreePrinter {
public:
vector<vector<int> > printTree(TreeNode* root) {
vector<vector<int> > res;
vector<int> temp;
queue<TreeNode*> q;
TreeNode *curLast = root,*nexLast = NULL,*cur;
q.push(root);
while (!q.empty()) {
cur = q.front();
temp.push_back(cur->val);
q.pop();
if (cur->left) {
q.push(cur->left);
nexLast = cur->left;
}
if (cur->right) {
q.push(cur->right);
nexLast = cur->right;
}
if (cur == curLast) {
res.push_back(temp);
temp.clear();
curLast = nexLast;
}
}
return res;
}
};
4、二叉树的先序序列化
解法:用递归先序遍历更简单,遇到空则在字符串str后追加#!,非空则追加‘数值!’。
注意:int型数据要先转换成字符串类型才能追加到字符串末尾,用stringstream可以实现转换。
class TreeToString {
public:
void preSerial(TreeNode* root,string &str) {
if (!root) {
str += "#!";
return;
}
str += int2string(root->val) + ‘!‘;
preSerial(root->left,str);
preSerial(root->right,str);
}
string int2string(int val) {
stringstream ss;
ss << val;
return ss.str();
}
string toString(TreeNode* root) {
string str;
preSerial(root,str);
return str;
}
};
5、二叉树先序反序列化
解法:①先将字符串序列转换成字符数组的形式。②遍历字符数组,遇到不是#的元素,则新建结点,并用剩下的元素构造这个结点的左子树;遇到#则不新建结点,并且当前正在构建的分支结束,用剩下的元素构造当前分支的父节点的右子树。
6、判断二叉树是否为平衡二叉树
平衡二叉树:任何结点的左右子树高度之差都不大于1.
解法:利用二叉树的后序遍历,统计左右子树的高度,同时通过一个全局的bool变量记录遍历过程中是否出现非平衡二叉树,一旦bool被置为false则说明有子树不是平衡二叉树,直接返回,最后返回这个bool变量即可。
class CheckBalance {
public:
int postVisit(TreeNode* root,bool &isBalance ) {
// 若当前结点空,则直接返回0,即当前子树的高度不增加
if (!root)
return 0;
// 若前结点不空,则统计其左子树的高度
int nL = postVisit(root->left,isBalance );
// 如果在遍历当前节点的左子树的过程中出现非平衡二叉树,此时不再遍历右子树,直接返回
if (!isBalance )
return nL;
// 若当前结点不空且左子树是平衡二叉树,则遍历右子树,统计其高度
int nR = postVisit(root->right,isBalance );
// 如果在遍历右子树的过程中出现非AVL,此时不再判断当前结点为根的子树是否是平衡二叉树,直接返回
if (!isBalance )
return nR;
// 如果当前节点的左右子树都是平衡二叉树,则根据左右子树的高度判断当前结点为根的子树是否是平衡二叉树
if (abs(nL-nR) > 1)
isBalance = false;
// 不管当前结点为根的子树是不是平衡二叉树,都应该使子树的高度增加1
return 1+max(nL,nR);
}
bool check(TreeNode* root) {
bool isBalance
= true;
int height = postVisit(root,isBalance );
return isBalance ;
}
};
7、判断二叉树是不是二叉搜索树
解法:二叉树的中序遍历,若得到的遍历序列是递增的则原二叉树是二叉搜索树。
bool checkSearch(TreeNode* root)
{
stack<TreeNode*>
s;
TreeNode *cur = root,*last
=NULL;
while(cur
|| !s.empty()) {
while(cur)
{
s.push(cur);
cur = cur->left;
}
cur = s.top();
if(!last)
last = cur;
if(cur->val
>= last->val) {
s.pop();
cur = cur->right;
last = cur;
}
else
return false;
}
return
true;
}
8、判断二叉树是不是完全二叉树
解法:利用层序遍历。如果当前结点左孩子空右孩子不空,则肯定不是完全二叉树;如果左右孩子都不空,则正常遍历;如果左孩子不空右孩子空,则在完全二叉树中这个左孩子一定是最后一层最右的结点,并且这种情况只可能发生一次,用n来记录这种情况发生的次数,n>1则返回false。
class
CheckCompletion
{
public
:
bool
chk(TreeNode* root) {
queue<TreeNode*>
q;
q.push(root);
int
n
= 0
;
while
(!q.empty())
{
TreeNode*
temp = q.front();
q.pop();
if
(!temp->left
&& temp->right)
return
false
;
if
(temp->left
&& temp->right) {
q.push(temp->left);
q.push(temp->right);
}
if
(temp->left
&& !temp->right) {
if
(temp->left->left
|| temp->left->right)
return
false
;
n++;
if
(n
> 1
)
return
false
;
q.push(temp->left);
}
}
return
true
;
}
};
9、折纸问题
描述:把纸条竖着放在桌上,然后从纸条的下边向上对折,压出折痕后再展开。此时有1条折痕,突起的方向指向纸条的背面,这条折痕叫做“下”折痕 ;突起的方向指向纸条正面的折痕叫做“上”折痕。如果每次都从下边向上边
对折,对折N次。请从上到下计算出所有折痕的方向。
解法:折痕方向的分布其实是一棵满二叉树。根节点是0,所有结点的左右孩子都分别为0,1。0代表“下”折痕,1代表“上”折痕。可以用递归算法来实现。也可以用非递归方式实现。
递归解法:
class
FoldPaper
{
public
:
vector<string>
foldPaper(int
n)
{
vector<string>
ret;
foldPrint(
"down"
,
ret, n);
return
ret;
}
void
foldPrint(string
fold, vector<string>& ret, int
remain){
if
(remain
== 0
)
return
;
foldPrint(
"down"
,
ret, remain - 1
);
ret.push_back(fold);
foldPrint(
"up"
,
ret, remain - 1
);
}
};
非递归解法:
class
FoldPaper
{
public
:
vector<string>
foldPaper(int
n)
{
string
sLeft[] = {"down"
,
"down"
,
"up"
};
string
sRight[] = {"down"
,
"up"
,
"up"
};
vector<string>
lS(sLeft,sLeft+3
);
vector<string>
rS(sRight,sRight+3
);
vector<string>
last,odd,even = lS;
odd.push_back(
"down"
);
if
(n
== 1
)
{
return
odd;
}
if
(n
== 2
)
{
return
even;
}
int
i;
if
(n%
2
==
1
)
{
i
= 3
;
last
= odd;
}
else
{
i
= 4
;
last
= even;
}
vector<string>
res;
vector<string>::iterator
iter;
for
(;i
<= n;) {
iter
= last.begin();
while
(iter
< last.end()) {
res.insert(res.end(),lS.begin(),lS.end());
res.push_back(*iter);
res.insert(res.end(),rS.begin(),rS.end());
if
(iter
< last.end()-1
)
res.push_back(*(iter+
1
));
iter
= iter+2
;
}
last.swap(res);
res.clear();
i
= i+2
;
}
return
last;
}
};
10、一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请找到这两个错误节点并返回他们的值。小的值在前。
解法:二叉树的中序遍历可以检查出树是不是搜索树。用last指针跟踪上一个遍历的结点就可以比较遍历过程中是否出现降序。若调换了两个结点则最多会出现两次降序,若只有第一次,则降序的两个结点就是调换的结点,若有两次降序则第一次较大的结点和第二次较小的结点是调换的两个结点。
class FindErrorNode {
public:
vector<int> findError(TreeNode* root) {
stack<TreeNode*> s;
vector<int> res;
TreeNode *cur = root,*last =NULL;
while(cur || !s.empty()) {
while (cur) {
s.push(cur);
cur = cur->left;
}
cur = s.top();
s.pop();
if(last && cur->val < last->val) {
if (res.empty()) {
res.push_back(cur->val);
res.push_back(last->val);
}
else {
res.erase(res.begin());
res.insert(res.begin(),cur->val);
break;
}
}
last = cur;
cur = cur->right;
}
return res;
}
};
11、对于给定的一棵二叉树,求整棵树上节点间的最大距离。
最大距离的定义:从一个节点走到(可以向上走可以向下走)另一个节点需要经过的结点数(包括这两个结点)。
解法:一棵树的节点间最大距离只可能是一下三种情况。①左子树中的最大距离;②右子树中的最大距离;③左子树中距离根节点最远的结点和右子树中距离根节点最远的结点之间的距离。
采用二叉树的后序递归遍历可是实现。在递归的每一层都求得以当前结点为根节点的树中的最大距离和这棵树中距离根节点最远的结点距离根节点的距离。
class
LongestDistance
{
public
:
vector<
int
>
postVisit(TreeNode* root) {
vector<
int
>
resL,resR,res;
if
(!root)
{ // 如果当前节点为空,则以它为根节点的树中最大距离和最远距离都为0
res.push_back(
0
);
res.push_back(
0
);
return
res;
}
// 若不空则先后处理左右子树,返回左右子树的最大距离和最远距离
resL
= postVisit(root->left);
resR
= postVisit(root->right);
// 计算以当前结点为根节点的树的最大距离和最远距离
int
max1
= resL[0
],max2;
if
(resR[
0
]
> max1)
max1
= resR[0
];
if
(resL[
1
]+resR[
1
]+
1
>
max1)
max1
= resL[1
]+resR[
1
]+
1
;
max2
= resL[1
]>resR[
1
]?resL[
1
]+
1
:resR[
1
]+
1
;
res.push_back(max1);
res.push_back(max2);
return
res;
}
int
findLongest(TreeNode*
root) {
vector<
int
>
max = postVisit(root);
return
max[
0
];
}
};
12、寻找一颗二叉树中节点数最多的搜索树的根节点
解法一:只有两种情况。①当前结点的左子树的最大搜索树以当前结点的左孩子为根节点,右子树的最大搜索树以右孩子为根节点,且左子树的最大值小于根节点,右子树的最小值大于根节点,则当前结点就是以其为根节点的子树的最大搜索树,结点个数为左右子树中最大搜索树结点个数之和加1,最大值为右子树的最大值,最小值为左子树的最小值;②第①种情况不满足,则左右子树中结点数最多的最大搜索树为当前的最大搜索树,存在四种情况,如下代码所示。
class
MaxSubtree
{
public
:
typedef
struct {
vector<
int
>
num;
TreeNode*
rMax;
}
Dataset;
Dataset
postVisit(TreeNode* root) {
Dataset
datal,datar,data;
if
(!root)
{
data.num.insert(data.num.begin(),3,0);
data.rMax
= NULL;
return
data;
}
datal
= postVisit(root->left);
datar
= postVisit(root->right);
if
(root->left
&& root->right) {
if
(datal.rMax
== root->left &&
datar.rMax
== root->right &&
datal.num[
2
]
< root->val &&
datar.num[
1
]
> root->val) {
data.rMax
= root;
data.num.push_back(datal.num[
0
]+datar.num[
0
]+
1
);
data.num.push_back(datal.num[
1
]<datar.num[
1
]?datal.num[
1
]:datar.num[
1
]);
data.num.push_back(datal.num[
2
]<datar.num[
2
]?datar.num[
2
]:datal.num[
2
]);
}
else
{
if
(datal.num[
0
]>datar.num[
0
])
data
= datal;
else
if
(datal.num[
0
]<datar.num[
0
])
data
= datar;
else
data
= datal.rMax->val > datar.rMax->val?datal:datar;
}
}
else
if
(!root->left
&& !root->right) {
data.rMax
= root;
data.num.push_back(datal.num[
0
]+datar.num[
0
]+
1
);
data.num.push_back(root->val);
data.num.push_back(root->val);
}
else
if
(!root->left)
{
if
(datar.num[
1
]
> root->val && datar.rMax == root->right) {
data.rMax
= root;
data.num.push_back(datal.num[
0
]+datar.num[
0
]+
1
);
data.num.push_back(root->val);
data.num.push_back(datar.num[
2
]);
}
else
data
= datar;
}
else
{
if
(datal.num[
2
]
< root->val && datal.rMax == root->left) {
data.rMax
= root;
data.num.push_back(datal.num[
0
]+datar.num[
0
]+
1
);
data.num.push_back(datal.num[
1
]);
data.num.push_back(root->val);
}
else
data
= datal;
}
return
data;
}
TreeNode*
getMax(TreeNode* root) {
Dataset
resData = postVisit(root);
return
resData.rMax;
}
};
解法2:
class
MaxSubtree
{
public
:
bool
chk[550
];
void
get(TreeNode*
rt,int
*mn,
int
*mx,
int
*sz,TreeNode
*&ret,int
&msz){
int
v
= rt->val;
mn[v]
= 10101010
;
mx[v] = 0
;
chk[v]
= false
;
int
lsz
= 0
,rsz
= 0
,lmx
= 0
,rmx
= 0
,lmn
= 10101010
,rmn
= 10101010
;
bool
cl = true
,cr
= true
;
if
(rt->left)
get(rt->left,mn,mx,sz,ret,msz),lsz = sz[rt->left->val],lmn = mn[rt->left->val],lmx = mx[rt->left->val],cl = chk[rt->left->val];
if
(rt->right)
get(rt->right,mn,mx,sz,ret,msz),rsz = sz[rt->right->val],rmn = mn[rt->right->val],rmx = mx[rt->right->val],cr = chk[rt->right->val];
if
(lsz
!= -1
&&
rsz != -1
){
if
(cl
&& cr && lmx < v && v < rmn){
int
tmp
= lsz + rsz + 1
;
chk[v]
= true
;
if
(tmp
> msz || (tmp == msz && ret->val < v)){
ret
= rt; msz = tmp;
}
}
sz[v]
= lsz + rsz + 1
;
mx[v]
= max(v,lmx);
mn[v]
= min(v,lmn);
}
}
TreeNode*
getMax(TreeNode* root) {
TreeNode*
ret = NULL;
int
mn[
550
],mx[
550
],sz[
550
],msz
= 0
;
get(root,mn,mx,sz,ret,msz);
return
ret;
}
};