P1132已知先後序和中序求先序排列解題報告
Name: 已知先後序和中序,求出它的先序排列
Copyright: 始發於goal00001111的專欄;允許自由轉載,但必須註明作者和出處
Author: goal00001111
Date: 11-12-08 10:58
Description:
題目描述:
描述 Description
給出一棵二叉樹的中序與後序排列。求出它的先序排列。(約定樹結點用不同的大寫字母表示,長度≤8)。
輸入格式 Input Format
第一行為二叉樹的中序序列
第二行為二叉樹的後序序列
輸出格式 Output Format
一行,為二叉樹的先序序列
樣例輸入 Sample Input
BADC BDCA
樣例輸出 Sample Output
ABCD
題目分析:
已知中序和前序排列,或者已知中序和後序序列,都能夠構造一棵二叉樹,此題考查後者。
前序遍歷的規律是:輸出根結點,輸出左子樹,輸出右子樹;
中序遍歷的規律是:輸出左子樹,輸出根結點,輸出右子樹;
後序遍歷的規律是:輸出左子樹,輸出右子樹,輸出根結點;
根據中序和後序序列的規律,我們可以知道構造二叉樹的過程是一個遞迴的過程,根據給定的 中序和後序序列,建立二叉樹的根結點,並將中序序列劃分為左子樹序列和右子樹序列,然後分別把左子樹序列和右子樹序列遞迴的構造左子樹和右子樹。
具體的演算法是分別用陣列mid[lm..rm]
很明顯,若pos == lm,則lenL = 0,說明根結點無左子樹;若pos == rm,則lenR = 0,說明根結點無右子樹;
根據後序序列的規律,可以知道根結點t的左子樹的後序排列為post[lp..lp+lenL-1];
根結點t的右子樹的後序排列為post[lp+lenL..rp-1]。
採用同樣的方法遞迴構造根結點t的左右子樹。
以樣例輸入為例:
中序序列:BADC
後序序列:BDCA
1.得到根結點t->data = 'A';
2.遍歷中序序列mid,得到mid[pos] = 'A',lenL = 1;
3.得到根結點t的左子樹的中序序列為mid[lm..pos-1] = "B",右子樹的中序序列為mid[pos+1..rm] = "DC";根結點t的左子樹的後序序列為post[lp..lp+lenL-1] = "B",右子樹的後序序列為post[lp+lenL..rp-1] = "DC";
4.遞迴構造根結點t的左子樹t->lc,設t= t->lc:
1.得到根結點t->data = 'B';
2.遍歷中序序列mid,得到mid[pos] = 'B',lenL = 0;
3.得到根結點t的左右子樹均為空,返回呼叫函式。
5. 遞迴構造根結點t的右子樹t->rc,設t= t->rc:
1.得到根結點t->data = 'C';
2.遍歷中序序列mid,得到mid[pos] = 'C',lenL = 1;
3.得到根結點t的左子樹的中序序列為"D",後序序列為"D";右子樹為空,
4.遞迴構造根結點t的左子樹t->lc;
6.最後得到整棵二叉樹,前序遍歷二叉樹,得到前序序列:ABCD。
說明:
演算法思想:遞迴和分治。
資料結構:陣列,二叉樹。
時間複雜度:O(N);
空間複雜度:O(N);
程式語言:分別用c++和pascal實現。
附註:
若題目改為已知中序和前序序列,輸出後序序列,因為先序序列pre[lp..rp]中根結點是第一個元素,所以只需將函式中的根結點資料由t->data = post[rp] 改為t->data = pre[lp]; 然後在遞迴構造左右子樹時注意左子樹中序序列為mid[lm+1..pos-1],右子樹中序序列為mid[pos+1..rm],左子樹先序序列為pre[lp+1..lp+lenL],右子樹先序序列為pre[lp+lenL+1..rp],
最後後序遍歷二叉樹就行了。
c++程式碼:
#include<iostream>
#include<string>
using namespace std;
typedef struct BTNode{
char data;
struct BTNode *lc, *rc;//左,右孩子指標
} *BTree;
void PostBtree(BTree & t, string mid, string post, int lm, int rm, int lp, int rp);
void Preorder(BTree p);
int main(int argc, char* argv[])
{
string mid, post;
BTree root;
cin >> mid;
cin >> post;
PostBtree(root, mid, post, 0, mid.size()-1, 0, post.size()-1);
Preorder(root);
system("pause");
return 0;
}
/*
函式名稱:PostBtree
函式功能:給出一棵二叉樹的中序與後序序列,構造這棵二叉樹。
輸入引數: BTree & t:二叉樹的結點t
string mid:儲存了二叉樹的中序序列的字串
string post:儲存了二叉樹的後序序列的字串
int lm, int rm:二叉樹的中序序列在陣列mid中的左右邊界
int lp, int rp:二叉樹的後序序列在陣列post中的左右邊界
*/
void PostBtree(BTree & t, string mid, string post, int lm, int rm, int lp, int rp)
{
t = new BTNode; //構造二叉樹根結點
t->data = post[rp];
t->lc = t->rc = NULL;
int pos = lm;
while (mid[pos] != post[rp])
pos++;
int lenL = pos - lm;
if (pos > lm)//有左孩子,遞迴構造左子樹
PostBtree(t->lc, mid, post, lm, pos-1, lp, lp+lenL-1);
if (pos < rm)//有右孩子,遞迴構造右子樹
PostBtree(t->rc, mid, post, pos+1, rm, lp+lenL, rp-1);
}
//先序遍歷
void Preorder(BTree p)
{
if(p != NULL)
{
cout << p->data; //輸出該結點
Preorder(p->lc); //遍歷左子樹
Preorder(p->rc); //遍歷右子樹
}
}
PASCAL程式碼:
PROGRAM EXAMP1132(INPUT, OUTPUT);
TYPE
BTree = ^node;
node = record
data : char;
lc, rc : BTree;
end; {record}
VAR
mid, post : string;
root : BTree;
{先序遍歷}
PROCEDURE Preorder(t : BTree);
begin
if t <> nil then
begin
write(t^.data);{輸出該結點}
Preorder(t^.lc); {遍歷左子樹}
Preorder(t^.rc); {遍歷右子樹}
end;
end;
{已知後序和中序,構造二叉樹}
PROCEDURE PostBtree(var t : BTree; mid, post : string; lm, rm, lp, rp : integer);
var
pos, lenL : integer;
begin
new(t);
t^.data := post[rp];
t^.lc := nil;
t^.rc := nil;
pos := lm;
while mid[pos] <> post[rp] do
inc(pos);
lenL := pos - lm;
if pos > lm then {有左孩子,遞迴構造左子樹}
PostBtree(t^.lc, mid, post, lm, pos-1, lp, lp+lenL-1);
if pos < rm then {有右孩子,遞迴構造右子樹}
PostBtree(t^.rc, mid, post, pos+1, rm, lp+lenL, rp-1);
end; {PostBtree}
BEGIN
readln(mid);
readln(post);
PostBtree(root, mid, post, 1, ord(mid[0]), 1, ord(post[0]));
Preorder(root);
END.