<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近一直在刷二元樹題目,但在要驗證結果時,通常用中序遍歷、層序遍歷檢視結果,驗證起來沒有畫圖來得直觀,所有想到自己動手製作二元樹的樹形圖。 直接開幹,先從svg入手:
SVG是可伸縮向量圖形 (Scalable Vector Graphics),於2003年1月14日成為 W3C 推薦標準。
SVG 用來定義用於網路的基於向量的圖形
SVG 使用 XML 格式定義圖形
SVG 是全球資訊網聯盟的標準
SVG 與諸如 DOM 和 XSL 之類的 W3C 標準是一個整體
製作二元樹的樹形圖,就使用圓形、直線、文字三種即可:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <circle cx="150" cy="50" r="30" stroke="black" stroke-width="2" fill="red"/> </svg>
cx和cy屬性定義圓點的x和y座標;r屬性定義圓的半徑
如果省略cx和cy,圓的中心會被設定為(0, 0)
<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,y2 屬性定義線條的起始端點座標
x2,y2 屬性定義線條的結束端點座標
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <text x="30" y="150" fill="red">Welcome to Hann's HomePage</text> </svg>
x,y 屬性定義文字左對齊顯示時的起始座標(居中對齊則是文字中間點)
fill 屬性定義文字的顏色
由<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]......],再用迴圈計算出各點座標。
連線的夾角
實際上不用考慮連線夾角,只要計算出連線始終兩端點的座標即可:
以字串形式儲存好屬性變數的特徵關鍵詞,用於遍歷二元樹時替換成實際資料:
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, "ntt", " ", -1) return Node }
遍歷二元樹對應的滿二元樹,讀出資料域並計算座標,轉成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 }
寫入檔案、調取瀏覽
//...... 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() } //......
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, "ntt", " ", -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" rel="external nofollow" rel="external nofollow" 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" rel="external nofollow" rel="external nofollow" 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() }
至此,已達成自己的預想結果,演演算法實在有點笨拙,但總算成功了。
到此這篇關於Go語言製作svg格式樹形圖的範例程式碼的文章就介紹到這了,更多相關Go語言樹形圖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45