首先看看效果:
在學習二叉樹資料結構的時候,用gdi+結合佇列或者棧來畫出來乙個二叉樹的結構,如果你不是一次繪製完成,而是從每兩個線的繪製有時間間隔的話,你就能看到樹的「生長過程」,而使用棧和佇列將得到完全不同的生長方式,也許這就是深度優先遍歷和廣度優先遍歷的區別把。那麼,我當時就在想,如果繪製乙個空間中的三叉樹會是什麼樣的效果呢?有了想法就一步步的實現。之前也知道gdi+提供了一些旋轉的函式和一些投影的知識,所以應該不會太難。
如果你已經完成了二叉樹的繪製,那麼當繪製三維三叉樹的時候你也許將會遇到兩個大問題:
1,如何生成?雖然規則看起來就是1個分成3個,最後每個分支的角度就將會越來越小。你可以想象它們的位置是相對的,這樣思考起來就比較簡單了,也就是對於任何乙個支,它所生成三個枝杈都滿足乙個空間正四面體這樣的結構,然後採用一些空間幾何的運算法則你或許可以準確的計算出來每個枝杈的生長方向(角度)或者終點的xyz座標,但是這裡有一種更為簡單的方式,也就是運用一定的旋轉縮放規則,在經過一系列的變換,你就可以把乙個線段從母分支變換到子分支。而在具體的實現細節中,一定心中始終要有乙個全域性座標系和區域性座標系的概念。
2,如何顯示?在二維的情況下,每個線段只需要xy座標就可以顯示出來,並且也不會有遮擋的問題。如果你解決了第乙個問題,現在假設你已經生成了乙個完好的三維三叉樹,在螢幕上顯示的時候如果只輸出xyz座標中的兩個,那麼得到的結果和二叉樹應該是類似的,即使看到的不是乙個二叉樹,那麼樹也是死的,看不到它的另一面或者側面。這個時候就需要乙個投影法則,就是根據側投影角度和正投影角度來把(x,y,z)座標變成(x',y'),然後就可以顯示在螢幕上,改變投影角度,就可以得到上述動態圖的效果。
這是以前寫的程式,一些公式和旋轉步驟,細節等等記不太清了,大體思路如上。
下面是所有的vb.net**:
用到了一些簡單控制項,從**中可以看出來。
imports system.math
imports system.windows.media.media3d
public class form1
dim bmp as new bitmap(1000, 1000)
dim g as graphics = graphics.fromimage(bmp)
private sub _3dbinarytree_load(sender as object, e as eventargs) handles mybase.load
hscrollbar1.value = 45
vscrollbar1.value = 45
'用來存放所有的線條
dim line3dstack as new queue(of line3d)
line3dstack.enqueue(new line3d(0, 0, 0, 0, 0, 180))
do while line3dstack.count <> 0
dim wire as line3d = line3dstack.dequeue
dim q as new point3d() with
dim dx, dy, dz as single
dx = wire.x2 - wire.x1
dy = wire.y2 - wire.y1
dz = wire.z2 - wire.z1
dim rate as single = 0.6
dim len as single = sqrt(dx * dx + dy * dy + dz * dz) * rate 'po長度
'樹的最小長度
if len < 8 then
continue do
end if
dim p as new point3d with
dim o as new point3d() with
dim n as new vector3d(dx, dy, dz) 'po向量
dim m as new matrix3d() '繞po軸,o點旋轉的矩陣
m.rotateat(new quaternion(n, 120), o)
dim mv as new matrix3d()
dim zz as single
dim pp as vector3d
if dz = 0 then
pp = new vector3d(0, 0, 1)
else
zz = -(dx + dy) / dz
pp = new vector3d(1, 1, zz)
end if
mv.rotateat(new quaternion(new vector3d(1, 1, zz), 90), o) '旋轉po到abc平面
mv.scaleat(new vector3d(1 / sqrt(4), 1 / sqrt(4), 1 / sqrt(4)), o)
dim b as point3d = mv.transform(p)
dim a as point3d = m.transform(b)
dim c as point3d = m.transform(a)
line3dlist.add(wire)
line3dstack.enqueue(new line3d(p, a))
line3dstack.enqueue(new line3d(p, b))
line3dstack.enqueue(new line3d(p, c))
loop
draw3d(line3dlist, 45, 30) '45°,30°的視角觀察三維圖
end sub
dim line3dlist as new list(of line3d)
sub draw3d(lines as list(of line3d), a as single, b as single)
g.clear(color.black)
a = a * pi / 180
b = b * pi / 180
for each item as line3d in lines
dim xoffset as single = 500
dim yoffset as single = 700
dim x1 as single = item.x1 * cos(a) - item.y1 * sin(a) + xoffset
dim y1 as single = -item.x1 * sin(a) * sin(b) - item.y1 * cos(a) * cos(b) + item.z1 * cos(b) + yoffset
dim x2 as single = item.x2 * cos(a) - item.y2 * sin(a) + xoffset
dim y2 as single = -item.x2 * sin(a) * sin(b) - item.y2 * cos(a) * cos(b) + item.z2 * cos(b) + +yoffset
dim rate as single = 1
x2 *= rate
x1 *= rate
y1 *= rate
y2 *= rate
dim startpoint as new point(x1, y1)
dim endpoint as new point(x2, y2)
g.drawline(pens.white, startpoint, endpoint)
next
picturebox1.image = bmp
end sub
private sub hscrollbar1_valuechanged(sender as object, e as eventargs) handles hscrollbar1.valuechanged
draw3d(line3dlist, hscrollbar1.value, vscrollbar1.value)
end sub
private sub vscrollbar1_valuechanged(sender as object, e as eventargs) handles vscrollbar1.valuechanged
draw3d(line3dlist, hscrollbar1.value, vscrollbar1.value)
end sub
end class
三叉搜尋樹
三叉搜尋樹是用來解決字典樹的記憶體問題的資料結構。為了避免不需要的節點的記憶體占用,每個字典樹節點不再使用陣列,而是使用 樹中有樹 的結構。在三叉搜尋樹中,字典樹節點的每個非空指標得到它自己。例如,有四個單詞,ab abba abcd和bcd,它的三叉搜尋樹結構如下 三叉搜尋樹包括三種箭頭。第一種,...
Trie三叉樹分詞實現
最長匹配中文詞典分詞演算法 中文的語句與英文不同,英文單詞之間均有空格隔開,英文的語句沒有分詞的概念。而中文則不同,單詞之間沒有空格隔開。在處理中文語句時需要對中文語句進行分詞。目前多數的中文分詞的演算法採用了,最長匹配詞典的演算法。詞典是將眾多的中文片語存放在乙個檔案內。如下的詞典的格式 大 大學...
求三叉樹高度
有12345個結點的滿3叉數的高度為 寫出計算過程 1 層 1 節點數 1 2 3 4 層 2 節點數 3 5 6 7 8 9 10 11 12 13 層 3 節點數 9 滿三叉樹每層節點數目 假設k 1層有n個節點 那麼第k層就應該有3n個節點。也就是說這是乙個首項是1,公比是3的等比數列。第n層...