- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一个表示层次关系的平面数据,如下所示:
ID Name PID
0 A NULL
1 B 0
2 C 0
4 D 1
5 E 1
6 F 4
3 G 0
此表代表'数据表',其中PID表示父元素。例如,在第一行中我们看到 A 的 PID 为 null 而 B 的 PID 为 0,这意味着 B 的父元素是 A,因为 0 是 A 的 ID,而 A 是根元素,因为它没有 PID .同样,C 有父 A,因为 C 也有 PID 0,而 0 是 A 的 ID。
我创建了一个 DataTable 类来表示上表。我还实现了方法 processDataTable
public Map<String, List<String>> processDataTable()
返回的映射使用元素作为键,并将后代节点的集合作为值。例如,映射中的第一项对应于元素 A,它有很多后代,而元素 C 没有后代。输出中成员的顺序并不重要。
public static void main(String...arg) {
DataTable dt = newDataTable();
dt.addRow(0, "A", null);
dt.addRow(1, "B", 0);
dt.addRow(2, "C", 0);
dt.addRow(4, "D", 1);
dt.addRow(5, "E", 1);
dt.addRow(6, "F", 4);
dt.addRow(3, "G", 0);
System.out.println("Output:");
System.out.println(dt.processDataTable());
}
Output:
{D=[F], A=[B, C, G, D, E, F], B=[D, E, F]}
or
{D=[F], E=null, F=null, G=null, A=[B, C, G, D, E, F], B=[D, E, F], C=null}
下面是我对 DataTable 的实现:
public class DataTable {
private List<Record> records = new ArrayList<>();
private Map<Integer, Integer> indexes = new HashMap<>();
private static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* Add new record into DataTable.
*
* @param id
* @param name
* @param parentId
*/
public void addRow(Integer id, String name, Integer parentId) {
if (indexes.get(id) == null) {
Record rec = new Record(id, name, parentId);
records.add(rec);
indexes.put(id, records.size() - 1);
}
}
public List<Record> getRecords() {
return records;
}
/**
* Process DataTable and return a Map of all keys and its children. The
* main algorithm here is to divide big record set into multiple parts, compute
* on multi threads and then merge all result together.
*
* @return
*/
public Map<String, List<String>> processDataTable() {
long start = System.currentTimeMillis();
int size = size();
// Step 1: Link all nodes together
invokeOnewayTask(new LinkRecordTask(this, 0, size));
Map<String, List<String>> map = new ConcurrentHashMap<>();
// Step 2: Get result
invokeOnewayTask(new BuildChildrenMapTask(this, 0, size, map));
long elapsedTime = System.currentTimeMillis() - start;
System.out.println("Total elapsed time: " + elapsedTime + " ms");
return map;
}
/**
* Invoke given task one way and measure the time to execute.
*
* @param task
*/
private void invokeOnewayTask(ForkJoinTask<?> task) {
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool(PROCESSORS);
pool.invoke(task);
long elapsedTime = System.currentTimeMillis() - start;
System.out.println(task.getClass().getSimpleName() + ":" + elapsedTime + " ms");
}
/**
* Find record by id.
*
* @param id
* @return
*/
public Record getRecordById(Integer id) {
Integer pos = indexes.get(id);
if (pos != null) {
return records.get(pos);
}
return null;
}
/**
* Find record by row number.
*
* @param rownum
* @return
*/
public Record getRecordByRowNumber(Integer rownum) {
return (rownum < 0 || rownum > records.size() - 1) ? null:records.get(rownum);
}
public int size() {
return records.size();
}
/**
* A task link between nodes
*/
private static class LinkRecordTask extends RecursiveAction {
private static final long serialVersionUID = 1L;
private DataTable dt;
private int start;
private int end;
private int limit = 100;
public LinkRecordTask(DataTable dt, int start, int end) {
this.dt = dt;
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if ((end - start) < limit) {
for (int i = start; i < end; i++) {
Record r = dt.records.get(i);
Record parent = dt.getRecordById(r.parentId);
r.parent = parent;
if(parent != null) {
parent.children.add(r);
}
}
} else {
int mid = (start + end) / 2;
LinkRecordTask left = new LinkRecordTask(dt, start, mid);
LinkRecordTask right = new LinkRecordTask(dt, mid, end);
left.fork();
right.fork();
left.join();
right.join();
}
}
}
/**
* Build Map<String, List<String>> result from given DataTable.
*/
private static class BuildChildrenMapTask extends RecursiveAction {
private static final long serialVersionUID = 1L;
private DataTable dt;
private int start;
private int end;
private int limit = 100;
private Map<String, List<String>> map;
public BuildChildrenMapTask(DataTable dt, int start, int end, Map<String, List<String>> map) {
this.dt = dt;
this.start = start;
this.end = end;
this.map = map;
}
@Override
protected void compute() {
if ((end - start) < limit) {
computeDirectly();
} else {
int mid = (start + end) / 2;
BuildChildrenMapTask left = new BuildChildrenMapTask(dt, start, mid, map);
BuildChildrenMapTask right = new BuildChildrenMapTask(dt, mid, end, map);
left.fork();
right.fork();
left.join();
right.join();
}
}
private void computeDirectly() {
for (int i = start; i < end; i++) {
Record rec = dt.records.get(i);
List<String> names = new ArrayList<String>();
loadDeeplyChildNodes(rec, names);
if(!names.isEmpty()) {
map.put(rec.name, names);
}
}
}
private void loadDeeplyChildNodes(Record r, List<String> names) {
Collection<Record> children = r.children;
for(Record rec:children) {
if(!names.contains(rec.name)) {
names.add(rec.name);
}
loadDeeplyChildNodes(rec, names);
}
}
}
}
我的记录类:
/**
* Represents a structure of a record in DataTable.
*/
public class Record {
public Integer id;
public String name;
public Integer parentId;
public Record parent;
public Collection<Record> children;
public Record(Integer id, String name, Integer parentId) {
this();
this.id = id;
this.name = name;
this.parentId = parentId;
}
public Record() {
children = Collections.newSetFromMap(new ConcurrentHashMap<Record, Boolean>())
}
public Collection<Record> getChildren() {
return children;
}
public Record getParent() {
return parent;
}
public Integer getParentId() {
return parentId;
}
@Override
public String toString() {
return "Record{" + "id=" + id + ", name=" + name + ", parentId=" + parentId + '}';
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((parentId == null) ? 0 : parentId.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Record)) {
return false;
}
Record other = (Record) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (parentId == null) {
if (other.parentId != null) {
return false;
}
} else if (!parentId.equals(other.parentId)) {
return false;
}
return true;
}
}
我的算法是:
- Link all parent and child of each record
- Build the map
On each step I apply fork join to divide the dataset into smaller parts and run in parellel.
我不知道这个实现有什么问题。有人可以给我一些建议吗?此实现在 Linear hierarchy 5K 记录的情况下出现 OutOfmemory 错误(项目 1 是项目 2 的根和父项目,项目 2 是项目 3 的父项目,项目 3 是项目 4 的父项目,...等等)。它得到了 OutOfmemory,因为它多次调用递归方法。
解决这个问题的好算法是什么,或者我应该修改哪个数据结构以使其更好?
最佳答案
您似乎已经屈服于编写比执行所需代码更多的代码的诱惑。根据您的数据,我们可以编写一个简单的树结构,让您可以进行祖先和后代搜索:
import java.util.HashMap;
import java.util.ArrayList;
class Node {
// static lookup table, because we *could* try to find nodes by walking
// the node tree, but the ids are uniquely identifying: this way we can
// do an instant lookup. Efficiency!
static HashMap<Long, Node> NodeLUT = new HashMap<Long, Node>();
// we could use Node.NodeLUT.get(...), but having a Node.getNode(...) is nicer
public static Node getNode(long id) {
return Node.NodeLUT.get(id);
}
// we don't call the Node constructor directly, we just let this factory
// take care of that for us instead.
public static Node create(long _id, String _label) {
return new Node(_id, _label);
}
public static Node create(long _id, String _label, long _parent) {
Node parent = Node.NodeLUT.get(_parent), node;
node = new Node(_id, _label);
parent.addChild(node);
return node;
}
// instance variables and methods
Node parent;
long id;
String label;
ArrayList<Node> children = new ArrayList<Node>();
// again: no public constructor. We can only use Node.create if we want
// to make Node objects.
private Node(long _id, String _label) {
parent = null;
id = _id;
label = _label;
Node.NodeLUT.put(id, this);
}
// this is taken care of in Node.create, too
private void addChild(Node child) {
children.add(child);
child.setParent(this);
}
// as is this.
private void setParent(Node _parent) {
parent = _parent;
}
/**
* Find the route from this node, to some descendant node with id [descendentId]
*/
public ArrayList<Node> getDescendentPathTo(long descendentId) {
ArrayList<Node> list = new ArrayList<Node>(), temp;
list.add(this);
if(id == descendentId) {
return list;
}
for(Node n: children) {
temp = n.getDescendentPathTo(descendentId);
if(temp != null) {
list.addAll(temp);
return list;
}
}
return null;
}
/**
* Find the route from this node, to some ancestral node with id [descendentId]
*/
public ArrayList<Node> getAncestorPathTo(long ancestorId) {
ArrayList<Node> list = new ArrayList<Node>(), temp;
list.add(this);
if(id == ancestorId) {
return list;
}
temp = parent.getAncestorPathTo(ancestorId);
if(temp != null) {
list.addAll(temp);
return list;
}
return null;
}
public String toString() {
return "{id:"+id+",label:"+label+"}";
}
}
所以让我们通过添加标准的 public static void main(String[] args)
方法来测试它以确保它有效,为了方便起见,一个函数将 Node 的 ArrayLists 转换成某种东西可读性:
public static String stringify(ArrayList<?> list) {
String listString = "";
for (int s=0, l=list.size(); s<l; s++) {
listString += list.get(s).toString();
if(s<l-1) { listString += ", "; }
}
return listString;
}
public static void main(String[] args) {
// hard coded data based on your question-supplied example data
Node.create(0, "A");
Node.create(1, "B", 0);
Node.create(2, "C", 0);
Node.create(4, "D", 1);
Node.create(5, "E", 1);
Node.create(6, "F", 4);
Node.create(3, "G", 0);
// let's see what we get!
Node root = Node.getNode(0);
Node f = Node.getNode(6);
System.out.println("From root to F: " + stringify(root.getDescendentPathTo(6)));
System.out.println("From F to root: " + stringify(f.getAncestorPathTo(0)));
}
输出?
From root to F: {id:0,label:A}, {id:1,label:B}, {id:4,label:D}, {id:6,label:F}
From F to root: {id:6,label:F}, {id:4,label:D}, {id:1,label:B}, {id:0,label:A}
完美。
所以我们需要做的就是编写将您的“平面定义”转换为 Node.create
调用的部分,然后完成。记住:不要把事情复杂化。如果你的数据是一棵平面树,你所需要的只是一个树结构。编写树结构所需要做的就是单个 Node 类。
关于java - 从平面数据中找到所有深度树结构的后代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26080970/
我有一个表(id, parent_id, data),其中parent_id 指向同一表中的另一行(或者为空)。 是否有一种标准的方法来查询(1)某个 id 的所有祖先和(2)某个 id 的所有后代?
networkx 中是否有函数/方法来识别给定(可选加权)距离内的所有祖先/后代? 例如,可以有效地产生与下面的函数相同的结果的东西? import networkx g = networkx.DiG
在我的窗口事件中,如果指针点击父对象或其子对象/后代对象,它应该做一些事情。问题是事件无法访问 parent 的 child 和孙子。条件存储在 targetIsInsideParent 变量中。 H
我有一个非常好的 DirectMySQL 单元,可以使用,我希望它成为 TDataset 的后代,这样我就可以将它与 QuickReport 一起使用,我只想要使用来自 TDataset 的 Dire
我将 mysql 表定义为: 类别:category_id、category_name、parent_category_id 我正在寻找一个很好的 sql 查询来检索给定 category_id 的所
我的 TCustomControl 后代使用线程,这涉及使用 InvalidateRect 进行无效化。我遇到这样的情况:当线程正在工作时关闭程序时,我不会停止 Destroy 中的线程,因为即使在进
我的 TMemo 后代有构造函数 constructor TMyMemo.Create(AOwner: TComponent); begin inherited Create(AOwner);
我正在尝试创建一个像Delphi 2009的TButtonedEdit这样的组件。它是一个自定义的TEdit,左右各有2个按钮。 在我的版本中,我使用 2 个 TSpeedButton 对象作为左右按
我遇到了一些问题,看起来使用 JQuery 应该很容易做到。基本上我的页面上有一个表格。表格中的每一行都有一个复选框和一个金额单元格。我正在尝试编写一个函数来遍历每一行并检查复选框是否已选中。如果是这
如何使用 linq 对包含相同对象的子集合 X 层深的对象集合获得与 doc.Descendants() 类似的功能? 最后一个嵌套集合包含需要获取的数据,所有其他父集合只是分组。我可以将集合转换为
我有一个引用自身的表,如下所示: CREATE TABLE Foo ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, parent INT NULL, nam
我正在尝试添加以下约束来对齐表格单元格内的图像。 self 指的是 ImageView 将驻留在其中的 UITableViewCell 对象: var imageViewTest = UIIm
我有以下标记: content Link Link Link
这里是一个简化的mxl结构'xml', Alice30 Bob31 Charley29 Dory25 这是我尝试过的; XmlDocument submiss
我正在尝试编写一个类,该类将在其对象创建时运行一个线程,并在对象被删除后停止该线程。 class MyThread : public boost::thread { public: MyThr
我有一个表 width: 100%以及该表中的元素 width: 40em; max-width: 100% , 但当浏览器窗口太小时,该元素仍在拉伸(stretch)表格。 我希望这个元素的宽度固定
我正在尝试在 Delphi 2007 中创建基于 TCustomComboBox 的自定义控件,但我陷入了第一个障碍。 我试图覆盖下拉列表的显示方式,主要是显示的文本,查看 stdctrls.pas
我正在尝试创建一个具有集合属性的自定义组件。但是,如果我尝试在设计时通过单击对象检查器中的“...”按钮来打开集合编辑器,则不会发生任何情况。我缺少什么? 这是我的 TCollection 后代:
我正在使用WPF DataGrid,并放置了DataGridTemplateColumn。因为该列应该对复杂类型(这是一个对象)执行编辑,所以我决定放置一个打开弹出窗口的切换按钮。代码如下:
我有一个基本的 UserControl (BaseControl.cs),上面有一个 OK 和 Cancel 按钮。单击按钮时,将触发一个事件。通常其他 UserControl 对象继承自 BaseC
我是一名优秀的程序员,十分优秀!