這裏是一個繪圖程序:
void drawTree(Graphics G)
{
if (flatTree.Count <= 0) return;
if (maxItemsPerRow <= 0) return;
if (maxLevels <= 0) return;
int width = (int)G.VisibleClipBounds.Width/(maxItemsPerRow + 2);
int height = (int)G.VisibleClipBounds.Height/(maxLevels + 2);
int side = width/4;
int textOffsetX = 3;
int textOffsetY = 5;
int graphOffsetY = 50;
Size squaresize = new Size(side * 2, side * 2);
foreach (SBBTreeNode<string> node in flatTree)
{
Point P0 = new Point(node.Col * width, node.Row * height + graphOffsetY);
Point textPt = new Point(node.Col * width + textOffsetX,
node.Row * height + textOffsetY + graphOffsetY);
Point midPt = new Point(node.Col * width + side,
node.Row * height + side + graphOffsetY);
if (node.Left != null)
G.DrawLine(Pens.Black, midPt,
new Point(node.Left.Col * width + side,
node.Left.Row * height + side + graphOffsetY));
if (node.Right != null)
G.DrawLine(Pens.Black, midPt,
new Point(node.Right.Col * width + side,
node.Right.Row * height + side + graphOffsetY));
G.FillEllipse(Brushes.Beige, new Rectangle(P0, squaresize));
G.DrawString(node.Data, Font, Brushes.Black, textPt);
G.DrawEllipse(Pens.Black, new Rectangle(P0, squaresize));
}
}
其結果是:
用法:
flatTree = FlatTree();
setRows();
setCols();
panel_tree.Invalidate();
現在的各個部分,導致了這樣:
drawTree例程顯然是由Panel的Paint事件觸發的。
我使用了一些類級別的變量:
這是我建立在我的測試樹;請注意,爲了讓事情簡單一點我已經甩了你的泛型類型T
爲string
:
Dictionary<string, SBBTreeNode<string> > tree
= new Dictionary<string, SBBTreeNode<string>>();
這是樹的平坦的穿越副本,也就是說,它的元素,通過水平排列,並從左至右:
List<SBBTreeNode<string>> flatTree = new List<SBBTreeNode<string>>() ;
下面是樹的dimesions:
int maxItemsPerRow = 0;
int maxLevels = 0;
這是平坦樹是如何創建的,使用隊列:
List<SBBTreeNode<string>> FlatTree()
{
List<SBBTreeNode<string>> flatTree = new List<SBBTreeNode<string>>();
Queue<SBBTreeNode<string>> queue = new Queue<SBBTreeNode<string>>();
queue.Enqueue((SBBTreeNode<string>)(tree[tree.Keys.First()]));
flatNode(queue, flatTree);
return flatTree;
}
這是遞歸調用來獲取節點依次是:
void flatNode(Queue<SBBTreeNode<string>> queue, List<SBBTreeNode<string>>flatTree)
{
if (queue.Count == 0) return;
SBBTreeNode<string> node = queue.Dequeue();
if (!node.IsHorizontal) flatTree.Add(node);
if (node.Left != null) { queue.Enqueue(node.Left); }
if (node.Left != null && node.Left.Right != null && node.Left.Right.IsHorizontal)
queue.Enqueue(node.Left.Right);
if (node.Right != null)
{
if (node.Right.IsHorizontal) flatTree.Add(node.Right);
else queue.Enqueue(node.Right);
}
flatNode(queue, flatTree);
}
最後,我們可以設置(虛擬)座標的每個節點:
void setCols()
{
List<SBBTreeNode<string>> FT = flatTree;
int levelMax = FT.Last().Row;
int LMaxCount = FT.Count(n => n.Row == levelMax);
int LMaxCount1 = FT.Count(n => n.Row == levelMax-1);
if (LMaxCount1 > LMaxCount)
{ LMaxCount = LMaxCount1; levelMax = levelMax - 1; }
int c = 1;
foreach (SBBTreeNode<string> node in FT) if (node.Row == levelMax)
{
node.Col = ++c;
if (node.Left != null) node.Left.Col = c - 1;
if (node.Right != null) node.Right.Col = c + 1;
}
List<SBBTreeNode<string>> Exceptions = new List<SBBTreeNode<string>>();
for (int n = FT.Count- 1; n >= 0; n--)
{
SBBTreeNode<string> node = FT[n];
if (node.Row < levelMax)
{
if (node.IsHorizontal) node.Col = node.Left.Col + 1;
else if ((node.Left == null) | (node.Right == null)) {Exceptions.Add(node);}
else node.Col = (node.Left.Col + node.Right.Col)/2;
}
}
// partially filled nodes will need extra attention
foreach (SBBTreeNode<string> node in Exceptions)
textBox1.Text += "\r\n >>>" + node.Data;
maxLevels = levelMax;
maxItemsPerRow = LMaxCount;
}
請注意,我有沒有編碼部分填充的節點的特殊情況,而只是將它們添加到例外列表中;你必須決定如何處理這些問題,例如他們是否可以發生,以及他們應該在哪裏塗漆。
好的,這幾乎就是它。我們必須做兩兩件事:
我已經冒昧兩個座標字段添加到您的節點類:
public int Row { get; set; }
public int Col { get; set; }
而且我writen我ADDNODE程序以這樣的方式,每個級別節點設置在那裏。
你一定會/需要以不同的方式做。一個簡單的SetRows程序是一個單元,尤其是當你使用flatTree爲橫向:
void setRows()
{
foreach (SBBTreeNode<string> node in flatTree)
{
if (node.Left != null) node.Left.Row = node.Row + 1;
if (node.Right != null) node.Right.Row =
node.Row + 1 - (node.Right.IsHorizontal ? 1:0);
}
}
說明:
除了flatTree,我使用的繪圖,該解決方案的核心是SetCols常規。
在一個平衡的B-Tree中,在最後一行或倒數第二行都會達到最大寬度。
這裏我計算該行中的節點數量。這給了我整個樹的寬度,maxItemsPerRow。該程序還設置了高度maxLevels
現在我第一設置山口值是最寬行中,由左到右(如果存在到最後一行中晃來晃去的孩子。)
然後我逐級向上移動並計算每個Col值作爲Left和Right Child之間的中間值,始終注意水平節點。
請注意,我假設所有水平節點都是正確的孩子!如果這不是真的,你將不得不在各種適應在FlatTree和setCol例程..
該圖像顯示了一個非對稱的B型樹,我會說.. – TaW 2014-10-28 09:50:50