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

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

5c5c77badfc044529e5618243a0d01c4.png

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



预定义元素


矩形 <rect>

圆形 <circle>

椭圆 <ellipse>

直线 <line>

文字 <text>

路径 <path>

折线 <polyline>

多边形 <polygon>

... more ...


2f3933abc3524e66a7607b5f99ec9eed.png


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


圆形 <circle>

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

cx和cy属性定义圆点的x和y坐标;r属性定义圆的半径

如果省略cx和cy,圆的中心会被设置为(0, 0)




直线 <line>

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

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



文字 <text>


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


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

fill 属性定义文字的颜色


457bbc1292264f938afb3d6efa96fcf6.png



结点SVG格式


根结点

由<circle>和<text>组成,存放结点的数据域

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


子树结点

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

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



叶结点

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

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



结点坐标


坐标的确定

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

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


连线的夹角

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


5700e35f82604061ac370e5830f05d97.png


结点文本

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

func XmlNode(M, N, X, Y int, Data string, Color ...string) string {
  var cColor, tColor string
  R := 30
  Node := `<Tab><g id="M,N">
  <circle cx="X" cy="Y" r="RC" stroke="black" stroke-width="2" fill="COLOR" />
  <text x="X" y="Y" fill="TextColor" font-size="20" text-anchor="middle"
    dominant-baseline="middle">DATA</text>
  <ROOT/>
  </g><CrLf>`
  if len(Color) == 0 {
    cColor, tColor = "orange", "red" //分别圆形背景色,文字颜色
  } else if len(Color) == 1 {
    cColor, tColor = Color[0], "red"
  } else {
    cColor, tColor = Color[0], Color[1]
  }
  Node = strings.Replace(Node, "M", strconv.Itoa(M), 1)
  Node = strings.Replace(Node, "N", strconv.Itoa(N), 1)
  Node = strings.Replace(Node, "X", strconv.Itoa(X), 2)
  Node = strings.Replace(Node, "Y", strconv.Itoa(Y), 2)
  Node = strings.Replace(Node, "RC", strconv.Itoa(R), 1)
  Node = strings.Replace(Node, "DATA", Data, 1)
  Node = strings.Replace(Node, "COLOR", cColor, 1)
  Node = strings.Replace(Node, "TextColor", tColor, 1)
  Node = strings.Replace(Node, "<CrLf>", "\n", -1)
  Node = strings.Replace(Node, "<Tab>", "\t", -1)
  Node = strings.Replace(Node, "\n\t\t", " ", -1)
  return Node
}



二叉树转SVG

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


二叉树转svg

func (bt *biTree) Xml4Tree() string {
  var Xml, Node string
  Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
    "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
  Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" style="stroke:black;stroke-width:2"/>`
  Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" target="_blank">
  <text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
  List := bt.LevelNullwith()
  Levels := len(List)
  for i := Levels - 1; i >= 0; i-- {
    negative := -1
    TmpXml := ""
    for j := 0; j < Pow2(i); j++ {
      t := Pow2(Levels - i - 1)
      x, y := 50*(2*t*j+t), 120*i+50
      if List[i][j] != nil {
        fillColor := "orange"
        if i == Levels-1 || i > 0 && i < Levels-1 &&
          List[i+1][j*2] == nil && List[i+1][j*2+1] == nil {
          fillColor = "lightgreen"
        }
        TmpStr := ""
        switch List[i][j].(type) {
        case int:
          TmpStr = strconv.Itoa(List[i][j].(int))
        case float64:
          TmpStr = strconv.FormatFloat(List[i][j].(float64), 'g', -1, 64)
        case string:
          TmpStr = List[i][j].(string)
        default:
          TmpStr = "Error Type"
        }
        Xml = XmlNode(i, j, x, y, TmpStr, fillColor)
      }
      if i > 0 {
        line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
        negative *= -1
        x0, y0 := 21, 21
        x += 50*negative*(2*t*j%2+t) - negative*x0
        line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
        Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
      }
      if List[i][j] != nil {
        TmpXml += Xml
      }
    }
    Node = TmpXml + Node
  }
  Xml = strings.Replace(Head, "CONTENT", Node, 1)
  Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
  Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
  Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
  return Xml
}


写入文件、调取浏览

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

    //......
  for index, text := range texts {
    svgFile := "./biTree0" + strconv.Itoa(index+1) + ".svg"
    file, err1 = os.Create(svgFile)
    if err1 != nil {
      panic(err1)
    }
    _, err1 = io.WriteString(file, text)
    if err1 != nil {
      panic(err1)
    }
    file.Close()
    exec.Command("cmd", "/c", "start", svgFile).Start()
    //Linux 代码:
    //exec.Command("xdg-open", svgFile).Start()
    //Mac 代码:
    //exec.Command("open", svgFile).Start()
  }
    //......


满二叉树按层作图

func Xml4Full(Levels int) string {
  var Xml, Node string
  Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
    "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
  Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" style="stroke:black;stroke-width:2"/>`
  Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" target="_blank">
  <text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
  for i := 0; i < Levels; i++ {
    negative := -1
    for j := 0; j < Pow2(i); j++ {
      t := Pow2(Levels - i - 1)
      x, y := 50*(2*t*j+t), 120*i+50
      if Levels != 1 && i == Levels-1 {
        Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j), "lightgreen")
      } else {
        Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j))
      }
      if i > 0 {
        line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
        negative *= -1
        x0, y0 := 21, 21
        //过连线起始端的半径与纵轴线夹角取45度时x,y坐标修正值21,21[30/1.414]
        //取30度时 x0,y0:= 15,30-26;取60度时 x0,y0:= 26[15*1.732],15
        x += 50*negative*(2*t*j%2+t) - negative*x0
        line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
        Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
      }
      Node += Xml
    }
  }
  Xml = strings.Replace(Head, "CONTENT", Node, 1)
  Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
  Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
  Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
  return Xml
}




全部源代码

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

