• go语言|数据结构:二叉树可视化(制作svg格式树形图)


    最近一直在刷二叉树题目,但在要验证结果时,通常用中序遍历、层序遍历查看结果,验证起来没有画图来得直观,所有想到自己动手制作二叉树的树形图。 直接开干,先从svg入手:

    什么是SVG?

    SVG定义

    SVG是可伸缩矢量图形 (Scalable Vector Graphics),于2003年1月14日成为 W3C 推荐标准。

    SVG 用来定义用于网络的基于矢量的图形
    SVG 使用 XML 格式定义图形
    SVG 是万维网联盟的标准
    SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体

    SVG优点

    SVG 可被非常多的工具读取和修改(比如记事本)
    SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
    SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
    SVG 图像可在任何的分辨率下被高质量地打印
    SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)
    SVG 可以与 JavaScript 技术一起运行
    SVG 是开放的标准
    SVG 文件是纯粹的 XML

    预定义元素

    矩形
    圆形
    椭圆
    直线
    文字
    路径
    折线
    多边形
    ... more ... 

    制作二叉树的树形图,就使用圆形、直线、文字三种元素即可:

    圆形

    1. <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    2. <circle cx="150" cy="50" r="30" stroke="black" stroke-width="2" fill="orange"/>
    3. svg>

     cx和cy属性定义圆点的x和y坐标;r属性定义圆的半径
    如果省略cx和cy,圆的中心会被设置为(0, 0)

    直线

    1. <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    2. <line x1="50" y1="50" x2="200" y2="200" style="stroke:red;stroke-width:2"/>
    3. svg>

    x1,y1,x2,y2 属性定义线条的直线两端点的坐标

    文字

    1. <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    2. <text x="30" y="150" fill="red">Welcome to Hann's HomePagetext>
    3. svg>

    x,y 属性定义文字左对齐显示时的起始坐标(居中对齐则是文字中间点)
    fill 属性定义文字的颜色

    结点SVG格式

    根结点

    组成,存放结点的数据域

    1. <g id="0,0">
    2. <circle cx="400" cy="50" r="30" stroke="black" stroke-width="2" fill="orange" />
    3. <text x="400" y="50" fill="red" font-size="20" text-anchor="middle" dominant-baseline="middle">1text>
    4. g>

    子树结点

    比根结点多出元素,用来表示父结点左或右指针的指向

    1. <g id="1,0">
    2. <circle cx="200" cy="170" r="30" stroke="black" stroke-width="2" fill="orange" />
    3. <text x="200" y="170" fill="red" font-size="20" text-anchor="middle" dominant-baseline="middle">2text>
    4. <line x1="200" y1="140" x2="379" y2="71" style="stroke:black;stroke-width:2"/>
    5. g>

    叶结点

    与子树结点相同,为区别显示把填充色改为greenlight

    1. <g id="1,1">
    2. <circle cx="600" cy="170" r="30" stroke="black" stroke-width="2" fill="lightgreen" />
    3. <text x="600" y="170" fill="red" font-size="20" text-anchor="middle" dominant-baseline="middle">3text>
    4. <line x1="600" y1="140" x2="421" y2="71" style="stroke:black;stroke-width:2"/>
    5. g>

    结点坐标

    坐标的确定

    结点坐标确定,把二叉树还原成满二叉树,结点位置标记为:

    [ [0,0], [1,0], [1,1], [2,0], [2,1], [2,2], [2,3], [3,0]......],再用循环计算出各点坐标。

    连线的夹角

    实际上不用考虑连线夹角,只要计算出连线始终两端点的坐标即可:

    结点文本

    以字符串形式保存好属性变量的特征关键词,用于遍历二叉树时替换成实际数据:

    1. func XmlNode(M, N, X, Y int, Data string, Color ...string) string {
    2. var cColor, tColor string
    3. R := 30
    4. Node := `
    5. dominant-baseline="middle">DATA
    6. `
    7. if len(Color) == 0 {
    8. cColor, tColor = "orange", "red" //分别圆形背景色,文字颜色
    9. } else if len(Color) == 1 {
    10. cColor, tColor = Color[0], "red"
    11. } else {
    12. cColor, tColor = Color[0], Color[1]
    13. }
    14. Node = strings.Replace(Node, "M", strconv.Itoa(M), 1)
    15. Node = strings.Replace(Node, "N", strconv.Itoa(N), 1)
    16. Node = strings.Replace(Node, "X", strconv.Itoa(X), 2)
    17. Node = strings.Replace(Node, "Y", strconv.Itoa(Y), 2)
    18. Node = strings.Replace(Node, "RC", strconv.Itoa(R), 1)
    19. Node = strings.Replace(Node, "DATA", Data, 1)
    20. Node = strings.Replace(Node, "COLOR", cColor, 1)
    21. Node = strings.Replace(Node, "TextColor", tColor, 1)
    22. Node = strings.Replace(Node, "", "\n", -1)
    23. Node = strings.Replace(Node, "", "\t", -1)
    24. Node = strings.Replace(Node, "\n\t\t", " ", -1)
    25. return Node
    26. }

    二叉树转SVG

    遍历二叉树对应的满二叉树,读出数据域并计算坐标,转成svg格式:

    二叉树转svg

    1. func (bt *biTree) Xml4Tree() string {
    2. var Xml, Node string
    3. Head := " +
    4. "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT"
    5. Line := ``
    6. Link := `
    7. List := bt.LevelNullwith()
    8. Levels := len(List)
    9. for i := Levels - 1; i >= 0; i-- {
    10. negative := -1
    11. TmpXml := ""
    12. for j := 0; j < Pow2(i); j++ {
    13. t := Pow2(Levels - i - 1)
    14. x, y := 50*(2*t*j+t), 120*i+50
    15. if List[i][j] != nil {
    16. fillColor := "orange"
    17. if i == Levels-1 || i > 0 && i < Levels-1 &&
    18. List[i+1][j*2] == nil && List[i+1][j*2+1] == nil {
    19. fillColor = "lightgreen"
    20. }
    21. TmpStr := ""
    22. switch List[i][j].(type) {
    23. case int:
    24. TmpStr = strconv.Itoa(List[i][j].(int))
    25. case float64:
    26. TmpStr = strconv.FormatFloat(List[i][j].(float64), 'g', -1, 64)
    27. case string:
    28. TmpStr = List[i][j].(string)
    29. default:
    30. TmpStr = "Error Type"
    31. }
    32. Xml = XmlNode(i, j, x, y, TmpStr, fillColor)
    33. }
    34. if i > 0 {
    35. line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
    36. line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
    37. negative *= -1
    38. x0, y0 := 21, 21
    39. x += 50*negative*(2*t*j%2+t) - negative*x0
    40. line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
    41. line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
    42. Xml = strings.Replace(Xml, "", line1, 1)
    43. }
    44. if List[i][j] != nil {
    45. TmpXml += Xml
    46. }
    47. }
    48. Node = TmpXml + Node
    49. }
    50. Xml = strings.Replace(Head, "CONTENT", Node, 1)
    51. Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
    52. Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
    53. Xml = strings.Replace(Xml, "", Link, 1)
    54. return Xml
    55. }

    写入文件、调取浏览

    要把.svg文件的默认打开方式关联为默认浏览器,否则不显示而是提示是否要下载

    1. //......
    2. for index, text := range texts {
    3. svgFile := "./biTree0" + strconv.Itoa(index+1) + ".svg"
    4. file, err1 = os.Create(svgFile)
    5. if err1 != nil {
    6. panic(err1)
    7. }
    8. _, err1 = io.WriteString(file, text)
    9. if err1 != nil {
    10. panic(err1)
    11. }
    12. file.Close()
    13. exec.Command("cmd", "/c", "start", svgFile).Start()
    14. //Linux 代码:
    15. //exec.Command("xdg-open", svgFile).Start()
    16. //Mac 代码:
    17. //exec.Command("open", svgFile).Start()
    18. }
    19. //......

     满二叉树按层作图

    1. func Xml4Full(Levels int) string {
    2. var Xml, Node string
    3. Head := " +
    4. "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT"
    5. Line := ``
    6. Link := `
    7. for i := 0; i < Levels; i++ {
    8. negative := -1
    9. for j := 0; j < Pow2(i); j++ {
    10. t := Pow2(Levels - i - 1)
    11. x, y := 50*(2*t*j+t), 120*i+50
    12. if Levels != 1 && i == Levels-1 {
    13. Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j), "lightgreen")
    14. } else {
    15. Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j))
    16. }
    17. if i > 0 {
    18. line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
    19. line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
    20. negative *= -1
    21. x0, y0 := 21, 21
    22. //过连线起始端的半径与纵轴线夹角取45度时x,y坐标修正值21,21[30/1.414]
    23. //取30度时 x0,y0:= 15,30-26;取60度时 x0,y0:= 26[15*1.732],15
    24. x += 50*negative*(2*t*j%2+t) - negative*x0
    25. line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
    26. line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
    27. Xml = strings.Replace(Xml, "", line1, 1)
    28. }
    29. Node += Xml
    30. }
    31. }
    32. Xml = strings.Replace(Head, "CONTENT", Node, 1)
    33. Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
    34. Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
    35. Xml = strings.Replace(Xml, "", Link, 1)
    36. return Xml
    37. }

    全部源代码

    本文所用的二叉树插图,都是用以下代码制作svg图片,然后另存png格式上传:

    1. package main
    2. import (
    3. "fmt"
    4. "io"
    5. "os"
    6. "os/exec"
    7. "strconv"
    8. "strings"
    9. )
    10. type btNode struct {
    11. Data interface{}
    12. Lchild *btNode
    13. Rchild *btNode
    14. }
    15. type biTree struct {
    16. Root *btNode
    17. }
    18. func Build(data interface{}) *biTree {
    19. var list []interface{}
    20. if data == nil {
    21. return &biTree{}
    22. }
    23. switch data.(type) {
    24. case []interface{}:
    25. list = append(list, data.([]interface{})...)
    26. default:
    27. list = append(list, data)
    28. }
    29. if len(list) == 0 {
    30. return &biTree{}
    31. }
    32. node := &btNode{Data: list[0]}
    33. list = list[1:]
    34. Queue := []*btNode{node}
    35. for len(list) > 0 {
    36. if len(Queue) == 0 {
    37. //panic("Given array can not build binary tree.")
    38. return &biTree{Root: node}
    39. }
    40. cur := Queue[0]
    41. val := list[0]
    42. Queue = Queue[1:]
    43. if val != nil {
    44. cur.Lchild = &btNode{Data: val}
    45. if cur.Lchild != nil {
    46. Queue = append(Queue, cur.Lchild)
    47. }
    48. }
    49. list = list[1:]
    50. if len(list) > 0 {
    51. val := list[0]
    52. if val != nil {
    53. cur.Rchild = &btNode{Data: val}
    54. if cur.Rchild != nil {
    55. Queue = append(Queue, cur.Rchild)
    56. }
    57. }
    58. list = list[1:]
    59. }
    60. }
    61. return &biTree{Root: node}
    62. }
    63. func (bt *biTree) AppendNode(data interface{}) {
    64. root := bt.Root
    65. if root == nil {
    66. bt.Root = &btNode{Data: data}
    67. return
    68. }
    69. Queue := []*btNode{root}
    70. for len(Queue) > 0 {
    71. cur := Queue[0]
    72. Queue = Queue[1:]
    73. if cur.Lchild != nil {
    74. Queue = append(Queue, cur.Lchild)
    75. } else {
    76. cur.Lchild = &btNode{Data: data}
    77. return
    78. }
    79. if cur.Rchild != nil {
    80. Queue = append(Queue, cur.Rchild)
    81. } else {
    82. cur.Rchild = &btNode{Data: data}
    83. break
    84. }
    85. }
    86. }
    87. func Copy(bt *biTree) *biTree {
    88. root := bt.Root
    89. if root == nil {
    90. return &biTree{}
    91. }
    92. node := &btNode{Data: root.Data}
    93. Queue1, Queue2 := []*btNode{root}, []*btNode{node}
    94. for len(Queue1) > 0 {
    95. p1, p2 := Queue1[0], Queue2[0]
    96. Queue1, Queue2 = Queue1[1:], Queue2[1:]
    97. if p1.Lchild != nil {
    98. Node := &btNode{Data: p1.Lchild.Data}
    99. p2.Lchild = Node
    100. Queue1 = append(Queue1, p1.Lchild)
    101. Queue2 = append(Queue2, Node)
    102. }
    103. if p1.Rchild != nil {
    104. Node := &btNode{Data: p1.Rchild.Data}
    105. p2.Rchild = Node
    106. Queue1 = append(Queue1, p1.Rchild)
    107. Queue2 = append(Queue2, Node)
    108. }
    109. }
    110. return &biTree{Root: node}
    111. }
    112. func Mirror(bt *biTree) *biTree {
    113. root := bt.Root
    114. if root == nil {
    115. return &biTree{}
    116. }
    117. node := &btNode{Data: root.Data}
    118. Queue1, Queue2 := []*btNode{root}, []*btNode{node}
    119. for len(Queue1) > 0 {
    120. p1, p2 := Queue1[0], Queue2[0]
    121. Queue1, Queue2 = Queue1[1:], Queue2[1:]
    122. if p1.Lchild != nil {
    123. Node := &btNode{Data: p1.Lchild.Data}
    124. p2.Rchild = Node
    125. Queue1 = append(Queue1, p1.Lchild)
    126. Queue2 = append(Queue2, Node)
    127. }
    128. if p1.Rchild != nil {
    129. Node := &btNode{Data: p1.Rchild.Data}
    130. p2.Lchild = Node
    131. Queue1 = append(Queue1, p1.Rchild)
    132. Queue2 = append(Queue2, Node)
    133. }
    134. }
    135. return &biTree{Root: node}
    136. }
    137. func (bt *biTree) BForder2D() [][]interface{} {
    138. var res [][]interface{}
    139. root := bt.Root
    140. if root == nil {
    141. return res
    142. }
    143. Queue := []*btNode{root}
    144. for len(Queue) > 0 {
    145. Nodes := []interface{}{}
    146. Levels := len(Queue)
    147. for Levels > 0 {
    148. cur := Queue[0]
    149. Queue = Queue[1:]
    150. Nodes = append(Nodes, cur.Data)
    151. Levels--
    152. if cur.Lchild != nil {
    153. Queue = append(Queue, cur.Lchild)
    154. }
    155. if cur.Rchild != nil {
    156. Queue = append(Queue, cur.Rchild)
    157. }
    158. }
    159. res = append(res, Nodes)
    160. }
    161. return res
    162. }
    163. func (bt *biTree) LevelNullwith(Fills ...interface{}) [][]interface{} {
    164. var Nodes [][]interface{}
    165. var Fill0 interface{}
    166. if len(Fills) == 0 {
    167. Fill0 = nil
    168. } else if len(Fills) == 1 {
    169. Fill0 = Fills[0]
    170. } else {
    171. panic("Error: number of parameters is greater than 1")
    172. }
    173. root := bt.Root
    174. if root == nil {
    175. return Nodes
    176. }
    177. Count := 0
    178. Queue := []*btNode{root}
    179. for len(Queue) > 0 {
    180. nodes := []interface{}{}
    181. Level := len(Queue)
    182. for Level > 0 {
    183. cur := Queue[0]
    184. Queue = Queue[1:]
    185. nodes = append(nodes, cur.Data)
    186. Count++
    187. Level--
    188. if cur.Lchild != nil {
    189. Queue = append(Queue, cur.Lchild)
    190. }
    191. if cur.Rchild != nil {
    192. Queue = append(Queue, cur.Rchild)
    193. }
    194. }
    195. Nodes = append(Nodes, nodes)
    196. }
    197. newbiTree := Copy(bt)
    198. for i := 1; i < Pow2(len(Nodes))-Count; i++ {
    199. newbiTree.AppendNode(Fill0)
    200. }
    201. return newbiTree.BForder2D()
    202. }
    203. func XmlNode(M, N, X, Y int, Data string, Color ...string) string {
    204. var cColor, tColor string
    205. R := 30
    206. Node := `
    207. dominant-baseline="middle">DATA
    208. `
    209. if len(Color) == 0 {
    210. cColor, tColor = "orange", "red"
    211. } else if len(Color) == 1 {
    212. cColor, tColor = Color[0], "red"
    213. } else {
    214. cColor, tColor = Color[0], Color[1]
    215. }
    216. Node = strings.Replace(Node, "M", strconv.Itoa(M), 1)
    217. Node = strings.Replace(Node, "N", strconv.Itoa(N), 1)
    218. Node = strings.Replace(Node, "X", strconv.Itoa(X), 2)
    219. Node = strings.Replace(Node, "Y", strconv.Itoa(Y), 2)
    220. Node = strings.Replace(Node, "RC", strconv.Itoa(R), 1)
    221. Node = strings.Replace(Node, "DATA", Data, 1)
    222. Node = strings.Replace(Node, "COLOR", cColor, 1)
    223. Node = strings.Replace(Node, "TextColor", tColor, 1)
    224. Node = strings.Replace(Node, "", "\n", -1)
    225. Node = strings.Replace(Node, "", "\t", -1)
    226. Node = strings.Replace(Node, "\n\t\t", " ", -1)
    227. return Node
    228. }
    229. func Pow2(x int) int { //x>=0
    230. res := 1
    231. for i := 0; i < x; i++ {
    232. res *= 2
    233. }
    234. return res
    235. }
    236. func Xml4Full(Levels int) string {
    237. var Xml, Node string
    238. Head := " +
    239. "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT"
    240. Line := ``
    241. Link := `
    242. for i := 0; i < Levels; i++ {
    243. negative := -1
    244. for j := 0; j < Pow2(i); j++ {
    245. t := Pow2(Levels - i - 1)
    246. x, y := 50*(2*t*j+t), 120*i+50
    247. if Levels != 1 && i == Levels-1 {
    248. Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j), "lightgreen")
    249. } else {
    250. Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j))
    251. }
    252. if i > 0 {
    253. line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
    254. line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
    255. negative *= -1
    256. x0, y0 := 21, 21
    257. //过连线起始端的半径与纵轴线夹角取45度时x,y坐标修正值21,21[30/1.414]
    258. //取30度时 x0,y0:= 15,30-26;取60度时 x0,y0:= 26[15*1.732],15
    259. x += 50*negative*(2*t*j%2+t) - negative*x0
    260. line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
    261. line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
    262. Xml = strings.Replace(Xml, "", line1, 1)
    263. }
    264. Node += Xml
    265. }
    266. }
    267. Xml = strings.Replace(Head, "CONTENT", Node, 1)
    268. Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
    269. Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
    270. Xml = strings.Replace(Xml, "", Link, 1)
    271. return Xml
    272. }
    273. func (bt *biTree) Xml4Tree() string {
    274. var Xml, Node string
    275. Head := " +
    276. "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT"
    277. Line := ``
    278. Link := `
    279. List := bt.LevelNullwith()
    280. Levels := len(List)
    281. for i := Levels - 1; i >= 0; i-- {
    282. negative := -1
    283. TmpXml := ""
    284. for j := 0; j < Pow2(i); j++ {
    285. t := Pow2(Levels - i - 1)
    286. x, y := 50*(2*t*j+t), 120*i+50
    287. if List[i][j] != nil {
    288. fillColor := "orange"
    289. if i == Levels-1 || i > 0 && i < Levels-1 &&
    290. List[i+1][j*2] == nil && List[i+1][j*2+1] == nil {
    291. fillColor = "lightgreen"
    292. }
    293. TmpStr := ""
    294. switch List[i][j].(type) {
    295. case int:
    296. TmpStr = strconv.Itoa(List[i][j].(int))
    297. case float64:
    298. TmpStr = strconv.FormatFloat(List[i][j].(float64), 'g', -1, 64)
    299. case string:
    300. TmpStr = List[i][j].(string)
    301. default:
    302. TmpStr = "Error Type"
    303. }
    304. Xml = XmlNode(i, j, x, y, TmpStr, fillColor)
    305. }
    306. if i > 0 {
    307. line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
    308. line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
    309. negative *= -1
    310. x0, y0 := 21, 21
    311. x += 50*negative*(2*t*j%2+t) - negative*x0
    312. line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
    313. line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
    314. Xml = strings.Replace(Xml, "", line1, 1)
    315. }
    316. if List[i][j] != nil {
    317. TmpXml += Xml
    318. }
    319. }
    320. Node = TmpXml + Node
    321. }
    322. Xml = strings.Replace(Head, "CONTENT", Node, 1)
    323. Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
    324. Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
    325. Xml = strings.Replace(Xml, "", Link, 1)
    326. return Xml
    327. }
    328. func main() {
    329. var file *os.File
    330. var err1 error
    331. list1 := []interface{}{"-", "*", 6, "+", 3, nil, nil, 2, 8}
    332. list2 := []interface{}{1, 2, 3, 4, 5, nil, 6, 7, 8}
    333. tree1 := Build(list1)
    334. tree2 := Build(list2)
    335. tree3 := Mirror(tree2)
    336. texts := []string{tree1.Xml4Tree(), tree2.Xml4Tree(), tree3.Xml4Tree(), Xml4Full(4)}
    337. for index, text := range texts {
    338. svgFile := "./biTree0" + strconv.Itoa(index+1) + ".svg"
    339. file, err1 = os.Create(svgFile)
    340. if err1 != nil {
    341. panic(err1)
    342. }
    343. _, err1 = io.WriteString(file, text)
    344. if err1 != nil {
    345. panic(err1)
    346. }
    347. file.Close()
    348. exec.Command("cmd", "/c", "start", svgFile).Start()
    349. //Linux 代码:
    350. //exec.Command("xdg-open", svgFile).Start()
    351. //Mac 代码:
    352. //exec.Command("open", svgFile).Start()
    353. }
    354. fmt.Println("Welcome to my homepage: https://blog.csdn.net/boysoft2002")
    355. exec.Command("cmd", "/c", "start", "https://blog.csdn.net/boysoft2002").Start()
    356. }

    至此,已达成自己的预想结果,算法实在有点笨拙,但总算成功了。如有达人会更好的算法,且刚好看到此文,请务必给予点拨指教(不吝)......

     上面一长条“细线”并非分割线,而是11层的满二叉树,太宽了点击放大看看!

  • 相关阅读:
    SpringMVC - 以 Servlet 3.0 的方式搭建 SSM 框架
    《动态规划 ---- 线性规划一》----- 动态规划的基本概念,线性动态规划-->背包问题
    如何通过编码器信号计算输送线/输送带线速度(飞剪、追剪算法基础)
    Spring MVC HandlerMethodReturnValueHandler原理解析
    案例分享 生产环境逐步迁移至k8s集群 - pod注册到consul
    TSINGSEE青犀老旧小区升级改造AI+视频监控方案
    驾校教练爆笑语录
    秒懂 BeanFactory、BeanFactoryPostProcessor、BeanPostProcessor、FactoryBean的区别
    MinGW-w64下载文件失败the file has been downloaded incorrectly!
    【转】浅谈威胁狩猎(Threat Hunting)
  • 原文地址:https://blog.csdn.net/boysoft2002/article/details/126685307