事前注文トラバーサルから(配列として)ツリーを構築する方法があることを私は知っています。より一般的な質問は、順序どおりのトラバーサルと事前順序のトラバーサルを考慮して、それを構築することです。この場合、インオーダートラバーサルは冗長ですが、間違いなく簡単になります。誰かが注文後のトラバーサルのためにそれを行う方法を教えてもらえますか?反復ソリューションと再帰ソリューションの両方が必要です。
スタックを使って繰り返しやってみましたが、ロジックがまったくうまくいかなかったので、ひどい乱雑な木になりました。同じことが再帰にも当てはまりました。
BSTのポストオーダートラバーサルからの配列がある場合、ルートが配列の最後の要素であることがわかります。ルートの左の子は配列の最初の部分を占め、ルートよりも小さいエントリで構成されます。次に、ルートよりも大きい要素で構成される右の子に従います。 (両方の子が空の場合があります)。
________________________________
| | |R|
--------------------------------
left child right child root
したがって、主な問題は、左の子が終了し、右の子が開始するポイントを見つけることです。
両方の子も注文後の走査から取得されるため、それらの構築は同じ方法で再帰的に行われます。
BST fromPostOrder(value[] nodes) {
// No nodes, no tree
if (nodes == null) return null;
return recursiveFromPostOrder(nodes, 0, nodes.length - 1);
}
// Construct a BST from a segment of the nodes array
// That segment is assumed to be the post-order traversal of some subtree
private BST recursiveFromPostOrder(value[] nodes,
int leftIndex, int rightIndex) {
// Empty segment -> empty tree
if (rightIndex < leftIndex) return null;
// single node -> single element tree
if (rightIndex == leftIndex) return new BST(nodes[leftIndex]);
// It's a post-order traversal, so the root of the tree
// is in the last position
value rootval = nodes[rightIndex];
// Construct the root node, the left and right subtrees are then
// constructed in recursive calls, after finding their extent
BST root = new BST(rootval);
// It's supposed to be the post-order traversal of a BST, so
// * left child comes first
// * all values in the left child are smaller than the root value
// * all values in the right child are larger than the root value
// Hence we find the last index in the range [leftIndex .. rightIndex-1]
// that holds a value smaller than rootval
int leftLast = findLastSmaller(nodes, leftIndex, rightIndex-1, rootval);
// The left child occupies the segment [leftIndex .. leftLast]
// (may be empty) and that segment is the post-order traversal of it
root.left = recursiveFromPostOrder(nodes, leftIndex, leftLast);
// The right child occupies the segment [leftLast+1 .. rightIndex-1]
// (may be empty) and that segment is the post-order traversal of it
root.right = recursiveFromPostOrder(nodes, leftLast + 1, rightIndex-1);
// Both children constructed and linked to the root, done.
return root;
}
// find the last index of a value smaller than cut in a segment of the array
// using binary search
// supposes that the segment contains the concatenation of the post-order
// traversals of the left and right subtrees of a node with value cut,
// in particular, that the first (possibly empty) part of the segment contains
// only values < cut, and the second (possibly empty) part only values > cut
private int findLastSmaller(value[] nodes, int first, int last, value cut) {
// If the segment is empty, or the first value is larger than cut,
// by the assumptions, there is no value smaller than cut in the segment,
// return the position one before the start of the segment
if (last < first || nodes[first] > cut) return first - 1;
int low = first, high = last, mid;
// binary search for the last index of a value < cut
// invariants: nodes[low] < cut
// (since cut is the root value and a BST has no dupes)
// and nodes[high] > cut, or (nodes[high] < cut < nodes[high+1]), or
// nodes[high] < cut and high == last, the latter two cases mean that
// high is the last index in the segment holding a value < cut
while (low < high && nodes[high] > cut) {
// check the middle of the segment
// In the case high == low+1 and nodes[low] < cut < nodes[high]
// we'd make no progress if we chose mid = (low+high)/2, since that
// would then be mid = low, so we round the index up instead of down
mid = low + (high-low+1)/2;
// The choice of mid guarantees low < mid <= high, so whichever
// case applies, we will either set low to a strictly greater index
// or high to a strictly smaller one, hence we won't become stuck.
if (nodes[mid] > cut) {
// The last index of a value < cut is in the first half
// of the range under consideration, so reduce the upper
// limit of that. Since we excluded mid as a possible
// last index, the upper limit becomes mid-1
high = mid-1;
} else {
// nodes[mid] < cut, so the last index with a value < cut is
// in the range [mid .. high]
low = mid;
}
}
// now either low == high or nodes[high] < cut and high is the result
// in either case by the loop invariants
return high;
}
順序のないトラバーサルは本当に必要ありません。ポストオーダートラバーサルのみを指定してツリーを再構築する簡単な方法があります。
これは、スタックを使用して再帰的または反復的に簡単に実行でき、2つのインデックスを使用して、実際に配列を分割するのではなく、現在のサブ配列の開始と終了を示すことができます。
注文後のトラバーサルは次のようになります。
visit left
visit right
print current.
そしてこのように順番に:
visit left
print current
visit right
例を見てみましょう:
7
/ \
3 10
/ \ / \
2 5 9 12
/
11
順序は次のとおりです:2 3 5 7 9 10 11 12
後注文は:2 5 3 9 11 12 10 7
ポストオーダー配列を逆の順序で繰り返し、その値がどこにあるかを中心にインオーダー配列を分割し続けます。これを再帰的に実行すると、それがあなたのツリーになります。例えば:
current = 7, split inorder at 7: 2 3 5 | 9 10 11 12
見覚えがあります? BST構造に関する限り、左側にあるのは左側のサブツリーであり、右側にあるのは右側のサブツリーです。疑似ランダムな順序です。しかし、あなたは今あなたのルートが何であるかを知っています。次に、2つの半分について同じことを行います。ポストオーダートラバーサルの左半分から要素の最初の出現(最後から)を見つけます。それは3になります。3の周りに分割します。
current = 3, split inorder at 3: 2 | 5 ...
これまでのところ、ツリーは次のようになっています。
7
/
3
これは、ポストオーダートラバーサルの値は常に子が表示された後に表示され、インオーダートラバーサルの値は子の値の間に表示されるという事実に基づいています。
何もループしないでください。最後の要素はあなたのルートです。次に、配列を逆方向に取得し、BSTの挿入ルールに従います。
eg:-
given just the postorder -- 2 5 3 9 11 12 10 7
7
\
10
----
7
\
10
\
12
-----
7
\
10
\
12
/
11
-------
7
\
10
/ \
9 12
/
11
--------
7
/ \
3 10
/ \ / \
2 5 9 12
/
11