以图形展示任意二叉树,如下图,一个中缀表达式表示的二叉树:3.14*r²*h/3
- package main
-
- import (
- "fmt"
- "io"
- "os"
- "os/exec"
- "strconv"
- "strings"
- )
-
- type any = interface{}
-
- type btNode struct {
- Data any
- Lchild *btNode
- Rchild *btNode
- }
-
- type biTree struct {
- Root *btNode
- Info *biTreeInfo
- }
-
- type biTreeInfo struct {
- Data []any
- DataLevel [][]any
- L, R []bool
- X, Y, W []int
- Index, Nodes int
- Width, Height int
- MarginX, MarginY int
- SpaceX, SpaceY int
- SvgWidth, SvgHeight int
- SvgXml string
- }
-
- func Build(Data ...any) *biTree {
- if len(Data) == 0 || Data[0] == nil {
- return &biTree{}
- }
- node := &btNode{Data: Data[0]}
- Queue := []*btNode{node}
- for lst := Data[1:]; len(lst) > 0 && len(Queue) > 0; {
- cur, val := Queue[0], lst[0]
- Queue, lst = Queue[1:], lst[1:]
- if val != nil {
- cur.Lchild = &btNode{Data: val}
- Queue = append(Queue, cur.Lchild)
- }
- if len(lst) > 0 {
- val, lst = lst[0], lst[1:]
- if val != nil {
- cur.Rchild = &btNode{Data: val}
- Queue = append(Queue, cur.Rchild)
- }
- }
- }
- return &biTree{Root: node}
- }
-
- func BuildFromList(List []any) *biTree {
- return Build(List...)
- }
-
- func AinArray(sub int, array []int) int {
- for idx, arr := range array {
- if sub == arr {
- return idx
- }
- }
- return -1
- }
-
- func Pow2(x int) int { //x>=0
- res := 1
- for i := 0; i < x; i++ {
- res *= 2
- }
- return res
- }
-
- func Max(L, R int) int {
- if L > R {
- return L
- } else {
- return R
- }
- }
-
- func (bt *btNode) MaxDepth() int {
- if bt == nil {
- return 0
- }
- Lmax := bt.Lchild.MaxDepth()
- Rmax := bt.Rchild.MaxDepth()
- return 1 + Max(Lmax, Rmax)
- }
-
- func (bt *btNode) Coordinate(x, y, w int) []any {
- var res []any
- if bt != nil {
- L, R := bt.Lchild != nil, bt.Rchild != nil
- res = append(res, []any{bt.Data, L, R, x, y, w})
- res = append(res, bt.Lchild.Coordinate(x-w, y+1, w/2)...)
- res = append(res, bt.Rchild.Coordinate(x+w, y+1, w/2)...)
- }
- return res
- }
-
- func (bt *biTree) NodeInfo() []any {
- return bt.Root.Coordinate(0, 0, Pow2(bt.Root.MaxDepth()-2))
- }
-
- func (bt *biTree) TreeInfo() {
- height := bt.Root.MaxDepth()
- width := Pow2(height - 1)
- lsInfo := bt.NodeInfo()
- btInfo := &biTreeInfo{
- Height: height,
- Width: width,
- Nodes: len(lsInfo),
- }
- for _, data := range lsInfo {
- for i, info := range data.([]any) {
- switch i {
- case 0:
- btInfo.Data = append(btInfo.Data, info.(any))
- case 1:
- btInfo.L = append(btInfo.L, info.(bool))
- case 2:
- btInfo.R = append(btInfo.R, info.(bool))
- case 3:
- btInfo.X = append(btInfo.X, info.(int))
- case 4:
- btInfo.Y = append(btInfo.Y, info.(int))
- case 5:
- btInfo.W = append(btInfo.W, info.(int))
- }
- }
- }
- for j, k := 0, width*2; j < height; j++ {
- DLevel := []any{}
- for i := k / 2; i < width*2; i += k {
- index := AinArray(i-width, btInfo.X)
- if index > -1 {
- DLevel = append(DLevel, btInfo.Data[index])
- } else {
- DLevel = append(DLevel, nil)
- }
- DLevel = append(DLevel, []int{i, j})
- if k/4 == 0 {
- DLevel = append(DLevel, []int{0, 0})
- DLevel = append(DLevel, []int{0, 0})
- } else {
- DLevel = append(DLevel, []int{i - k/4, j + 1})
- DLevel = append(DLevel, []int{i + k/4, j + 1})
- }
- }
- k /= 2
- btInfo.DataLevel = append(btInfo.DataLevel, DLevel)
- }
- bt.Info = btInfo
- }
-
- func (bt *biTree) Info2SVG(Margin ...int) string {
- var res, Line, Color string
- info := bt.Info
- MarginX, MarginY := 0, 10
- SpaceX, SpaceY := 40, 100
- switch len(Margin) {
- case 0:
- break
- case 1:
- MarginX = Margin[0]
- case 2:
- MarginX, MarginY = Margin[0], Margin[1]
- case 3:
- MarginX, MarginY, SpaceX = Margin[0], Margin[1], Margin[2]
- default:
- MarginX, MarginY = Margin[0], Margin[1]
- SpaceX, SpaceY = Margin[2], Margin[3]
- }
- info.MarginX, info.MarginY = MarginX, MarginY
- info.SpaceX, info.SpaceY = SpaceX, SpaceY
- info.SvgWidth = Pow2(info.Height)*info.SpaceX + info.SpaceX
- info.SvgHeight = info.Height * info.SpaceY
- for i, Data := range info.Data {
- Node := "\n\t
\n\t \n\t \n\t \n\t " - DataStr := ""
- switch Data.(type) {
- case int:
- DataStr = strconv.Itoa(Data.(int))
- case float64:
- DataStr = strconv.FormatFloat(Data.(float64), 'g', -1, 64)
- case string:
- DataStr = Data.(string)
- default:
- DataStr = "Error Type"
- }
- Node = strings.Replace(Node, "INDEX", strconv.Itoa(info.Index), 1)
- Node = strings.Replace(Node, "M", strconv.Itoa(info.X[i]), 1)
- Node = strings.Replace(Node, "N", strconv.Itoa(info.Y[i]), 1)
- x0, y0 := (info.X[i]+info.Width)*SpaceX+MarginX, 50+info.Y[i]*SpaceY+MarginY
- x1, y1 := x0-info.W[i]*SpaceX, y0+SpaceY-30
- x2, y2 := x0+info.W[i]*SpaceX, y0+SpaceY-30
- Color = "orange"
- if info.L[i] && info.R[i] {
- Line = XmlLine(x0-21, y0+21, x1, y1) + "\n\t" + XmlLine(x0+21, y0+21, x2, y2)
- } else if info.L[i] && !info.R[i] {
- Line = XmlLine(x0-21, y0+21, x1, y1)
- } else if !info.L[i] && info.R[i] {
- Line = XmlLine(x0+21, y0+21, x2, y2)
- } else {
- Color = "lightgreen"
- }
- Node = strings.Replace(Node, "
", XmlCircle(x0, y0, Color), 1) - Node = strings.Replace(Node, "
", XmlText(x0, y0, DataStr), 1) - if info.L[i] || info.R[i] {
- Node = strings.Replace(Node, "
", Line, 1) - }
- res += Node
- }
- info.SvgXml = res
- return res
- }
-
- func XmlCircle(X, Y int, Color string) string {
- Radius := 30
- Circle := "
+ strconv.Itoa(X) + "\" cy=\"" + strconv.Itoa(Y) + - "\" r=\"" + strconv.Itoa(Radius) + "\" stroke=\"black\" stroke-width=" +
- "\"2\" fill=\"" + Color + "\" />"
- return Circle
- }
-
- func XmlText(X, Y int, DATA string) string {
- iFontSize, tColor := 20, "red"
- Text := "
+ strconv.Itoa(X) + "\" y=\"" + strconv.Itoa(Y) + - "\" fill=\"" + tColor + "\" font-size=\"" + strconv.Itoa(iFontSize) +
- "\" text-anchor=\"middle\" dominant-baseline=\"middle\">" + DATA + ""
- return Text
- }
-
- func XmlLine(X1, Y1, X2, Y2 int) string {
- Line := "
+ strconv.Itoa(X1) + "\" y1=\"" + strconv.Itoa(Y1) + - "\" x2=\"" + strconv.Itoa(X2) + "\" y2=\"" + strconv.Itoa(Y2) +
- "\" style=\"stroke:black;stroke-width:2\" />"
- return Line
- }
-
- func (bt *biTree) ShowSVG(FileName ...string) {
- var file *os.File
- var err1 error
- Head := "
- "=\"http://www.w3.org/1999/xlink\" version=\"1.1\" width=" +
- "\"Width\" height=\"Height\">\nLINKCONTENT\n"
-
Hann's CSDN Homepage ` - Xml := strings.Replace(Head, "LINK", Link, 1)
- Xml = strings.Replace(Xml, "Width", strconv.Itoa(bt.Info.SvgWidth), 1)
- Xml = strings.Replace(Xml, "Height", strconv.Itoa(bt.Info.SvgHeight), 1)
- Xml = strings.Replace(Xml, "CONTENT", bt.Info.SvgXml, 1)
- svgFile := "biTree.svg"
- if len(FileName) > 0 {
- svgFile = FileName[0] + ".svg"
- }
- file, err1 = os.Create(svgFile)
- if err1 != nil {
- panic(err1)
- }
- _, err1 = io.WriteString(file, Xml)
- 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 main() {
-
- list := []any{"*", "*", "*", "/", 5, "*", 3.14, 1, 3, nil, nil, 6, 6}
- tree := Build(list...)
- tree.TreeInfo()
- tree.Info2SVG()
- tree.ShowSVG()
-
- fmt.Println(tree.Info.Data)
- fmt.Println(tree.Info.DataLevel)
-
- }
增加一个结构biTreeInfo,在遍历二叉树时把作图要用的信息存入此结构中,方便读取信息。
type any = interface{} type btNode struct { Data any Lchild *btNode Rchild *btNode } type biTree struct { Root *btNode Info *biTreeInfo } type biTreeInfo struct { Data []any DataLevel [][]any L, R []bool X, Y, W []int Index, Nodes int Width, Height int MarginX, MarginY int SpaceX, SpaceY int SvgWidth, SvgHeight int SvgXml string }//数据域类型用 type any = interface{} 自定义类型,模拟成any数据类型。
遍历二叉树获取每个结点在svg图形中的坐标,使用先序递归遍历:
func (bt *btNode) Coordinate(x, y, w int) []any {
var res []any
if bt != nil {
L, R := bt.Lchild != nil, bt.Rchild != nil
res = append(res, []any{bt.Data, L, R, x, y, w})
res = append(res, bt.Lchild.Coordinate(x-w, y+1, w/2)...)
res = append(res, bt.Rchild.Coordinate(x+w, y+1, w/2)...)
}
return res
}
二叉树的每个结点,转svg时有圆、文字、左或右直线(叶结点没有真线)。
func XmlCircle(X, Y int, Color string) string {
Radius := 30
Circle := ""\" r=\"" + strconv.Itoa(Radius) + "\" stroke=\"black\" stroke-width=" +
"\"2\" fill=\"" + Color + "\" />"
return Circle
}func XmlText(X, Y int, DATA string) string {
iFontSize, tColor := 20, "red"
Text := ""\" fill=\"" + tColor + "\" font-size=\"" + strconv.Itoa(iFontSize) + "
"\" text-anchor=\"middle\" dominant-baseline=\"middle\">" + DATA + "
return Text
}func XmlLine(X1, Y1, X2, Y2 int) string {
Line := ""\" x2=\"" + strconv.Itoa(X2) + "\" y2=\"" + strconv.Itoa(Y2) +
"\" style=\"stroke:black;stroke-width:2\" />"
return Line
}
TreeInfo()写入二叉树结点信息,其中DataLevel是层序遍历的结果,也可以用它来作图。
Info2XML()就是把上述方法所得信息,转化成SVG的xml代码;
ShowSVG()生成并显示图形,svg的xml如下:
多棵二叉树同时展示,Info2SVG()可以设置起始位置
- tree2 := Build("*", "*", 3.14, 6, 6)
- tree2.TreeInfo()
- tree2.Info2SVG()
- tree2.ShowSVG("tree2")
-
- //左右并列展示
- tree2.Info2SVG(tree.Info.SvgWidth, tree.Info.SpaceY)
- tree.Info.SvgXml += tree2.Info.SvgXml
- tree.Info.SvgWidth += tree2.Info.SvgWidth
- tree.ShowSVG("tree12")
- tree.Info2SVG() //恢复tree原状

- //上下并列展示
- tree2.Info2SVG(tree.Info.SvgWidth-tree2.Info.SvgWidth, tree.Info.SvgHeight)
- tree.Info.SvgXml += tree2.Info.SvgXml
- tree.Info.SvgHeight += tree2.Info.SvgHeight
- tree.ShowSVG("tree123")
- tree.Info2SVG() //恢复tree原状
以上2段代码放在前文源代码的main()函数中测试。
结点显示的代码固定了文字和圆形的大小颜色,如果读者愿意自己动手的话,可以尝试把这些要素设成参数或者增加biTreeInfo结构的属性。
