点:二叉树的理解,代码能力
题意:由前序遍历序列和中序遍历的序列,创建对应的二叉树。
如前序序列:16, 7, 6, 3, 14, 12, 8, 15, 28, 23, 27, 41, 30, 29, 55
和中序序列:3, 6, 7, 8, 12, 14, 15, 16, 23, 27, 28, 29, 30, 41, 55
剑指offer面试题6
思路:1、前序遍历序列的第一个元素就是根节点,这和后序遍历整好相反,后续遍历序列是最后一个元素是根节点
2、对于典型的二叉树,即“左节点比父节点的值小,右节点比父节点的值大”这样的二叉树,根本无需中序序列,仅前序或后序即可唯一确定一个二叉树。
3、但这类题中想要得出的二叉树是不用遵循“左节点比父节点的值小,右节点比父节点的值大”的规则的,这时单独的前序遍历或后序遍历包括按层遍历,其序列无法对应一个唯一二叉树。必须有中序遍历序列。
原因非常简单:前序和后序在不遵循左小右大规则时,无法确定叶子节点是其父节点的左孩子还是右孩子。如父节点A有一个子节点B,无论左孩子是B还是右孩子是B,前序遍历序列结果永远是AB,后序遍历序列结果也永远是BA,这时,即便同时给定前序遍历序列和后序遍历序列,依然不知道到底B是A的左子节点还是右子节点。
然而无论是前序还是后序,若再给定中序遍历序列,那么就可以确定B是A的左子节点还是右子节点,因为中序本身就是给定了子节点是父节点的左孩子还是右孩子。
4、基于上面的1-3,创建方式如下:
1、前序遍历序列:第一个是根节点,由中序序列确定左子树的部分,右子树的部分
2、左子树第一个节点,左子树的根节点,然后如法炮制第1步,右子树亦然,这些典型使用递归
中序的左右界定的功能:有效的区分了左右子树以及左右叶子节点,这是这类题的关键
3、递归创建节点,注意递归结束条件即边界条件
后序遍历序列 + 中序遍历序列、按层遍历序列 + 中序遍历序列,都可以类似的道理生成唯一二叉树,其他都不行,原因就是不能确定孩子是左还是右。
代码:
#include
template struct Node {
T val;
Node *lchild;
Node *rchild;
Node(T _val):val(_val), lchild(nullptr), rchild(nullptr) {}
};
template class Btree {
Node *root;
public:
Btree():root(nullptr) {}
void Free (Node *cur) {
if (cur) {
Free(cur->lchild);
Free(cur->rchild);
delete cur;
cur = nullptr;
}
}
~Btree () {
Free(root);
}
void Add (T val) {
if (!root) {
root = new Node(val);
} else {
Node *cur = root;
while (cur) {
if (cur->val > val) {
if (cur->lchild) {
cur = cur->lchild;
} else {
cur->lchild = new Node(val);
break;
}
} else if (cur->val < val) {
if (cur->rchild) {
cur = cur->rchild;
} else {
cur->rchild = new Node(val);
break;
}
} else {
break;
}
}
}
}
void Pre (Node *cur) {
if (cur) {
std::cout << cur->val << "\t";
Pre(cur->lchild);
Pre(cur->rchild);
}
}
void pre () {
Pre(root);
std::cout << std::endl;
}
void Mid (Node *cur) {
if (cur) {
Mid(cur->lchild);
std::cout << cur->val << "\t";
Mid(cur->rchild);
}
}
void mid () {
Mid(root);
std::cout << std::endl;
}
};
void recreate (int *pre, int *mid, const int size, int prestart, int preend, int midstart, int midend, Btree &bt) {
if (preend - prestart != midend - midstart) {
return;
}
if (prestart > preend || midstart > midend || prestart >= size || preend >= size || !pre || !mid) {
return;
}
std::cout << "inesrt node idx " << prestart << "(" << pre[prestart] << ")" << std::endl;
bt.Add(pre[prestart]);
if (prestart == preend && midstart == midend) {
return;
}
int mididx = -1, count = 0;
for (int i = midstart; i <= midend; i++) {
if (pre[prestart] == mid[i]) {
mididx = i;
break;
} else {
++count;
}
}
std::cout << "left(pre; mid): " << (prestart + 1) << ", " << prestart + count << "; " << midstart << ", " << (mididx - 1) << std::endl;
std::cout << "right(pre; mid): " << (prestart + count + 1) << ", " << preend << "; " << (mididx + 1) << ", " << midend << std::endl << "---------------------" << std::endl;
if (mididx > 0) {
recreate(pre, mid, size, prestart + 1, prestart + count, midstart, mididx - 1, bt);
recreate(pre, mid, size, prestart + count + 1, preend, mididx + 1, midend, bt);
}
}
void recreate_btree_by_pre_and_mid (int *pre, int *mid, int size) {
Btree bt;
recreate(pre, mid, size, 0, size - 1, 0, size - 1, bt);
bt.pre();
bt.mid();
}
int main () {
int pre[] = {16, 7, 6, 3, 14, 12, 8, 15, 28, 23, 27, 41, 30, 29, 55};
int mid[] = {3, 6, 7, 8, 12, 14, 15, 16, 23, 27, 28, 29, 30, 41, 55};
recreate_btree_by_pre_and_mid(pre, mid, sizeof(pre)/sizeof(pre[0]));
return 0;
}