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()
}

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

目录
相关文章
|
7月前
|
Go 开发者 索引
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣33 / 81/ 153/154)(Go语言版)
本文深入探讨了LeetCode中四道关于「搜索旋转排序数组」的经典题目,涵盖了无重复和有重复元素的情况。通过二分查找的变形应用,文章详细解析了每道题的解题思路和Go语言实现代码。关键点包括判断有序区间、处理重复元素以及如何缩小搜索范围。文章还总结了各题的异同,并推荐了类似题目,帮助读者全面掌握二分查找在旋转数组中的应用。无论是初学者还是有经验的开发者,都能从中获得实用的解题技巧和代码实现方法。
314 14
|
8月前
|
算法 Go
【LeetCode 热题100】深入理解二叉树结构变化与路径特性(力扣104 / 226 / 114 / 543)(Go语言版)
本博客深入探讨二叉树的深度计算、结构变换与路径分析,涵盖四道经典题目:104(最大深度)、226(翻转二叉树)、114(展开为链表)和543(二叉树直径)。通过递归与遍历策略(前序、后序等),解析每题的核心思路与实现方法。结合代码示例(Go语言),帮助读者掌握二叉树相关算法的精髓。下一讲将聚焦二叉树构造问题,欢迎持续关注!
193 10
|
8月前
|
存储 算法 数据可视化
【二叉树遍历入门:从中序遍历到层序与右视图】【LeetCode 热题100】94:二叉树的中序遍历、102:二叉树的层序遍历、199:二叉树的右视图(详细解析)(Go语言版)
本文详细解析了二叉树的三种经典遍历方式:中序遍历(94题)、层序遍历(102题)和右视图(199题)。通过递归与迭代实现中序遍历,深入理解深度优先搜索(DFS);借助队列完成层序遍历和右视图,掌握广度优先搜索(BFS)。文章对比DFS与BFS的思维方式,总结不同遍历的应用场景,为后续构造树结构奠定基础。
385 10
|
8月前
|
Go
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣437 / 236 )(Go语言版)
本文深入探讨二叉树中路径与祖先问题,涵盖两道经典题目:LeetCode 437(路径总和 III)和236(最近公共祖先)。对于路径总和 III,文章分析了双递归暴力解法与前缀和优化方法,后者通过哈希表记录路径和,将时间复杂度从O(n²)降至O(n)。在最近公共祖先问题中,采用后序遍历递归查找,利用“自底向上”的思路确定最近公共祖先节点。文中详细解析代码实现与核心要点,帮助读者掌握深度追踪技巧,理解树结构中路径与节点关系的本质。这类问题在面试中高频出现,掌握其解法意义重大。
190 4
|
8月前
|
Go 索引 Perl
【LeetCode 热题100】【二叉树构造题精讲:前序 + 中序建树 & 有序数组构造 BST】(详细解析)(Go语言版)
本文详细解析了二叉树构造的两类经典问题:通过前序与中序遍历重建二叉树(LeetCode 105),以及将有序数组转化为平衡二叉搜索树(BST,LeetCode 108)。文章从核心思路、递归解法到实现细节逐一拆解,强调通过索引控制子树范围以优化性能,并对比两题的不同构造逻辑。最后总结通用构造套路,提供进阶思考方向,帮助彻底掌握二叉树构造类题目。
432 9
|
9月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
288 10
 算法系列之数据结构-二叉树
|
9月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
246 3
 算法系列之数据结构-Huffman树
|
9月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
704 19
|
11月前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
301 9