package main
import (
  "fmt"
  "io"
  "os"
  "os/exec"
  "strconv"
  "strings"
)
type btNode struct {
  Data   interface{}
  Lchild *btNode
  Rchild *btNode
}
type biTree struct {
  Root *btNode
}
func Build(data interface{}) *biTree {
  var list []interface{}
  if data == nil {
    return &biTree{}
  }
  switch data.(type) {
  case []interface{}:
    list = append(list, data.([]interface{})...)
  default:
    list = append(list, data)
  }
  if len(list) == 0 {
    return &biTree{}
  }
  node := &btNode{Data: list[0]}
  list = list[1:]
  Queue := []*btNode{node}
  for len(list) > 0 {
    if len(Queue) == 0 {
      //panic("Given array can not build binary tree.")
      return &biTree{Root: node}
    }
    cur := Queue[0]
    val := list[0]
    Queue = Queue[1:]
    if val != nil {
      cur.Lchild = &btNode{Data: val}
      if cur.Lchild != nil {
        Queue = append(Queue, cur.Lchild)
      }
    }
    list = list[1:]
    if len(list) > 0 {
      val := list[0]
      if val != nil {
        cur.Rchild = &btNode{Data: val}
        if cur.Rchild != nil {
          Queue = append(Queue, cur.Rchild)
        }
      }
      list = list[1:]
    }
  }
  return &biTree{Root: node}
}
func (bt *biTree) AppendNode(data interface{}) {
  root := bt.Root
  if root == nil {
    bt.Root = &btNode{Data: data}
    return
  }
  Queue := []*btNode{root}
  for len(Queue) > 0 {
    cur := Queue[0]
    Queue = Queue[1:]
    if cur.Lchild != nil {
      Queue = append(Queue, cur.Lchild)
    } else {
      cur.Lchild = &btNode{Data: data}
      return
    }
    if cur.Rchild != nil {
      Queue = append(Queue, cur.Rchild)
    } else {
      cur.Rchild = &btNode{Data: data}
      break
    }
  }
}
func Copy(bt *biTree) *biTree {
  root := bt.Root
  if root == nil {
    return &biTree{}
  }
  node := &btNode{Data: root.Data}
  Queue1, Queue2 := []*btNode{root}, []*btNode{node}
  for len(Queue1) > 0 {
    p1, p2 := Queue1[0], Queue2[0]
    Queue1, Queue2 = Queue1[1:], Queue2[1:]
    if p1.Lchild != nil {
      Node := &btNode{Data: p1.Lchild.Data}
      p2.Lchild = Node
      Queue1 = append(Queue1, p1.Lchild)
      Queue2 = append(Queue2, Node)
    }
    if p1.Rchild != nil {
      Node := &btNode{Data: p1.Rchild.Data}
      p2.Rchild = Node
      Queue1 = append(Queue1, p1.Rchild)
      Queue2 = append(Queue2, Node)
    }
  }
  return &biTree{Root: node}
}
func Mirror(bt *biTree) *biTree {
  root := bt.Root
  if root == nil {
    return &biTree{}
  }
  node := &btNode{Data: root.Data}
  Queue1, Queue2 := []*btNode{root}, []*btNode{node}
  for len(Queue1) > 0 {
    p1, p2 := Queue1[0], Queue2[0]
    Queue1, Queue2 = Queue1[1:], Queue2[1:]
    if p1.Lchild != nil {
      Node := &btNode{Data: p1.Lchild.Data}
      p2.Rchild = Node
      Queue1 = append(Queue1, p1.Lchild)
      Queue2 = append(Queue2, Node)
    }
    if p1.Rchild != nil {
      Node := &btNode{Data: p1.Rchild.Data}
      p2.Lchild = Node
      Queue1 = append(Queue1, p1.Rchild)
      Queue2 = append(Queue2, Node)
    }
  }
  return &biTree{Root: node}
}
func (bt *biTree) BForder2D() [][]interface{} {
  var res [][]interface{}
  root := bt.Root
  if root == nil {
    return res
  }
  Queue := []*btNode{root}
  for len(Queue) > 0 {
    Nodes := []interface{}{}
    Levels := len(Queue)
    for Levels > 0 {
      cur := Queue[0]
      Queue = Queue[1:]
      Nodes = append(Nodes, cur.Data)
      Levels--
      if cur.Lchild != nil {
        Queue = append(Queue, cur.Lchild)
      }
      if cur.Rchild != nil {
        Queue = append(Queue, cur.Rchild)
      }
    }
    res = append(res, Nodes)
  }
  return res
}
func (bt *biTree) LevelNullwith(Fills ...interface{}) [][]interface{} {
  var Nodes [][]interface{}
  var Fill0 interface{}
  if len(Fills) == 0 {
    Fill0 = nil
  } else if len(Fills) == 1 {
    Fill0 = Fills[0]
  } else {
    panic("Error: number of parameters is greater than 1")
  }
  root := bt.Root
  if root == nil {
    return Nodes
  }
  Count := 0
  Queue := []*btNode{root}
  for len(Queue) > 0 {
    nodes := []interface{}{}
    Level := len(Queue)
    for Level > 0 {
      cur := Queue[0]
      Queue = Queue[1:]
      nodes = append(nodes, cur.Data)
      Count++
      Level--
      if cur.Lchild != nil {
        Queue = append(Queue, cur.Lchild)
      }
      if cur.Rchild != nil {
        Queue = append(Queue, cur.Rchild)
      }
    }
    Nodes = append(Nodes, nodes)
  }
  newbiTree := Copy(bt)
  for i := 1; i < Pow2(len(Nodes))-Count; i++ {
    newbiTree.AppendNode(Fill0)
  }
  return newbiTree.BForder2D()
}
func XmlNode(M, N, X, Y int, Data string, Color ...string) string {
  var cColor, tColor string
  R := 30
  Node := `<Tab><g id="M,N">
  <circle cx="X" cy="Y" r="RC" stroke="black" stroke-width="2" fill="COLOR" />
  <text x="X" y="Y" fill="TextColor" font-size="20" text-anchor="middle"
    dominant-baseline="middle">DATA</text>
  <ROOT/>
  </g><CrLf>`
  if len(Color) == 0 {
    cColor, tColor = "orange", "red"
  } else if len(Color) == 1 {
    cColor, tColor = Color[0], "red"
  } else {
    cColor, tColor = Color[0], Color[1]
  }
  Node = strings.Replace(Node, "M", strconv.Itoa(M), 1)
  Node = strings.Replace(Node, "N", strconv.Itoa(N), 1)
  Node = strings.Replace(Node, "X", strconv.Itoa(X), 2)
  Node = strings.Replace(Node, "Y", strconv.Itoa(Y), 2)
  Node = strings.Replace(Node, "RC", strconv.Itoa(R), 1)
  Node = strings.Replace(Node, "DATA", Data, 1)
  Node = strings.Replace(Node, "COLOR", cColor, 1)
  Node = strings.Replace(Node, "TextColor", tColor, 1)
  Node = strings.Replace(Node, "<CrLf>", "\n", -1)
  Node = strings.Replace(Node, "<Tab>", "\t", -1)
  Node = strings.Replace(Node, "\n\t\t", " ", -1)
  return Node
}
func Pow2(x int) int { //x>=0
  res := 1
  for i := 0; i < x; i++ {
    res *= 2
  }
  return res
}
func Xml4Full(Levels int) string {
  var Xml, Node string
  Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
    "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
  Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" style="stroke:black;stroke-width:2"/>`
  Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" target="_blank">
  <text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
  for i := 0; i < Levels; i++ {
    negative := -1
    for j := 0; j < Pow2(i); j++ {
      t := Pow2(Levels - i - 1)
      x, y := 50*(2*t*j+t), 120*i+50
      if Levels != 1 && i == Levels-1 {
        Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j), "lightgreen")
      } else {
        Xml = XmlNode(i, j, x, y, strconv.Itoa(Pow2(i)+j))
      }
      if i > 0 {
        line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
        negative *= -1
        x0, y0 := 21, 21
        //过连线起始端的半径与纵轴线夹角取45度时x,y坐标修正值21,21[30/1.414]
        //取30度时 x0,y0:= 15,30-26;取60度时 x0,y0:= 26[15*1.732],15
        x += 50*negative*(2*t*j%2+t) - negative*x0
        line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
        Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
      }
      Node += Xml
    }
  }
  Xml = strings.Replace(Head, "CONTENT", Node, 1)
  Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
  Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
  Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
  return Xml
}
func (bt *biTree) Xml4Tree() string {
  var Xml, Node string
  Head := "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/" +
    "1999/xlink\" version=\"1.1\" width=\"Width\" height=\"Height\">\nCONTENT</svg>"
  Line := `<line x1="X1" y1="Y1" x2="X2" y2="Y2" style="stroke:black;stroke-width:2"/>`
  Link := `<a xlink:href="https://blog.csdn.net/boysoft2002" target="_blank">
  <text x="5" y="20" fill="blue">Hann's CSDN Homepage</text></a>`
  List := bt.LevelNullwith()
  Levels := len(List)
  for i := Levels - 1; i >= 0; i-- {
    negative := -1
    TmpXml := ""
    for j := 0; j < Pow2(i); j++ {
      t := Pow2(Levels - i - 1)
      x, y := 50*(2*t*j+t), 120*i+50
      if List[i][j] != nil {
        fillColor := "orange"
        if i == Levels-1 || i > 0 && i < Levels-1 &&
          List[i+1][j*2] == nil && List[i+1][j*2+1] == nil {
          fillColor = "lightgreen"
        }
        TmpStr := ""
        switch List[i][j].(type) {
        case int:
          TmpStr = strconv.Itoa(List[i][j].(int))
        case float64:
          TmpStr = strconv.FormatFloat(List[i][j].(float64), 'g', -1, 64)
        case string:
          TmpStr = List[i][j].(string)
        default:
          TmpStr = "Error Type"
        }
        Xml = XmlNode(i, j, x, y, TmpStr, fillColor)
      }
      if i > 0 {
        line1 := strings.Replace(Line, "X1", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y1", strconv.Itoa(y-30), 1)
        negative *= -1
        x0, y0 := 21, 21
        x += 50*negative*(2*t*j%2+t) - negative*x0
        line1 = strings.Replace(line1, "X2", strconv.Itoa(x), 1)
        line1 = strings.Replace(line1, "Y2", strconv.Itoa(y-120+y0), 1)
        Xml = strings.Replace(Xml, "<ROOT/>", line1, 1)
      }
      if List[i][j] != nil {
        TmpXml += Xml
      }
    }
    Node = TmpXml + Node
  }
  Xml = strings.Replace(Head, "CONTENT", Node, 1)
  Xml = strings.Replace(Xml, "Width", strconv.Itoa(Pow2(Levels)*50), 1)
  Xml = strings.Replace(Xml, "Height", strconv.Itoa(Levels*120), 1)
  Xml = strings.Replace(Xml, "<ROOT/>", Link, 1)
  return Xml
}
func main() {
  var file *os.File
  var err1 error
  list1 := []interface{}{"-", "*", 6, "+", 3, nil, nil, 2, 8}
  list2 := []interface{}{1, 2, 3, 4, 5, nil, 6, 7, 8}
  tree1 := Build(list1)
  tree2 := Build(list2)
  tree3 := Mirror(tree2)
  texts := []string{tree1.Xml4Tree(), tree2.Xml4Tree(), tree3.Xml4Tree(), Xml4Full(4)}
  for index, text := range texts {
    svgFile := "./biTree0" + strconv.Itoa(index+1) + ".svg"
    file, err1 = os.Create(svgFile)
    if err1 != nil {
      panic(err1)
    }
    _, err1 = io.WriteString(file, text)
    if err1 != nil {
      panic(err1)
    }
    file.Close()
    exec.Command("cmd", "/c", "start", svgFile).Start()
    //Linux 代码:
    //exec.Command("xdg-open", svgFile).Start()
    //Mac 代码:
    //exec.Command("open", svgFile).Start()
  }
  fmt.Println("Welcome to my homepage: https://blog.csdn.net/boysoft2002")
  exec.Command("cmd", "/c", "start", "https://blog.csdn.net/boysoft2002").Start()
}

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

目录
相关文章
|
4天前
|
Java
JAVA数据结构刷题 -- 二叉树进阶
JAVA数据结构刷题 -- 二叉树进阶
12 0
|
4天前
|
存储 Java
JAVA数据结构刷题 -- 力扣二叉树
JAVA数据结构刷题 -- 力扣二叉树
11 0
TU^
|
5天前
|
存储 机器学习/深度学习 算法
数据结构~~二叉树-堆
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
TU^
14 1
|
6天前
|
索引
[数据结构]——二叉树链式结构的实现
[数据结构]——二叉树链式结构的实现
|
6天前
|
算法
[数据结构]——二叉树——堆排序
[数据结构]——二叉树——堆排序
|
6天前
|
存储 算法 索引
[数据结构]——二叉树——堆的实现
[数据结构]——二叉树——堆的实现
|
6天前
|
存储 算法
[数据结构]—二叉树基本概念
[数据结构]—二叉树基本概念
|
14小时前
|
存储 JSON Java
【字节跳动青训营】后端笔记整理-1 | Go语言入门指南:基础语法和常用特性解析(三)
在 Go 语言里,符合语言习惯的做法是使用一个单独的返回值来传递错误信息。
12 0
|
14小时前
|
存储 Go C++
【字节跳动青训营】后端笔记整理-1 | Go语言入门指南:基础语法和常用特性解析(二)
Go 语言中的复合数据类型包括数组、切片(slice)、映射(map)和结构体(struct)。
16 0
|
20小时前
|
编译器 Serverless Go
Go 语言常量
Go 语言常量
11 1