简体   繁体   English

修剪多路树-有什么更好的解决方案?

[英]Trim a Multiway Tree - what is a better solution?

I was wondering whether anyone could offer a more simplified solution or improvements to my code for the following problem. 我想知道是否有人可以为以下问题提供更简化的解决方案或对我的代码进行改进。

Say we have a tree with branches going to some depth "d" And we would like to trim this tree, such that we preserve "n" branches at depth d and then another n branches at depth d-1 ; 假设我们有一棵树,其分支的深度达到“ d”,并且我们想要修剪这棵树,这样我们就可以在深度d保留“ n”个分支,然后在深度d-1保留另一个n分支; then another n branches at d-2 etc... 然后在d-2处又有n个分支,等等。

In my solution I needed to resort to a dictionary to keep track of the number of branches and ref variable depth to keep track of and reducing the depth level 在我的解决方案中,我需要借助dictionary来跟踪分支的数量,并ref可变深度来跟踪并降低深度级别

Would be very interested to know a simpler, more elegant solution, or any hints / tips to improve what I have 知道一个更简单,更优雅的解决方案,或改善我所拥有的任何提示/技巧将非常有兴趣

Data Structure 数据结构

type MultiTree = | MNode of int * list<MultiTree>

Test Tree 测试树

let Mtree2 = MNode (0,
                    [MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])])])

Trim Tree Function 修剪树功能

let trimTree noLosses depth t=
    let mlimit = ref depth
    let dict = Dictionary()

    let fn k =
        match dict.TryGetValue(k) with
        | true, l -> dict.[k] <- l + 1
        |_ -> dict.Add(k,1)
        if dict.[k] >= noLosses then mlimit := !mlimit - 1 else mlimit := !mlimit

    let rec loop d t  = 
        match t with
            | MNode(i,sub) when d > !mlimit ->  
                fn !mlimit      
                MNode(i, List.map (loop (d+1)) [])
            | MNode(i,sub) -> MNode(i, List.map (loop (d+1)) sub)
    loop 1  t


let Mtree2trimmed = Mtree2 |> trimTree 3 2

Result 结果

Using Thomas P's "printTree" function this is nicely visualized 使用Thomas P的“ printTree”函数,可以很好地实现可视化

let printt tree =
let rec loop depth (MNode(n, sub)) =
    printfn "%s %A" depth n
    for s in sub do loop (depth + "  ") s
loop "" tree

Output - 输出-

printt Mtree2

 0
   1
     2
       3
   1
     2
       3
   1
     2
       3
   1
     2
       3
   1
     2
       3
   1
     2
       3

Output - 输出-

printt Mtree2trimmed

 0
   1
     2
   1
     2
   1
     2
   1
   1
   1

So if you've made it this far - any advice? 因此,如果您已经做到了这一点-有什么建议吗?

Cheers! 干杯!

This problem is called . 这个问题称为 The basic approach is the following: 基本方法如下:

  1. Mark your data with a supplementary data structure; 用补充数据结构标记数据;
  2. Process based on supplementary data; 根据补充数据进行处理;
  3. Reduce to remove those items that should not be in the final result; 减少以删除那些不应包含在最终结果中的项目; Also, eliminate supplementary data as they are not needed anymore. 此外,请消除不再需要的补充数据。

Let's mark each branch with to supplementary values: 让我们用补充值标记每个分支:

  • depth ( 0 =topmost branch) depth0 =最上面的分支)
  • sequential (for each subtree, starting with 0 ) sequential (对于每个子树,从0开始)

In order to accomplish that, I have changed the definition of your tree so that it was generic: 为了实现这一点,我更改了树的定义,使其具有通用性:

type MultiTree<'T> = | MNode of 'T * list<MultiTree<'T>>

And now: 现在:

let rec mapper depth sequential = function
    | MNode(value, sub) ->
        MNode( (depth, sequential, value),
               (sub |> List.mapi (fun i value -> mapper (depth+1) i value))
             )

let tree1 = MNode (0,
                    [MNode (1,[MNode (2,[])]);
                    MNode (3,[]);])

printfn "Original data"
tree1 |> printt
printfn "Mapped data"
tree1 |> mapper 0 0 |> printt

The result would be: 结果将是:

Original data
 0
   1
     2
   3
Mapped data
 (0, 0, 0)
   (1, 0, 1)
     (2, 0, 2)
   (1, 1, 3)

Now, when the data is marked, we may apply whatever filter we want. 现在,在标记数据后,我们可以应用所需的任何过滤器。 Here are three examples, plus a bit larger tree to demonstrate all possibilities: 这是三个示例,外加一棵更大的树来演示所有可能性:

// Take only first n branches at every level
let rec filter1 n = function
    // first subtrees pass (up to "noLosses" count)
    | MNode( (depth, sequential, value), sub)
        when sequential < n
                            -> Some(MNode(value, List.choose (filter1 n) sub))
    // the rest are skipped
    | _                     -> None

// Take "d" levels of branches unchanged, at higher levels take only second branch
let rec filter2 d = function
    | MNode( (depth, sequential, value), sub)
        when depth < d      // lower depth - pass

        || sequential = 1   // at higher levels, take only the second branch (=1)
                            -> Some(MNode(value, List.choose (filter2 d) sub))
    // the rest are skipped
    | _                     -> None

// Take only first n branches at every level;
// "n" is a list to identify maximal element at each level
let rec filter3 ns ts =
    match ns, ts with
    // Empty "noLosse" list -> no value
    | [], _                      -> None
    // if the sequential order of Tree branch is less than
    // the corresponding value in "ns" list, let the value pass the filter
    | nsHead :: nsTail, MNode((_, sequential, value), sub)
        when sequential < nsHead -> Some(MNode(value, List.choose (filter3 nsTail) sub))
    // the rest are skipped
    | _, _                       -> None

printfn "Original data"
tree2 |> printt
printfn "Filter1 applied"
tree2 |> mapper 0 0 |> filter1 2 |> Option.iter printt
printfn "Filter2 applied"
tree2 |> mapper 0 0 |> filter2 2 |> Option.iter printt
printfn "Filter3 applied"
tree2 |> mapper 0 0 |> filter3 [4; 4; 2] |> Option.iter printt

Note, we need Option.iter because the filter may return None value. 注意,我们需要Option.iter因为过滤器可能返回None值。

Outputs: 输出:

Original data
 1
   11
     111
       1111
       1112
       1113
   12
     121
       1211
     122
       1221
       1222
       1223
     123
     124
   13
     131
       1211
   14
     141
       1411
   15
     151
       1511
   16
     161
       1611
Filter1 applied
 1
   11
     111
       1111
       1112
   12
     121
       1211
     122
       1221
       1222
Filter2 applied
 1
   11
   12
     122
       1222
   13
   14
   15
   16
Filter3 applied
 1
   11
     111
   12
     121
     122
   13
     131
   14
     141

An important note, this approach is not optimized for performance. 需要注意的是,这种方法并未针对性能进行优化。 Its primary idea is to show how to split the task into a sequence of smaller tasks so that the actual filtering is literally a simple match case. 其主要思想是展示如何将任务分解为一系列较小的任务,以便实际过滤实际上是一个简单的匹配情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM