Serialize and Deserialize an N-ary Tree

多叉树的序列化与反序列化

Posted by pinylin on June 12, 2024

今天面试,碰到了比较开放性的两个问题,如果当时有纸有笔可以写写画画的话,可能就碰巧碰出火花了,人脑缓存不够,深表遗憾

Serialize and Deserialize an N-ary Tree

本来的题目是N 没有限制,但是在具体实现上child 用array 存储的话,没差别

具体参考: Serialize and Deserialize an N-ary Tree

blog 里没有Go实现我来补充一下

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
package main  
  
import (  
   "bufio"  
   "fmt"   
   "os"
)  
  
type Node struct {  
   key    rune  
   Childs []*Node  
}  
  
func newNode(key rune) *Node {  
   return &Node{key: key, Childs: make([]*Node, 0)}  
}  
  
func serialize(root *Node, writer *os.File) {  
   if root == nil {  
      return  
   }  
   fmt.Fprintf(writer, "%c", root.key)  
   for _, child := range root.Childs {  
      serialize(child, writer)  
   }  
   fmt.Fprintf(writer, "%c", ')')  
}  
  
func deserialize(reader *bufio.Reader) *Node {  
   val, _, err := reader.ReadRune()  
   //slog.Warn("%s", val)  
   if err != nil || val == ')' {  
      return nil  
   }  
   root := newNode(rune(val))  
   for {  
      child := deserialize(reader)  
      if child == nil {  
         break  
      }  
      root.Childs = append(root.Childs, child)  
   }  
   return root  
}  
  
func createDummyTree() *Node {  
   root := newNode('A')  
   root.Childs = append(root.Childs, newNode('B'), newNode('C'), newNode('D'))  
   root.Childs[0].Childs = append(root.Childs[0].Childs, newNode('E'), newNode('F'))  
   root.Childs[2].Childs = append(root.Childs[2].Childs, newNode('G'), newNode('H'), newNode('I'), newNode('J'))  
   root.Childs[0].Childs[1].Childs = append(root.Childs[0].Childs[1].Childs, newNode('K'))  
   return root  
}  
  
func traverse(root *Node) {  
   //fmt.Println(fmt.Sprintf("%+v", root))  
   if root != nil {  
      fmt.Printf("%c ", root.key)  
      //slog.Warn("%c", root.key)  
      for _, child := range root.Childs {  
         traverse(child)  
      }  
   }  
}  
  
func main() {  
   root := createDummyTree()  
   file, err := os.Create("tree.txt")  
   if err != nil {  
      fmt.Println("Could not open file")  
      return  
   }  
   defer file.Close()  
   serialize(root, file)  
  
   //file, err := os.Open("tree.txt")  
   //if err != nil {   // fmt.Println("Could not open file")   // return   //}   //defer file.Close()   r := bufio.NewReader(file)  
   root1 := deserialize(r)  
   fmt.Println("Constructed N-Ary Tree from file is")  
   traverse(root1)  
}

Goroutine Pool

背景: 是需要并发的对上面的Tree (10W+节点进行业务处理),所以会需要 G Pool,需求确实合理

Actor

首先,最容易想到的就是前两天刚看的Actor model,只需要限制 Actor 的数量,然后将Msgs 负载均衡到 发送到 Actor,就能达到限制Goroutine 并发数量和重复利用Goroutine(这里Actor资源) 的目标

两个channel

这个最简单,应该是能想到的,毕竟之前循环打印数字和字母也是两个chan互相喂饭,,,脑子转不过来,下次还是自带纸笔吧

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
type Pool struct {  
   ctrl chan struct{}  // 控制并发Goroutine 数量
   work chan func()    // 需要执行的task chan
}  

func NewPool(size, queue, spawn int) *Pool {  
   if spawn <= 0 && queue > 0 {  
      panic("dead queue configuration detected")  
   }
   if spawn > size {  
	   panic("spawn > workers")  
	}
   p := &Pool{  
      ctrl:  make(chan struct{}, size),  
      work: make(chan func(), queue),  
   }  
   for i := 0; i < spawn; i++ {  // 这里是先初始化spawn个Goroutine  warmup
      p.ctrl <- struct{}{}  
      go p.worker(func() {})  
   }  
   return p  
}

func (p *Pool) Schedule(task func()) error {  
   select {  
   case p.work <- task:         // task push到 work chan
		return nil
   case p.ctrl <- struct{}{}:    // 这里是为了控制warmup后,G的数量最后还是能达到size
		go p.worker(task)        // init worker的时候, 都会先写入令牌
		return nil
   }  
}

func (p *Pool) worker(task func()) {  
	defer func() { <-p.ctrl }()  // worker退出时,释放令牌
	task()  
	for task := range p.work {  
		task()  
	}
}

补一下benchmark

确实有提升,,,

1
2
3
4
5
BenchmarkNormalGoroutine
BenchmarkNormalGoroutine-8       1712344               694.2 ns/op
BenchmarkPool
BenchmarkPool-8                  2194712               599.1 ns/op

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
func BenchmarkNormalGoroutine(b *testing.B) {  
   b.ResetTimer()  
   for i := 0; i < b.N; i++ {  
      finish := make(chan struct{})  
      go func() {  
         for k := 0; k < 100; k++ {  
            _ = k  
         }  
         finish <- struct{}{}  
      }()  
      <-finish  
   }  
}  
  
func BenchmarkPool(b *testing.B) {  
   p := NewPool(100, 60, 80)  
   //defer p.Close()  没实现
   b.ResetTimer()  
   for i := 0; i < b.N; i++ {  
      p.Schedule(func() {  
         for k := 0; k < 100; k++ {  
            _ = k  
         }  
      })  
   }  
}