//Definition for a binary tree node.
public class TreeNode {
int key;
TreeNode left;
TreeNode right;
TreeNode(int x) { key = x; }
}
ツリーノードの合計数を考えるとint
n
、ランダムに分布し、バイナリツリーを生成する方法(私が意味する、ランダムなバイナリツリーの形状、ではない。ランダムなキー値をあなたはのTreeNodeが1に等しいすべてのキーの値を設定することができます)とのTreeNodeを返しますroot
。
それは、次のAPIを実装する方法です。
public class RandomBinaryTree{
public TreeNode binaryTreeGenerator(int n){
}
}
PS:たとえば、n = 3
私はアルゴリズムができますしたいランダムに次のいずれかの生成5
バイナリツリーを毎回:
1 1 1 1 1
/ / / \ \ \
1 1 1 1 1 1
/ \ / \
1 1 1 1
バイナリツリーを生成するための任意のアルゴリズムがありequiprobablyノードの固定数ではn
?
偏り、単純なアルゴリズム
ルートから開始し、ランダムに再帰その後、各サブツリー内のノードの数を選択します。
public class RandomBinaryTree {
private Random random = new Random();
public TreeNode binaryTreeGenerator(int n, int key){
if (n == 0)
return null;
TreeNode root = new TreeNode(key);
// Number of nodes in the left subtree (in [0, n-1])
int leftN = random.nextInt(n);
// Recursively build each subtree
root.setLeft(binaryTreeGenerator(leftN, key));
root.setRight(binaryTreeGenerator(n - leftN - 1, key));
return root;
}
}
このアルゴリズムは、結果の均一な分布を強制しないと重くバランスの取れた木を好むだろう。単純な証明のために、ケース検討n = 3
(5つの参照可能なバイナリツリーの各々についての出現の計算確率をカタロニア語番号を関連組合せ論用)。
均一に分布し、より多くの挑戦
そここのテーマに関するいくつかの研究されているとしているこれはおそらく、(で最も簡単かつ最速の方法の一つであるO(n)と)。アイデアは、次に変換そのジャム均一な分布を使用してバイナリツリーにそれをマッピングし、同数の左右のブラケットを含むランダム・ワードを生成することです。
ステップ1:ランダム生成バランスの取れた単語を:
private static Random random = new Random();
// true means '(', false means ')'
private static boolean[] buildRandomBalancedWord(int n) {
boolean[] word = new boolean[n * 2];
List<Integer> positions = IntStream.range(0, 2 * n).boxed()
.collect(Collectors.toList());
for (int i = n; i > 0; i--) {
int index = random.nextInt(n + i);
word[positions.remove(index)] = true;
}
return word;
}
ステップ2:生成された単語を有することができるk
基本的に比類のない閉鎖ブラケットが「欠陥」を、。得られたマッピングを有する単語のセットから全単射であるように生成されたワード再配置する方法があること紙ショーk
を持つ単語のセットに欠陥0
の欠陥(整形単語が)。ここでの手順は以下のとおりです。
private static void rearrange(boolean[] word, int start, int end) {
int sum = 0;
int defectIndex = -1;
for (int i = start; i < end; i++) {
sum = sum + (word[i] ? 1 : -1);
if (defectIndex < 0 && sum < 0) {
defectIndex = i;
} else if (defectIndex >= 0 && sum == 0) {
// We now have irreducible u = rtl spanning [defectIndex, i]
int uLength = i - defectIndex + 1;
boolean[] flipped = new boolean[uLength - 2];
for (int j = 0; j < flipped.length; j++)
flipped[j] = !word[defectIndex + j + 1];
// Shift the remaining word
if (i + 1 < end)
System.arraycopy(word, i + 1, word, defectIndex + 1, end - (i + 1));
// Rewrite uw as lwrt*, t* being the flipped array
word[defectIndex] = true;
System.arraycopy(flipped, 0, word, end - flipped.length, flipped.length);
word[end - uLength + 1] = false;
// Now recurse on w, worst case we go (word.length/2)-deep
rearrange(word, defectIndex + 1, end - uLength + 1);
break;
}
}
}
ステップ3:バイナリツリーに整形ブラケットワードから1対1のマッピングがある:マッチングブラケットのすべてのペアがノードである、すべての内部は、左の部分木であり、後のすべてが右の部分木です。
// There is probably a smarter way to do this
public static TreeNode buildTree(boolean[] word, int key) {
Deque<TreeNode> stack = new ArrayDeque<>();
boolean insertRight = false;
TreeNode root = null;
TreeNode currentNode = null;
for (int i = 0; i < word.length; i++) {
if (word[i]) {
TreeNode previousNode = currentNode;
currentNode = new TreeNode(key);
if (root == null) {
root = currentNode;
} else if (insertRight) {
previousNode.setRight(currentNode);
insertRight = false;
} else {
previousNode.setLeft(currentNode);
}
stack.push(currentNode);
} else {
currentNode = stack.pop();
insertRight = true;
}
}
return root;
}
いくつかのユーティリティ関数:
public static boolean[] buildRandomWellFormedWord(int n) {
boolean[] word = buildRandomBalancedWord(n);
rearrange(word, 0, word.length);
return word;
}
public static String toString(boolean[] word) {
StringBuilder str = new StringBuilder();
for (boolean b : word)
str.append(b ? "(" : ")");
return str.toString();
}
テスト:レッツ・プリントサイズ3の10百万回のランを超える実際の分布:
public static void main(String[] args) throws Exception {
Map<String, Integer> counts = new HashMap<String, Integer>();
int N = 10000000, n = 3;
for (int i = 0; i < N; i++) {
boolean[] word = buildRandomWellFormedWord(n);
String str = toString(word);
Integer count = counts.get(str);
if (count == null)
count = 0;
counts.put(str, count + 1);
}
counts.entrySet().stream().forEach(e ->
System.out.println("P[" + e.getKey() + "] = " + e.getValue().doubleValue() / N));
}
出力は次のようになります。
P[()()()] = 0.200166
P[(()())] = 0.200451
P[()(())] = 0.199894
P[((()))] = 0.199006
P[(())()] = 0.200483
だから、buildTree(buildRandomWellFormedWord(n), key)
サイズのバイナリツリーが得られるn
すべての可能な木の上に均一に分布し、次の。