- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
//Definition for a binary tree node.
public class TreeNode {
int key;
TreeNode left;
TreeNode right;
TreeNode(int x) { key = x; }
}
给定TreeNode的总数int
n
,如何生成一棵随机分布的二叉树(我指的是二叉树的random形状,不是随机键值。您可以将 TreeNodes 的所有键值设置为 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
是否有任何算法可以等概率地生成具有固定节点数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 种可能的二叉树中每一种的出现概率(有关相关组合,请参阅 Catalan numbers)。
已经有一些关于这个主题的研究和this可能是最简单和最快的方法之一(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 步: 从格式正确的括号单词到二叉树存在一对一的映射:每一对匹配的括号都是一个节点,里面的所有内容都是左子树,所有内容之后是右子树:
// 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();
}
测试:让我们打印超过 1000 万次大小为 3 的实际分布:
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
的二叉树,遵循对所有可能树的均匀分布。
关于java - 如何根据节点编号随机生成二叉树?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56873764/
我使用 Faker gem 来播种某些数据。我怎样才能设置最大。假公司名称的长度,如何设置假号码的范围? name = Faker::Company.name 这里我想包括最大长度,因为名称对最大长
我试图实现一种方法,在该方法中我创建一个记分板(while 循环)并按某个数字字段(点)对获取的结果进行排序。但我需要实现的是如下 rank----username--point 1st------t
如何在 ListView (vsReport) 中对项目进行编号?现在我有类似的东西: Item := ListView1.Items.Add; Item.Caption :=inttostr(Ite
我正在尝试使用几个按钮在红色和绿色之间切换,我已经浏览了 Jquery 文档,但我似乎无法弄清楚这个。 您可以在我的代码片段中看到,当我单击按钮时,所有按钮都会变成不同的颜色。 而且必须有一种更短的方
是否可以在 GROUP_CONCAT 中进行编号 喜欢 如果,来自 GROUP_CONCAT(empnam SEPARATOR ', ') 我有一套, 我需要 我试过跟随,但没有得到想要的结
在 Linux 中,如何使用用户指定的事件编号创建输入事件接口(interface)并将其映射到特定的设备事件? 我正在使用 gpio-keys 驱动程序来翻译键盘上的按键操作。我定义了要在我的板配置
对于低级应用程序,我在编译时将某种索引定义为一种类型: template class Idx{ using TYPE = T_; static const int IDX = IDX_; }
请问如何设置这种格式的编号: 1) number 1 2) number 2 3) ... 在 HTML 中? 我只找到了一种获取这种格式的方法: 1. number 1 2. number 2 3.
对于我的研究,我需要很少的数据,其中之一是前一次提交的 SHA 编号,即如果我提供特定的提交编号/SHA 编号,我应该能够获得它之前的 SHA 编号。 帮我用 git 命令来获得相同的结果。 最佳答案
我需要询问何时按下数字 1 键,而不是在数字键盘上,而是在 Q 上方的数字 1(试图使这尽可能清楚)。 我已经浏览了 Keys 数组上所有可用的键,但没有一个匹配我正在寻找的键。 有没有办法做到这一点
我正在尝试使用 NAnt 任务设置构建服务器。我有几个想要构建的 Git 存储库,但我在结果的版本控制方面遇到了问题。 如何对库 (dll) 进行版本控制,以便每个构建都使用一个数字来表示每个版本?我
我有一个如下所示的表格: +-------+--------+--------+ | Grp | Party | Member | +-------+--------+--------+ | F
我正在使用 SQL Server 2008。我在查询中返回了这些数据,看起来非常像按 Day 和 ManualOrder 排序的... ID Day ManualOrder Lat L
区域:Silverlight 中的文本框 问题:我需要知道当前编辑的是什么“行号”。 我试过:作为一种解决方法,我尝试使用 textBox.Split("\r") 进行拆分,并计算 Regex 上具有
提前致歉,我不确定如何将 null 值添加到 pandas 数据框,所以我在列表中放置了“无”。我有一个具有以下值的数据框: None, None, 50,60,70,80,90,None,None,
我的应用程序中有一个标签,其中会带有徽章编号,该标签基于3个不同时间发生的3种不同操作。我知道如何使用以下方式设置标签栏标志: [[[[[self tabBarController] tabBar]
有谁知道为什么我们的供应商需要我们的 sku 编号来开发新应用程序? 我们自己将应用程序带入应用程序商店,也许我错了,但构建应用程序或将其上传到商店不需要 sku - 它仅在 iTunes 中连接而不
这个问题已经有答案了: Rationale for Matcher throwing IllegalStateException when no 'matching' method is called
我使用[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]清除徽章编号。它工作正常,但同时删除了远程通知。 还有许多其
我有一个待办事项列表应用程序,我想在图标徽章中显示未完成任务的数量。 我的问题是:我是否应该在用户每次在应用程序中删除/添加新任务时更新徽章编号,还是仅在应用程序即将进入后台模式时更新徽章编号? 最佳
我是一名优秀的程序员,十分优秀!