Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

Gray-Ice

个人博客兼个人网站

在学习AVL树的时候我就有用栈代替递归遍历树的想法了,当时只是觉得可行,奈何被左旋右旋单旋双旋转昏了头,拿递归实现都是硬着头皮做的,今天我实现了这个想法,感觉整个人都升华了。

PS 本篇博文只是博主实现心中突然冒出来的设想的产物,在写之前并没有查看过别的文章,而且写出来后代码也没有进行大量的测试,所以很有可能有大量的bug,不建议观看。

本篇博文的示例使用了一个路由前缀树,具有查找/插入这两个功能。实现可能会有bug,看看思路就行。

那么我讲一讲逻辑,如果看过CSAPP就会知道递归其实就是不断的压栈存数据和弹栈取数据,所以我们完全可以用栈+循环来代替递归。在下面这颗路由前缀树中,查找的逻辑是: 判断当前路由层级是否为最后一层,如果是的话就返回对应的Handler;如果不是则搜寻子节点,将全部匹配的子节点压栈。然后弹栈,开始下一次循环。这个逻辑如果用在二叉树中既可以实现先序遍历也可以实现后序遍历,只要改变一下压栈的顺序就可以了。

其实我觉得没有什么好解释的,蛮简单的,不过理解前是真的不明白,理解后就只觉得: 就这(

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package route_trie

import (
"strings"

"github.com/golang-collections/collections/stack"
)

type HandlerFunc *string

type Node struct {
ISWild bool
Path string
Children []*Node
Handler HandlerFunc
}

// 该结构体用于路由前缀树查询
type pairNode struct {
Node *Node
Index int
}

// 拆分路径
func splitPath(path string) []string {
return strings.Split(path, "/")
}

func newNode(path string) *Node {
// 判断path[0]是否为通配符,如果是,则将ISWild字段设置为true
if len(path) != 0 {
if path[0] == '*' || path[0] == ':' {
return &Node{ISWild: true, Path: path, Children: make([]*Node, 0)}
} else {
return &Node{ISWild: false, Path: path, Children: make([]*Node, 0)}
}
} else {
return &Node{ISWild: false, Path: path, Children: make([]*Node, 0)}
}
}

// 找到单个节点,不使用通配符。该函数用于插入。
func (n *Node) searchChild(path string) *Node {
if n.Children == nil {
return nil
}
for _, child := range n.Children {
if child == nil {
continue
}
if child.Path == path {
return child
}
}
return nil
}

// 找到所有匹配的子节点,使用通配符。该函数用于查询。
func (n *Node) searchChildren(path string) []*Node {
children := make([]*Node, 0)
for _, child := range n.Children {
// 查找合适的节点,添加进children
if child.Path == path || child.ISWild {
children = append(children, child)
}
}

// 返回所有匹配的结果
return children
}

// 路由树的插入操作
func (n *Node) Insert(path string, handler HandlerFunc) {
// 分割路由
paths := splitPath(path)
p_len := len(paths)

// 初始化root节点
root := n
for i := 0; i < p_len; i++ {
child := root.searchChild(paths[i])

// 没有找到匹配的子节点,所以新建一个子节点
if child == nil {
child = newNode(paths[i])
root.Children = append(root.Children, child)
}

// 将root赋值为子节点,用于下一层路由的插入
root = child
}
root.Handler = handler
}

// 查找路由对应的handler
func (n *Node) Search(path string) HandlerFunc {
// 初始化必要变量
paths := splitPath(path)
p_len := len(paths)
nstack := stack.New()
index := 0

// pairNode.Index记录当前层级,Node记录子节点
root := &pairNode{Node: n, Index: index}
for {
// 栈已经空了,查找不到结果,返回nil
if root == nil {
return nil
}
index = root.Index
// 判断是否是最后一层
if index == p_len {
return root.Node.Handler
}

// 查找匹配的子节点,将子节点全部压栈
children := root.Node.searchChildren(paths[index])
for _, child := range children {
nstack.Push(&pairNode{Index: index + 1, Node: child})
}

// 判断栈顶是否为空
if nstack.Peek() != nil {
root = nstack.Pop().(*pairNode)
} else {
root = nil
}
}
}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
route_tire "route_trie"
)

func main() {
var root route_tire.Node
root.Children = make([]*route_tire.Node, 0)
s := "hey!"
root.Insert("/*hello/hey", &s)
fmt.Println(root.Children)
fmt.Println("This is handler: ")
fmt.Println(*root.Search("/hello/hey"))
}

结果:

1
2
3
4
 go run main.go
[0xc0000ae040]
This is handler:
hey!

评论



愿火焰指引你