【问题标题】:Efficient way of flattening a recursive data structure in golang在golang中扁平化递归数据结构的有效方法
【发布时间】:2018-01-27 02:16:00
【问题描述】:

我有一个递归数据结构,可以包含几种不同类型的数据:

type Data interface{
 // Some methods
}
type Pair struct { // implements Data
  fst Data
  snd Data
}
type Number float64 // implements Data

现在我想将Pairs 链扁平化为[]Data。但是,fst 字段中的Data 不应该被展平,只有snd 中的数据应该被展平。例如:

chain := Pair{Number(1.0), Pair{Number(2.0), Pair{Number(3.0), nil}}}
chain2 := Pair{Pair{Number(1.0), Number(4.0)}, Pair{Number(2.0), Pair{Number(3.0), nil}}}

变成:

data := []Data{Number(1.0), Number(2.0), Number(3.0)}
data2 := []Data{Pair{Number(1.0), Number(4.0)}, Number(2.0), Number(3.0)}

我的幼稚做法是:

var data []Data
chain := Pair{Number(1.0), Pair{Number(2.0), Pair{Number(3.0), nil}}}

for chain != nil {
    data = append(data, chain.fst)
    chain = chain.snd
}

是否有更有效的方法可以将变量chain 中的数据结构扁平化为[]Data 数组?

【问题讨论】:

    标签: arrays go data-structures


    【解决方案1】:

    您可以使用递归函数。往下走,把对数加起来,在底部,分配数组,往上走,从后往前填满数组。

    如果需要支持任意树,可以在Data中添加一个size方法,然后再进行一次树遍历,实际填充数组。

    【讨论】:

      【解决方案2】:

      嗯,您的幼稚方法不适用于嵌套在 fst 中的 Pairs。如果你有chain := Pair{Pair{Number(1.0), Number(2.0)}, Number{3.0}},它最终会是[]Data{Pair{Number(1.0), Number(2.0)}, Number{3.0}}。这是一个固有的递归问题,那么为什么不这样实现呢?

      我建议在您的界面中添加一个flatten() 方法。 Pairs 可以递归嵌套自己,Numbers 只是返回它们的值。

      这是一个完整的工作示例,经过一些最少的测试:

      package main
      
      import "fmt"
      
      type Data interface {
          flatten() []Data
      }
      
      type Pair struct {
          fst Data
          snd Data
      }
      
      type Number float64
      
      func (p Pair) flatten() []Data {
          res := []Data{}
          if p.fst != nil {
              res = append(res, p.fst.flatten()...)
          }
          if p.snd != nil {
              res = append(res, p.snd.flatten()...)
          }
          return res
      }
      
      func (n Number) flatten() []Data {
          return []Data{n}
      }
      
      func main() {
          tests := []Data{
              Pair{Number(1.0), Pair{Number(2.0), Pair{Number(3.0), nil}}},
              Pair{Pair{Number(1.0), Number(2.0)}, Number(3.0)},
              Pair{Pair{Pair{Number(1.0), Number(2.0)}, Pair{Number(3.0), Number(4.0)}}, Pair{Pair{Number(5.0), Number(6.0)}, Number(7.0)}},
              Number(1.0),
          }
          for _, t := range tests {
              fmt.Printf("Original: %v\n", t)
              fmt.Printf("Flattened: %v\n", t.flatten())
          }
      }
      

      (假设顶级输入 Data 永远不会是 nil)。

      代码打印:

      Original: {1 {2 {3 <nil>}}}
      Flattened: [1 2 3]
      Original: {{1 2} 3}
      Flattened: [1 2 3]
      Original: {{{1 2} {3 4}} {{5 6} 7}}
      Flattened: [1 2 3 4 5 6 7]
      Original: 1
      Flattened: [1]
      

      【讨论】:

      • 在我的情况下,fst 中的数据不应该被展平,只有snd 字段中的数据应该是展平的。我应该在问题中更清楚地说明这一点(现在已经更新了)。不知道该怎么称呼它,因为它实际上并不是数据结构的完全扁平化......无论如何,我所追求的是一种比重复使用append(data, c.fst)(如果有的话)更有效的方法。
      【解决方案3】:

      正如建议的那样,编写一个递归函数最适合这个问题。但是也可以写一个非递归版本(恕我直言递归版本会更清楚):

      func flatten(d Data) []Data {
          var res []Data
      
          stack := []Data{d}
      
          for {
              if len(stack) == 0 {
                  break
              }
              switch x := stack[len(stack)-1].(type) {
              case Pair:
                  stack[len(stack)-1] = x.snd
                  stack = append(stack, x.fst)
              case Number:
                  res = append(res, x)
                  stack = stack[:len(stack)-1]
              default:
                  if x == nil {
                      stack = stack[:len(stack)-1]
                  } else {
                      panic("INVALID TYPE")
                  }
              }
          }
      
          return res
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-17
        • 1970-01-01
        • 2016-06-03
        • 2023-01-10
        • 1970-01-01
        • 1970-01-01
        • 2011-11-15
        • 2020-03-14
        相关资源
        最近更新 更多