JavaWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JavaWorld@TW » Java 新手區 » 演算法  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 树的逐级求和?
jiangshachina

Hi, Java!



發文: 527
積分: 1
於 2009-03-21 21:19 user profilesend a private message to usersend email to jiangshachinareply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
这肯定是一个老掉牙的问题了,但我至少在JavaWorld@TW没找到答案(可能没正确地搜索)。
假设一颗树,原来只有叶节点包含数值,如下面的节点N111,N112,N121和N21。
1
2
3
4
5
6
7
8
9
ROOT
 |--N1
   |--N11
     |--N111(30)
     |--N112(10)
   |--N12
     |--N121(10)
 |--N2
   |--N21(30)

现在要逐级地把下级节点中的数值累加到它们父节点中,直至根节点,即最终的结果如下所示:
1
2
3
4
5
6
7
8
9
ROOT(80)
 |--N1(50)
   |--N11(40)
     |--N111(30)
     |--N112(10)
   |--N12(10)
     |--N121(10)
 |--N2(30)
   |--N21(30)

目前我使用递归算法解决上述问题,但希望能够使用迭代算法,可不清楚如何进行转换Sad


vote up 0 vote down
jiangshachina edited on 2009-03-21 22:18
reply to postreply to post
a cup of Java, cheers!
同是Java爱好者,相逢何必曾相识!
作者 Re:树的逐级求和? [Re:jiangshachina]
jiangshachina

Hi, Java!



發文: 527
積分: 1
於 2009-03-22 08:53 user profilesend a private message to usersend email to jiangshachinareply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
此处树的构造是基于一种常用的结构:
1
2
3
4
5
6
7
8
TreeNode {
 
  private TreeNode parent = null;
 
  private List<TreeNode> children = new ArrayList<TreeNode>();
 
  ...
}
这样使用递归算法会十分的方便。

对于我的问题,我有另一种思路:
因为在初始时,只有leaf上才有数值,那么root或branch的数值,就是root或branch下属所有leaf的数值之和。
如,ROOT=N111+N112+N121+N21=80;N1=N111+N112+N121=50;N12=N121=10;...
所以,如果知道了所有的leaf,则可以从leaf出发,由下向上遍历,将该leaf的数值依次赋给(加法)它的parent,grandparent,...,直至root。这可由一个两层嵌套循环实现。
但如何得到所有的leaf呢?这恐怕要先遍历一次树Sad
所以,我的这一思路最终需要遍历树两次,显然也不太好。


vote up 0 vote down
reply to postreply to post
a cup of Java, cheers!
同是Java爱好者,相逢何必曾相识!
作者 Re:树的逐级求和? [Re:jiangshachina]
Duncan

街友 JaJa

版主

發文: 7594
積分: 39
於 2009-03-22 14:28 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
jiangshachina wrote:
这肯定是一个老掉牙的问题了,但我至少在JavaWorld@TW没找到答案(可能没正确地搜索)。
假设一颗树,原来只有叶节点包含数值,如下面的节点N111,N112,N121和N21。
1
2
3
4
5
6
7
8
9
ROOT
 |--N1
   |--N11
     |--N111(30)
     |--N112(10)
   |--N12
     |--N121(10)
 |--N2
   |--N21(30)

现在要逐级地把下级节点中的数值累加到它们父节点中,直至根节点,即最终的结果如下所示:
1
2
3
4
5
6
7
8
9
ROOT(80)
 |--N1(50)
   |--N11(40)
     |--N111(30)
     |--N112(10)
   |--N12(10)
     |--N121(10)
 |--N2(30)
   |--N21(30)

目前我使用递归算法解决上述问题,但希望能够使用迭代算法,可不清楚如何进行转换Sad


為何考慮將 recursion 作法改成 iteration 呢?

這個問題還得考量你對 iteration 作法的定義為何。
假如你一開始只獲得 tree 的 root node,那麼你要從 root node 取得所有的 tree nodes,這個過程本身可以說是 recursion 的作法,即使你之後求和是 iteration 過程,這樣子整體來說就不算是 iteration 的演算法。


vote up 0 vote down
reply to postreply to post

給我
貓咪 其餘免談

我要效法葛屁老師,我承認了,我是蘿莉控。
作者 Re:树的逐级求和? [Re:jiangshachina]
jiangshachina

Hi, Java!



發文: 527
積分: 1
於 2009-03-22 15:32 user profilesend a private message to usersend email to jiangshachinareply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
假如你一開始只獲得 tree 的 root node,那麼你要從 root node 取得所有的 tree nodes,這個過程本身可以說是 recursion 的作法
我一开始确实只有root。
但仅就遍历树而言,也是可以分递归和非递归算法吧。
即使你之後求和是 iteration 過程,這樣子整體來說就不算是 iteration 的演算法
求和本身是很简单的,关键是如何找到各个node(包含branch和leaf),然后将leaf中的值依次传给branch和root。

一般地,递归算法不是都可以转化为迭代算法吗?


vote up 0 vote down
jiangshachina edited on 2009-03-22 15:38
reply to postreply to post
a cup of Java, cheers!
同是Java爱好者,相逢何必曾相识!
作者 Re:树的逐级求和? [Re:jiangshachina]
jiangshachina

Hi, Java!



發文: 527
積分: 1
於 2009-03-24 07:08 user profilesend a private message to usersend email to jiangshachinareply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
其实我只是想显示地使用runtime stack来执行recursion罢了。

vote up 0 vote down
reply to postreply to post
a cup of Java, cheers!
同是Java爱好者,相逢何必曾相识!
作者 Re:树的逐级求和? [Re:jiangshachina]
Duncan

街友 JaJa

版主

發文: 7594
積分: 39
於 2009-03-24 20:34 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
jiangshachina wrote:
其实我只是想显示地使用runtime stack来执行recursion罢了。


如果你要的是這種以 stack 來模仿 recursion 的作法,這有標準的作法。

1. 把每次 recursive method 內的所有操作,扣除掉 recursive method call 的部分改成 iteration 去完成。
2. 分析一下,recursive method call 回返時,需要哪些數據用已繼續完成 calling method 未完成的操作。

可以使用兩個 stack 來分別用以紀錄 recursive call 時的狀態,以及待完成的操作。

以你提到的這個例子來說,遞迴的作法是在求和時,如果遇到 non-leaf node 則進行 recursive method call,當 method return subtree 的和後,把此和存入 non-leaf node 裡,並繼續與之前位於同一深度的其他 nodes 的累積值作累加。這樣子在使用 stack 模擬 recursive method call 時所要保存的只有兩個:一是累積值,二是下一深度的總和要存到哪個 node。

概略地說,把原本 recursive 方式求和作法可以改成以下幾個流程:

1. 求一個 non-leaf node 的值就是先放一個結束標記到 node stack 裡,再把此 node 的所有 child nodes 一一放入
2. 將 node stack 裡的 node 一一取出來累加
2a. 累加時如果遇到 non-leaf node,就先把已累加的值與此 non-leaf node 存入 state stack,累加值歸零,再進行步驟 1
2b. 累加時如果遇到空 node(null),則把新累加值存進 state stack 最頂端所記錄的 node,把新累加值類加進 state stack 最頂端所記錄的累加值
3. node stack 為空時,則累加完畢。

底下是簡單的 demo code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package demo.iteration;
 
import java.io.File;
import java.util.Stack;
 
public class DemoApp {
  static class State {
    private TreeNode<Long> node;
    private Long value;
    public TreeNode<Long> getNode() {
      return node;
    }
    public Long getAccumulation() {
      return value;
    }
    private State(TreeNode<Long> node, Long value) {
      this.node = node;
      this.value = value;
    }
    public static State create(TreeNode<Long> target, Long acc) {
      return new State(target, acc);
    }
  }
  
  public static void main(String[] args) {
    File d = new File(args[0]); // args[0] 必須是某檔案夾(目錄)的路徑
    TreeNode<Long> root = buildTree(d, null);
    calculateSumIterative(root);
    dumpTree(root);
  }
  
  public static long calculateSumRecursive(TreeNode<Long> root) {
    long accumulation = 0;
    for (TreeNode<Long> child : root.getChildren()) {
      if (child.isLeaf()) {
        accumulation += child.getData();
      }
      else {
        long sum = calculateSumRecursive(child);
        accumulation += sum;
      }
    }
    root.setData(accumulation);
    return accumulation;
  }
  
