根据前序遍历-中序遍历结果 来还原一颗二叉树
来源:互联网 发布:手机文件查看软件 编辑:程序博客网 时间:2024/05/16 10:21
前序-中序-二叉树还原
对于这样一个二叉树
A
/ /
B C
/ / / /
D E F G
/ / / / / / / /
H I J K M N O P
前序遍历的结果是:ABDHIEJKCFMNGOP,我们称之为PRE
中序遍历的结果是:HDIBJEKAMFNCOGP,我们称之为MID
还原的方法是:
(1)pi指向PRE的第一个字符
(2)用pi从PRE中取一个字符pc
(3)查找pc在MID中出现的位置mi
(4)根据mi确定pc与前一个字符的关系(左孩子/右孩子/没有关系)
(5)pi+1
(6)反复重复(2)~(5)步,直到pi超过了PRE的长度
可以看到,问题的关键在于步骤(4),即如何确定pc与前一个字符的关系。
在这里我们要用到两个辅助结构:
(1)一个链表,存放Helper结构
(2)一个Helper结构,用于记录每一个节点在MID中的下标
链表我们可以用STL的list,Helper的结构如下
struct Helper {
TreeNode* node;
int index;
public:
Helper(TreeNode* pNode, int idx)
: node(pNode), index(idx) { }
};
当然,二叉树的节点也要有:struct TreeNode {
char data;
TreeNode* lChild;
TreeNode* rChild;
public:
TreeNode(char c) : data(c), lChild(0), rChild(0) { }
};
好了,我们一步一步来看看如何解决这个还原二叉树的问题
(1) (A, 7)
取PRE第一个字符,然后通过Helper放入list中,并构造出一个list的初始环境
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
PRE: A B D H I E J K C F M N G O P
MID: H D I B J E K A M F N C O G P
【list】
nod 0 A 0
idx -1 7 15
cur ^
prv
cur, prv都是指向list中元素的指针,头尾两个元素表示边界
(2) (B, 3)
取PRE第二个字符,根据list来判定它为谁的左孩子/右孩子
可以看到,pc=B,其在MID中的下标mi为3,简略为(B, 3),以后都这么简略
(B, 3)在(A, 7)左边,因为3 < 7,所以B为A的左孩子,并插入到list中,注意
cur指针的变动,以后每次插入元素后cur指针都会变动。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
PRE: A B D H I E J K C F M N G O P
MID: H D I B J E K A M F N C O G P
【list】
nod 0 B A 0
idx -1 3 7 15
cur ^
prv
(3) (D, 1)
(D, 1)在(B, 3)左边,因为 1 < 3,所以D是B的左孩子,插入list中
【list】
nod 0 D B A 0
idx -1 1 3 7 15
cur ^
prv
(4) (H, 0)
(H, 0)在(D, 1)左边,H是D的左孩子,插入list中
【list】
nod 0 H D B A 0
idx -1 0 1 3 7 15
cur ^
prv
(5) (I, 2)
(I, 2)不在(H, 0)的左边,因为H已经是最左边了,于是我们要prv = cur,cur++
【list】
nod 0 H D B A 0
idx -1 0 1 3 7 15
cur ^
prv ^
(I, 2)不在(H, 0),(D, 1)中间,因为0 < 2 < 1不成立,把prv指向的(H, 0)删除
(6) (I, 2)
(I, 2)不在(D, 1)左边,prv = cur, cur++
【list】
nod 0 D B A 0
idx -1 1 3 7 15
cur ^
prv ^
(I, 2)在(D, 1),(B, 3)中间,因为1 < 2 < 3,所以I是D的右孩子
然后把D删除,把I插入,cur指向I
【list】
nod 0 I B A 0
idx -1 2 3 7 15
cur ^
prv
(7) (E, 5)
(E, 5)不在(I, 2)左边,prv = cur, cur++
【list】
nod 0 I B A 0
idx -1 2 3 7 15
cur ^
prv ^
结果(E, 5)不在(I, 2),(B, 3)中间,把prv指向的(I, 2)删除
(8) (E, 5)
(E, 5)不在(B, 3)左边,prv = cur, cur++
【list】
nod 0 B A 0
idx -1 3 7 15
cur ^
prv ^
(E, 5)在(B, 3),(A, 7)中间,于是E是B的右孩子,删除B,插入E,cur指向E
【list】
nod 0 E A 0
idx -1 5 7 15
cur ^
prv
(9) (J, 4)
(J, 4)是在(E, 5)左边的,于是插入(J, 4),cur指向(J, 4)
【list】
nod 0 J E A 0
idx -1 4 5 7 15
cur ^
prv
(10)(K, 6)
(K, 6)不在(J, 4)左边,prv = cur, cur++
(K, 6)不在(J, 4),(E, 5)中间,因此删除(J, 4)
【list】
nod 0 E A 0
idx -1 5 7 15
cur ^
prv
(11)(K, 6)
(K, 6)不在(E, 5)左边,prv = cur, cur++
(K, 6)在(E, 5),(A, 7)中间,于是K是E的右孩子,删除E,插入K,cur指向K
【list】
nod 0 K A 0
idx -1 6 7 15
cur ^
prv
***到此为止,A的左子树都已经还原完成了,右子树的还原和左子树一样,以下我只给出
***C的确定方法,其余就省略了。
(12)(C, 11)
(C, 11)不在(K, 6)左边,prv = cur, cur++
(C, 11)不在(K, 6),(A, 7)中间,删除(K, 6)
【list】
nod 0 A 0
idx -1 7 15
cur ^
prv
(13)(C, 11)
(C, 11)不在(A, 7)左边,prv = cur, cur++
(C, 11)在(A, 7),(0, 15)中间,于是C是A的右孩子,删除A,插入C,cur指向C
【list】
nod 0 C 0
idx -1 11 15
cur ^
prv
以下省略。。。。
对了,千万不要忘记在确定X节点是Y节点的左/右孩子后要做相应的链接操作哦。
下面给出算法的C++表示,这里我们用iterator来表示cur, prv指针。我们之所以要用list是
因为list在插入/删除元素后iterator不会失效。还有一点,因为list<>::iterator不支持
random access,所以我们要用prv, cur两个iterator表示一前一后,否则的话直接用cur和
cur + 1就行了,这样的话就简单多了。
然后通过验证可以发现,其二叉树还原后的形态的确是我们希望的形态。
- #include <iostream>
- #include <stack>
- #include <string>
- #include <list>
- using namespace std;
- struct TreeNode {
- char data;
- TreeNode* lChild;
- TreeNode* rChild;
- public:
- TreeNode(char c) : data(c), lChild(0), rChild(0) { }
- };
- struct Helper {
- TreeNode* node;
- int index;
- public:
- Helper(TreeNode* pNode, int idx)
- : node(pNode), index(idx) { }
- };
- int main() {
- void preorderTraversal(TreeNode* pTree);
- void inorderTraversal(TreeNode* pTree);
- void Pre_Mid_Restore(string pre, string mid, TreeNode*& result);
- /* A
- / /
- B C
- / / / /
- D E F G
- / / / / / / / /
- H I J K M N O P
- */
- string Preorder1 = "ABDHIEJKCFMNGOP";
- string Midorder1 = "HDIBJEKAMFNCOGP";
- string Preorder2 = "ABDFCEG";
- string Midorder2 = "BFDAEGC";
- string Preorder3 = "ABDCEFG";
- string Midorder3 = "DBAFEGC";
- TreeNode* res = 0;
- Pre_Mid_Restore(Preorder1, Midorder1, res);
- preorderTraversal(res);
- inorderTraversal(res);
- Pre_Mid_Restore(Preorder2, Midorder2, res);
- preorderTraversal(res);
- inorderTraversal(res);
- Pre_Mid_Restore(Preorder3, Midorder3, res);
- preorderTraversal(res);
- inorderTraversal(res);
- cin.get();
- }
- //前序-中序-二叉树还原
- void Pre_Mid_Restore(string pre, string mid, TreeNode*& result) {
- size_t pi = 0; //前序遍历所得字符串的下标
- size_t mi = 0; //中序遍历所得字符串的下标
- char pc; //前序遍历的字符
- result = new TreeNode(pre[pi]); //前序遍历的第一个字符是根节点
- TreeNode* pNode = 0;
- mi = mid.find(pre[pi]); //在中序字符串中找到前序字符串的当前字符位置
- list<Helper> helper;
- helper.push_back(Helper(0, -1));
- helper.push_back(Helper(result, mi));
- helper.push_back(Helper(0, mid.size()));
- list<Helper>::iterator cur = helper.begin();
- cur++;
- /*
- 下标 -1 7 15
- 节点 0 A 0
- iter ^
- */
- for(pi = 1; pi < pre.size(); pi++) {
- pc = pre[pi]; //前序字符串的当前字符
- mi = mid.find(pc); //在中序字符串中的位置
- while(true) {
- if (mi < (*cur).index) { //在左边就是左孩子
- pNode = new TreeNode(pc);
- (*cur).node->lChild = pNode;
- cur = helper.insert(cur, Helper(pNode, mi));
- break;
- }
- else { //不在左边
- list<Helper>::iterator prv = cur;
- cur++;
- if((*prv).index < mi && mi < (*cur).index) { //在中间就是右孩子
- pNode = new TreeNode(pc);
- (*prv).node->rChild = pNode;
- helper.erase(prv);
- cur = helper.insert(cur, Helper(pNode, mi));
- break;
- } //不在中间就不是右孩子
- else {
- helper.erase(prv);
- continue;
- }
- }
- //some erase work
- }
- }
- }
- //前序遍历
- void preorderTraversal(TreeNode* pTree) {
- stack<TreeNode*> treeStack;
- do {
- while(pTree != 0) {
- cout << pTree->data;
- if(pTree->rChild != 0) {
- treeStack.push(pTree->rChild);
- }
- pTree = pTree->lChild;
- }
- if(!treeStack.empty()) {
- pTree = treeStack.top();
- treeStack.pop();
- }
- }while(!treeStack.empty() || pTree != 0);
- cout << endl;
- }
- //中序遍历
- void inorderTraversal(TreeNode* pTree) {
- stack<TreeNode*> treeStack;
- do {
- while(pTree != 0) {
- treeStack.push(pTree);
- pTree = pTree->lChild;
- }
- if(!treeStack.empty()) {
- pTree = treeStack.top();
- treeStack.pop();
- cout << pTree->data;
- pTree = pTree->rChild;
- }
- }while(!treeStack.empty() || pTree != 0);
- cout << endl;
- }
- 根据前序遍历-中序遍历结果 来还原一颗二叉树
- 根据后序-中序遍历结果 来还原一颗二叉树
- 根据层次遍历和中序遍历的结果还原一颗二叉树
- 根据层次遍历和中序遍历的结果还原一颗二叉树
- 【二叉树】根据二叉树的中序遍历和前序遍历,还原二叉树
- 根据二叉树的前序遍历和中序遍历的结果,重建二叉树
- 根据后序遍历和中序遍历结果还原二叉树
- 二叉树问题-根据前序遍历结果和中序遍历结果得出后序遍历结果
- 根据前序遍历和中序遍历还原二叉树
- 数据结构——根据前序遍历和中序遍历还原二叉树
- 根据前序遍历和中序遍历结果构造二叉树
- 根据前序遍历和中序遍历结果重建二叉树(递归方法)
- 根据前序遍历和中序遍历结果构造二叉树
- 根据二叉树的前序遍历和中序遍历(或者中序遍历和后序遍历)还原二叉树
- 根据前序遍历,中序遍历构建二叉树
- 根据前序遍历、中序遍历重建二叉树
- 根据前序遍历中序遍历求二叉树
- 题目1078:二叉树遍历(根据前序和中序遍历结果,获得后序遍历)
- 培养孩子形成六大好习惯
- centos yum 包管理
- .Net部署二三事之一——如何为VS安装文件MSI制作更新补丁MSP
- 关于ajax toolkit中的下拉菜单CascadingDropDown 的method error 500问题
- 魔方图最终版本奇数偶数均已实现
- 根据前序遍历-中序遍历结果 来还原一颗二叉树
- java计算器源代码
- ZendFramework中使用Lucene全文检索
- B/S和C/S模式比较
- SAP Query Reporting
- Prioritizing Web Usability
- Solving the PowerPoint Predicament: Using Digital Media for Effective Communication
- neogeo模拟街机游戏
- Security Threat Mitigation and Response: Understanding Cisco Security MARS