  public static void calculateSumIterative(TreeNode<Long> root) {
    Stack<State> states = new Stack<State>();
    Stack<TreeNode<Long>> nodes = new Stack<TreeNode<Long>>(); // 放待求和的 nodes
    nodes.push(root);
    long accumulation = 0;
    while (!nodes.empty()) {
      TreeNode<Long> node = nodes.pop();
      if (node == null) { // 此深度所有 nodes 拜訪完畢, line 39 method return
        State state = states.pop();
        state.getNode().setData(accumulation);  // 把此深度所有 nodes 總和存入 State 中記錄的 node, line 43
        accumulation += state.getAccumulation(); // 把 subtree 總和也加入累積值中, line 40
      }
      else if (node.isLeaf()) {
        accumulation += node.getData(); // line 36
      }
      else { // line 39 method call
        states.push(State.create(node, accumulation)); // 把此深度已累積值與 subtree root node 記錄下來
        accumulation = 0; // subtree 總和歸零, line 33
        nodes.push(null); // 用已分隔不同的深度的 nodes
        nodes.addAll(node.getChildren());
      }
    }
    assert states.isEmpty();
  }
  
  public static TreeNode<Long> buildTree(File directory, TreeNode<Long> parent) {
    TreeNode<Long> node = new TreeNode<Long>(directory.getAbsolutePath(), 0L, parent);
    for (File f : directory.listFiles()) {
      if (f.isDirectory()) {
        node.append(buildTree(f, node));
      }
      else {
        node.append(new TreeNode<Long>(f.getName(), f.length(), node));
      }
    }
    return node;
  }
  
  public static void dumpTree(TreeNode<?> root) {
    dumpTree(root, "");
  }
 
  private static void dumpTree(TreeNode<?> root, String indent) {
    System.out.printf("%s%s(%s)%n", indent, root.getName(), root.getData());
    for (TreeNode<?> node : root.getChildren()) {
      dumpTree(node, indent + "  ");
    }
  }
}


TreeNode.java (0.89k)


vote up 0 vote down
reply to postreply to post

給我
貓咪 其餘免談

我要效法葛屁老師,我承認了,我是蘿莉控。
作者 Re:树的逐级求和? [Re:jiangshachina]
jiangshachina

Hi, Java!



發文: 527
積分: 1
於 2009-03-24 21:23 user profilesend a private message to usersend email to jiangshachinareply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
非常感谢 Smile
一定仔细研究...

再提个问题:
既然系统在执行recursion时,会隐式地使用stack,那么是否可以认为,我们自己显示地使用stack去执行recursion并不会改善程序的性能?


vote up 0 vote down
jiangshachina edited on 2009-03-24 21:26
reply to postreply to post
a cup of Java, cheers!
同是Java爱好者,相逢何必曾相识!
作者 Re:树的逐级求和? [Re:jiangshachina]
Duncan

街友 JaJa

版主

發文: 7594
積分: 39
於 2009-03-24 23:08 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
jiangshachina wrote:
非常感谢 Smile
一定仔细研究...

再提个问题:
既然系统在执行recursion时,会隐式地使用stack,那么是否可以认为,我们自己显示地使用stack去执行recursion并不会改善程序的性能?


改成這種方式的確是不會改善效率,通常還會變差,特別是越高階的程式語言,在操作 stack 時的 overhead 會比 recursive method call + 傳遞 argument 的 overhead 還來得大。

通常會想把 recursive 演算改成這種以 stack 為主的 iteration 演算,主要會是在於避開 runtime 所提供的 stack size 在 recursive method call 深度太深時不夠用的情況。


vote up 0 vote down
reply to postreply to post

給我
貓咪 其餘免談

我要效法葛屁老師,我承認了,我是蘿莉控。

» JavaWorld@TW »  Java 新手區 » 演算法

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JavaWorld@TW


Powered by Powerful JuteForum® Version Jute 1.5.8
Copyright© 2002-2003 Rainman Zhu,Zua,Netboy,Scott. All Rights Reserved.