Tải bản đầy đủ (.pdf) (88 trang)

Creating a one-to-many Map doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (286.12 KB, 88 trang )

Creating a One-to-Many Map (MultiMap)
|
415
This code displays the following:
Key: 0 Value: zero :
Key: 1 Value: one :
Key: 2 Value: two :
Key: 3 Value: three : duplicate three : duplicate three :
Key: 4 Value:
testArray.Add("BAR");
testArray.Add("BAZ");
myMap[10] = testArray;
myMap[10] = testArray;
// Remove items from MultiMap.
myMap.Remove(0);
myMap.Remove(1);
// Display MultiMap.
Console.WriteLine( );
Console.WriteLine("myMap.Count: " + myMap.Count);
foreach (KeyValuePair<int, List<string>> entry in myMap)
{
Console.Write("entry.Key: " + entry.Key.ToString( ) +
"\tentry.Value(s): ");
foreach (string str in myMap[entry.Key])
{
if (str == null)
{
Console.Write("null : ");
}
else
{


Console.Write(str + " : ");
}
}
Console.WriteLine( );
}
// Determine if the map contains the key or the value.
Console.WriteLine( );
Console.WriteLine("myMap.ContainsKey(2): " + myMap.ContainsKey(2));
Console.WriteLine("myMap.ContainsValue(two): " +
myMap.ContainsValue("two"));
Console.WriteLine("Contains Key 2: " + myMap.ContainsKey(2));
Console.WriteLine("Contains Key 12: " + myMap.ContainsKey(12));
Console.WriteLine("Contains Value two: " + myMap.ContainsValue("two"));
Console.WriteLine("Contains Value BAR: " + myMap.ContainsValue("BAR"));
// Clear all items from MultiMap.
myMap.Clear( );
}
Example 11-5. Testing the MultiMap class (continued)
416
|
Chapter 11: Data Structures and Algorithms
Key: 5 Value: foo :
Key: 6 Value: foo :
((ArrayList) myMap[3])[0]: three
((ArrayList) myMap[3])[1]: duplicate three
myMap.Count: 6
entry.Key: 2 entry.Value(s): two :
entry.Key: 3 entry.Value(s): three : duplicate three : duplicate three :
entry.Key: 4 entry.Value(s):
entry.Key: 5 entry.Value(s): foo :

entry.Key: 6 entry.Value(s): foo :
entry.Key: 10 entry.Value(s): BAR : BAZ :
myMap.ContainsKey(2): True
myMap.ContainsValue(two): True
Contains Key 2: True
Contains Key 12: False
Contains Value two: True
Contains Value BAR: True
Discussion
A one-to-many map, or multimap, allows one object, a key, to be associated, or
mapped, to zero or more objects. The
MultiMap<T,U> class presented here operates
similarly to a
Dictionary<T,U>. The MultiMap<T,U> class contains a Dictionary<T,
List<U>>
field called map that contains the actual mapping of keys to values. Several
of the
MultiMap<T,U> methods are delegated to the methods on the map Dictionary<T,
List<U>>
object.
A
Dictionary<T,U> operates on a one-to-one principle: only one key may be associ-
ated with one value at any time. However, if you need to associate multiple values
with a single key, you must use the approach used by the
MultiMap<T,U> class. The
private
map field associates a key with a single List<U> of values, which allows multi-
ple mappings of values to a single key and mappings of a single value to multiple
keys. As an added feature, a key can also be mapped to a
null value.

Here’s what happens when key-value pairs are added to a
MultiMap<t,U> object:
1. The
MultiMap<T,U>.Add method is called with a key and value provided as
parameters.
2. The
Add method checks to see whether key exists in the map Dictionary<T,
List<U>>
object.
3. If
key does not exist, it is added as a key in the map Dictionary<T, List<U>>
object. This key is associated with a new List<U> as the value associated with key
in this Hashtable.
4. If the
key does exist, the key is looked up in the map Dictionary<T, List<U>>
object, and the value is added to the key’s List<U>.
Creating a One-to-Many Map (MultiMap)
|
417
To remove a key using the Remove method, the key and List<U> pair are removed
from the map
Dictionary<T, List<U>>. This allows removal of all values associated
with a single key. The
MultiMap<T,U>.Remove method calls the RemoveSingleMap
method, which encapsulates this behavior. Removal of key “0”, and all values
mapped to this key, is performed with the following code:
myMap.Remove(1);
To remove all keys and their associated values, use the MultiMap<T,U>.Clear method.
This method removes all items from the
map Dictionary<T, List<U>>.

The other major member of the
MultiMap<T,U> class needing discussion is its indexer.
The indexer returns the List<U> of values for a particular key through its get acces-
sor. The
set accessor simply adds the List<U> provided to a single key. This code cre-
ates an array of values and attempts to map them to key “
5” in the myMap object:
List<string> testArray = new List<string>( );
testArray.Add("BAR");
testArray.Add("BAZ");
myMap["5"] = testArray;
The following code makes use of the get accessor to access each value associated
with key
"3":
Console.WriteLine(myMap[3][0]);
Console.WriteLine(myMap[3][1]);
Console.WriteLine(myMap[3][2]);
This looks somewhat similar to using a jagged array. The first indexer ([3] in the pre-
ceding examples) is used to pull the
List<U> from the map Dictionary<T, List<U>>,
and the second indexer is used to obtain the value in the
List<U>. This code displays
the following:
three
duplicate three
duplicate three
This MultiMap<T,U> class also allows the use of the foreach loop to enumerate its key-
value pairs. The following code displays each key-value pair in the
MyMap object:
foreach (KeyValuePair<int, List<string>> entry in myMap)

{
Console.Write("Key: " + entry.Key.ToString( ) + "\tValue: ");
foreach (string str in myMap[entry.Key])
{
Console.Write(str + " : ");
}
Console.WriteLine( );
}
The outer foreach loop is used to retrieve all the keys, and the inner foreach loop is
used to display each value mapped to a particular key. This code displays the follow-
ing for the initial
MyMap object:
418
|
Chapter 11: Data Structures and Algorithms
Key: 0 Value: zero :
Key: 1 Value: one :
Key: 2 Value: two :
Key: 3 Value: three : duplicate three : duplicate three :
Key: 4 Value:
Key: 5 Value: foo :
Key: 6 Value: foo :
Two methods that allow searching of the MultiMap<T,U> object are ContainsKey and
ContainsValue. The ContainsKey method searches for the specified key in the map
Dictionary<T, List<U>>
. The ContainsValue method searches for the specified value
in a
List<U> in the map Dictionary<T, List<U>>. Both methods return true if the
key-value was found or false otherwise:
Console.WriteLine("Contains Key 2: " + myMap.ContainsKey(2));

Console.WriteLine("Contains Key 12: " + myMap.ContainsKey(12));
Console.WriteLine("Contains Value two: " + myMap.ContainsValue("two"));
Console.WriteLine("Contains Value BAR: " + myMap.ContainsValue("BAR"));
Note that the ContainsKey and ContainsValue methods are both case-sensitive.
See Also
The “List<T> Class,” “Dictionary<T,U> Class,” and “IEnumerator Interface” top-
ics in the MSDN documentation.
11.4 Creating a Binary Search Tree
Problem
You need to store information in a tree structure, where the left node is less than its
parent node and the right node is greater than or equal to (in cases in which the tree
can contain duplicates) its parent. The stored information must be easily inserted
into the tree, removed from the tree, and found within the tree.
Solution
To implement a binary tree of the type described in the Problem statement, each
node must be an object that inherits from the
IComparable<T> interface. This means
that every node to be included in the binary tree must implement the
CompareTo
method. This method will allow one node to determine whether it is less than,
greater than, or equal to another node.
Use the
BinaryTree<T> class shown in Example 11-6, which contains all of the nodes
in a binary tree and lets you traverse it.
Creating a Binary Search Tree
|
419
Example 11-6. Generic BinaryTree class
using System;
using System.Collections;

using System.Collections.Generic;
public class BinaryTree<T> : IEnumerable<T>
where T: IComparable<T>
{
public BinaryTree( ) {}
public BinaryTree(T value)
{
BinaryTreeNode<T> node = new BinaryTreeNode<T>(value);
root = node;
counter = 1;
}
private int counter = 0; // Number of nodes in tree
private BinaryTreeNode<T> root = null; // Pointer to root node in this tree
public void AddNode(T value)
{
BinaryTreeNode<T> node = new BinaryTreeNode<T>(value);
++counter;
if (root == null)
{
root = node;
}
else
{
root.AddNode(node);
}
}
public void AddNode(T value, int index)
{
BinaryTreeNode<T> node = new BinaryTreeNode<T>(value, index);
++counter;

if (root == null)
{
root = node;
}
else
{
root.AddNode(node);
}
}
public BinaryTreeNode<T> SearchDepthFirst(T value)
420
|
Chapter 11: Data Structures and Algorithms
The BinaryTreeNode<T> shown in Example 11-7 encapsulates the data and behavior
of a single node in the binary tree.
{
return (root.DepthFirstSearch(value));
}
public void Print( )
{
root.PrintDepthFirst( );
}
public BinaryTreeNode<T> Root
{
get {return (root);}
}
public int TreeSize
{
get {return (counter);}
} }

Example 11-7. Generic BinaryTreeNode class
public class BinaryTreeNode<T>
where T: IComparable<T>
{
public BinaryTreeNode( ) {}
public BinaryTreeNode(T value)
{
nodeValue = value;
}
private T nodeValue = default(T);
private BinaryTreeNode<T> leftNode = null; // leftNode.nodeValue < Value
private BinaryTreeNode<T> rightNode = null; // rightNode.nodeValue >= Value
public int Children
{
get
{
int currCount = 0;
if (leftNode != null)
{
++currCount;
currCount += leftNode.Children( );
}
if (rightNode != null)
{
Example 11-6. Generic BinaryTree class (continued)
Creating a Binary Search Tree
|
421
++currCount;
currCount += rightNode.Children( );

}
return (currCount);
}
}
public BinaryTreeNode<T> Left
{
get {return (leftNode);}
}
public BinaryTreeNode<T> Right
{
get {return (rightNode);}
}
public T Value
{
get {return (nodeValue);}
}
public void AddNode(BinaryTreeNode<T> node)
{
if (node.nodeValue.CompareTo(nodeValue) < 0)
{
if (leftNode == null)
{
leftNode = node;
}
else
{
leftNode.AddNode(node);
}
}
else if (node.nodeValue.CompareTo(nodeValue) >= 0)

{
if (rightNode == null)
{
rightNode = node;
}
else
{
rightNode.AddNode(node);
}
}
}
public bool AddUniqueNode(BinaryTreeNode<T> node)
{
bool isUnique = true;
Example 11-7. Generic BinaryTreeNode class (continued)
422
|
Chapter 11: Data Structures and Algorithms
if (node.nodeValue.CompareTo(nodeValue) < 0)
{
if (leftNode == null)
{
leftNode = node;
}
else
{
leftNode.AddNode(node);
}
}
else if (node.nodeValue.CompareTo(nodeValue) > 0)

{
if (rightNode == null)
{
rightNode = node;
}
else
{
rightNode.AddNode(node);
}
}
else //node.nodeValue.CompareTo(nodeValue) = 0
{
isUnique = false;
// Could throw exception here as well
}
return (isUnique);
}
public BinaryTreeNode<T> DepthFirstSearch(T targetObj)
{
// NOTE: foo.CompareTo(bar) == -1 > (foo < bar)
BinaryTreeNode<T> retObj = null;
int comparisonResult = targetObj.CompareTo(nodeValue);
if (comparisonResult == 0)
{
retObj = this;
}
else if (comparisonResult > 0)
{
if (rightNode != null)
{

retObj = rightNode.DepthFirstSearch(targetObj);
}
}
else if (comparisonResult < 0)
{
if (leftNode != null)
{
retObj = leftNode.DepthFirstSearch(targetObj);
}
Example 11-7. Generic BinaryTreeNode class (continued)
Creating a Binary Search Tree
|
423
}
return (retObj);
}
public void PrintDepthFirst( )
{
if (leftNode != null)
{
leftNode.PrintDepthFirst( );
}
Console.WriteLine(this.nodeValue.ToString( ));
if (leftNode != null)
{
Console.WriteLine("\tContains Left: " +
leftNode.nodeValue.ToString( ));
}
else
{

Console.WriteLine("\tContains Left: NULL");
}
if (rightNode != null)
{
Console.WriteLine("\tContains Right: " +
rightNode.nodeValue.ToString( ));
}
else
{
Console.WriteLine("\tContains Right: NULL");
}
if (rightNode != null)
{
rightNode.PrintDepthFirst( );
}
}
public List<T> CopyToList( )
{
List<T> tempList = new List<T>( );
if (leftNode != null)
{
tempList.AddRange(leftNode.CopyToList( ));
tempList.Add(leftNode.nodeValue);
}
if (rightNode != null)
{
tempList.Add(rightNode.nodeValue);
tempList.AddRange(rightNode.CopyToList( ));
}
return (tempList);

}
public void RemoveLeftNode( )
Example 11-7. Generic BinaryTreeNode class (continued)
424
|
Chapter 11: Data Structures and Algorithms
The methods defined in Table 11-3 are of particular interest to using a BinaryTree<T>
object.
The methods defined in Table 11-4 are of particular interest to using a
BinaryTreeNode<T> object.
{
leftNode = null;
}
public void RemoveRightNode( )
{
rightNode = null;
}
}
Table 11-3. Members of the BinaryTree<T> class
Member Description
Overloaded constructor
This constructor creates a BinaryTree<T> object with a root node. Its syntax is:
BinaryTree(T value)
where value is the root node for the tree. Note that this tree may not be flattened.
AddNode method
Adds a node to the tree. Its syntax is:
AddNode(T value, int id)
where value is the object to be added and id is the node index. Use this method if
the tree will be flattened.
AddNode method

Adds a node to the tree. Its syntax is:
AddNode(T value)
where value is the object to be added. Use this method if the tree will not be flat-
tened.
SearchDepthFirst method Searches for and returns a BinaryTreeNode<T> object in the tree, if one exists.
This method searches the depth of the tree first. Its syntax is:
SearchDepthFirst(T value)
where value is the object to be found in the tree.
Print method Displays the tree in depth-first format. Its syntax is:
Print( )
Root property Returns the BinaryTreeNode<T> object that is the root of the tree. Its syntax is:
Root
TreeSize property A read-only property that gets the number of nodes in the tree. Its syntax is:
int TreeSize {get;}
Example 11-7. Generic BinaryTreeNode class (continued)
Creating a Binary Search Tree
|
425
The code in Example 11-8 illustrates the use of the BinaryTree<T> and
BinaryTreeNode<T> classes when creating and using a binary tree.
Table 11-4. Members of the BinaryTreeNode<T> class
Member Description
Overloaded constructor This constructor creates a BinaryTreeNode<T> object. Its syntax is:
BinaryTreeNode(T value)
where value is the object contained in this node, which will be used to compare to its
parent.
Left property A read-only property to retrieve the left child node below this node. Its syntax is:
BinaryTreeNode<T> Left {get;}
Right property A read-only property to retrieve the right child node below this node. Its syntax is:
BinaryTreeNode<T> Right {get;}

Children property Retrieves the number of child nodes below this node. Its syntax is:
Children( )
GetValue method Returns the IComparable<T> object that this node contains. Its syntax is:
GetValue( )
AddNode method Adds a new node recursively to either the left or right side. Its syntax is:
AddNode(BinaryTreeNode<T> node)
where node is the node to beadded.Duplicatenodesmaybeaddedusingthismethod.
AddUniqueNode method Adds a new node recursively to either the left side or the right side. Its syntax is:
AddUniqueNode(BinaryTreeNode<T> node)
where node is the node to be added. Duplicate nodes may not be added using this
method. A Boolean value is returned:
true indicates a successful operation; false
indicates an attempt to add a duplicate node.
DepthFirstSearch method Searches for and returns a BinaryTreeNode<T> object in the tree, if one exists. This
method searches the depth of the tree first. Its syntax is:
DepthFirstSearch(T targetObj)
where targetObj is the object to be found in the tree.
PrintDepthFirst method Displays the tree in depth-first format. Its syntax is:
PrintDepthFirst( )
RemoveLeftNode method Removes the left node and any child nodes of this node. Its syntax is:
RemoveLeftNode( )
RemoveRightNode method Removes the right node and any child nodes of this node. Its syntax is:
RemoveRightNode( )
Example 11-8. Using the BinaryTree and Binary TreeNode classes
public static void TestBinaryTree( )
{
BinaryTree<string> tree = new BinaryTree<string>("d");
tree.AddNode("a");
tree.AddNode("b");
tree.AddNode("f");

tree.AddNode("e");
426
|
Chapter 11: Data Structures and Algorithms
The output for this method is shown here:
a
Contains Left: NULL
Contains Right: b
b
Contains Left: NULL
Contains Right: c
c
Contains Left: NULL
Contains Right: NULL
d
Contains Left: a
Contains Right: f
e
Contains Left: NULL
Contains Right: NULL
tree.AddNode("c");
tree.AddNode("g");
tree.Print( );
tree.Print( );
Console.WriteLine("tree.TreeSize: " + tree.Count);
Console.WriteLine("tree.Root.DepthFirstSearch(b).Children: " +
tree.Root.DepthFirstSearch("b").Children);
Console.WriteLine("tree.Root.DepthFirstSearch(a).Children: " +
tree.Root.DepthFirstSearch("a").Children);
Console.WriteLine("tree.Root.DepthFirstSearch(g).Children: " +

tree.Root.DepthFirstSearch("g").Children);
Console.WriteLine("tree.SearchDepthFirst(a): " +
tree.SearchDepthFirst("a").Value);
Console.WriteLine("tree.SearchDepthFirst(b): " +
tree.SearchDepthFirst("b").Value);
Console.WriteLine("tree.SearchDepthFirst(c): " +
tree.SearchDepthFirst("c").Value);
Console.WriteLine("tree.SearchDepthFirst(d): " +
tree.SearchDepthFirst("d").Value);
Console.WriteLine("tree.SearchDepthFirst(e): " +
tree.SearchDepthFirst("e").Value);
Console.WriteLine("tree.SearchDepthFirst(f): " +
tree.SearchDepthFirst("f").Value);
tree.Root.RemoveLeftNode( );
tree.Print( );
tree.Root.RemoveRightNode( );
tree.Print( );
}
Example 11-8. Using the BinaryTree and Binary TreeNode classes (continued)
Creating a Binary Search Tree
|
427
f
Contains Left: e
Contains Right: g
g
Contains Left: NULL
Contains Right: NULL
a
Contains Left: NULL

Contains Right: b
b
Contains Left: NULL
Contains Right: c
c
Contains Left: NULL
Contains Right: NULL
d
Contains Left: a
Contains Right: f
e
Contains Left: NULL
Contains Right: NULL
f
Contains Left: e
Contains Right: g
g
Contains Left: NULL
Contains Right: NULL
tree.TreeSize: 7
tree.Root.DepthFirstSearch(a).Children: 1
tree.Root.DepthFirstSearch(a).Children: 2
tree.Root.DepthFirstSearch(g).Children: 0
tree.SearchDepthFirst(a): a
tree.SearchDepthFirst(b): b
tree.SearchDepthFirst(c): c
tree.SearchDepthFirst(d): d
tree.SearchDepthFirst(e): e
tree.SearchDepthFirst(f): f
d

Contains Left: NULL
Contains Right: f
e
Contains Left: NULL
Contains Right: NULL
f
Contains Left: e
Contains Right: g
g
Contains Left: NULL
Contains Right: NULL
d
Contains Left: NULL
Contains Right: NULL
428
|
Chapter 11: Data Structures and Algorithms
Discussion
Trees are data structures in which each node has exactly one parent and possibly
many children. The root of the tree is a single node that branches out into one or
more child nodes. A node is the part of the tree structure that contains data and con-
tains the branches (or in more concrete terms, references) to its children node(s).
A tree can be used for many things, such as to represent a management hierarchy
with the president of the company at the root node and the various vice presidents as
child nodes of the president. The vice presidents may have managers as child nodes,
and so on. A tree can be used to make decisions, where each node of the tree con-
tains a question, and the answer given depends on which branch is taken to a child
node. The tree described in this recipe is called a binary tree. A binary tree can have
zero, one, or two child nodes for every node in the tree. A binary tree node can never
have more than two child nodes; this is where this type of tree gets its name. (There

are other types of trees. For instance, the n-ary tree can have zero to n nodes for each
node in the tree. This type of tree is defined in Recipe 11.5.)
A binary tree is very useful for storing objects and then efficiently searching for those
objects. There are definitely more efficient algorithms out there for sorting binary
trees than the one implemented here. For example, if you need to store key/value
pairs, you can look at using the
SortedList or the SortedDictionary classes built into
the .NET Framework. For storing large amounts of data (i.e., items numbering in the
hundreds of thousands or higher), these two data structures will perform better. For
storing small numbers of items (i.e., a few hundred or lower), this data structure’s
performance will be fine.
The following algorithm is used to store objects in a binary tree:
1. Start at the root node.
2. Is this node free?
a. If yes, add the object to this node, and you are done.
b. If no, continue.
3. Is the object to be added to the tree less than (less than is determined by the
IComparable<T>.CompareTo method of the node being added) the current node?
a. If yes, follow the branch to the node on the left side of the current node, and
go to step 2.
b. If no, follow the branch to the node of the right side of the current node, and
go to step 2.
Basically, this algorithm states that the node to the left of the current node contains
an object or value less than the current node, and the node to the right of the current
node contains an object or value greater than (or equal to, if the binary tree can con-
tain duplicates) the current node.
Creating a Binary Search Tree
|
429
Searching for an object in a tree is easy. Just start at the root and ask, “Is the object I

am searching for?” If it is not, then you need to ask “is the object I am searching for
less than the current node’s object?” If it is, follow the left branch to the next node in
the tree. If it is still not the correct object, continue down the right branch to the next
node. When you get to the next node, start the process over again.
The binary tree used in this recipe is made up of two classes. The
BinaryTree<T> class
is not a part of the actual tree; rather, it acts as a starting point from which you can
create a tree, add nodes to it, search the tree for items, and retrieve the root node to
perform other actions.
The second class,
BinaryTreeNode<T>, is the heart of the binary tree and represents a
single node in the tree. This class contains all the members that are required to cre-
ate and work with a binary tree.
The
BinaryTreeNode<T> class contains a protected field, nodeValue, which contains an
object implementing the
IComparable<T> interface. This structure allows you to per-
form searches and add nodes in the correct location in the tree. The
CompareTo
method of the IComparable<T> interface is used in searching and adding methods to
determine whether you need to follow the left or right branch. See the
AddNode,
AddUniqueNode, and DepthFirstSearch methods—discussed in the following para-
graphs—to see this in action.
There are two methods to add nodes to the tree,
AddNode and AddUniqueNode. The
AddNode method allows duplicates to be introduced to the tree, whereas the
AddUniqueNode allows only unique nodes to be added.
The
DepthFirstSearch method allows the tree to be searched by first checking the

current node to see whether it contains the value searched for; if not, recursion is
used to check the left or the right node. If no matching value is found in any node,
this method returns
null.
It is interesting to note that even though the
BinaryTree<T> class is provided to cre-
ate and manage the tree of
BinaryTreeNode<T> objects, you can merely use the
BinaryTreeNode<T> class as long as you keep track of the root node yourself. The code
shown in Example 11-9 creates and manages the tree without the use of the
BinaryTree<T> class.
Example 11-9. Creating and managing a binary tree without using the BinaryTree class
public static void TestManagedTreeWithNoBinaryTreeClass( )
{
// Create the root node.
BinaryTreeNode<string> topLevel = new BinaryTreeNode<string>("d");
// Create all nodes that will be added to the tree.
BinaryTreeNode<string> one = new BinaryTreeNode<string>("b");
BinaryTreeNode<string> two = new BinaryTreeNode<string>("c");
BinaryTreeNode<string> three = new BinaryTreeNode<string>("a");
430
|
Chapter 11: Data Structures and Algorithms
The output for this method is shown here:
a
Contains Left: NULL
Contains Right: b
BinaryTreeNode<string> four = new BinaryTreeNode<string>("e");
BinaryTreeNode<string> five = new BinaryTreeNode<string>("f");
BinaryTreeNode<string> six = new BinaryTreeNode<string>("g");

// Add nodes to tree through the root.
topLevel.AddNode(three);
topLevel.AddNode(one);
topLevel.AddNode(five);
topLevel.AddNode(four);
topLevel.AddNode(two);
topLevel.AddNode(six);
// Print the tree starting at the root node.
topLevel.PrintDepthFirst( );
// Print the tree starting at node 'Three'.
three.PrintDepthFirst( );
// Display the number of child nodes of various nodes in the tree.
Console.WriteLine("topLevel.Children: " + topLevel.Children);
Console.WriteLine("one.Children: " + one.Children);
Console.WriteLine("three.Children: " + three.Children);
Console.WriteLine("six.Children: " + six.Children);
// Search the tree using the depth-first searching method.
Console.WriteLine("topLevel.DepthFirstSearch(a): " +
topLevel.DepthFirstSearch("a").Value.ToString( ));
Console.WriteLine("topLevel.DepthFirstSearch(b): " +
topLevel.DepthFirstSearch("b").Value.ToString( ));
Console.WriteLine("topLevel.DepthFirstSearch(c): " +
topLevel.DepthFirstSearch("c").Value.ToString( ));
Console.WriteLine("topLevel.DepthFirstSearch(d): " +
topLevel.DepthFirstSearch("d").Value.ToString( ));
Console.WriteLine("topLevel.DepthFirstSearch(e): " +
topLevel.DepthFirstSearch("e").Value.ToString( ));
Console.WriteLine("topLevel.DepthFirstSearch(f): " +
topLevel.DepthFirstSearch("f").Value.ToString( ));
// Remove the left child node from the root node and display the entire tree.

topLevel.RemoveLeftNode( );
topLevel.PrintDepthFirst( );
// Remove all nodes from the tree except for the root and display the tree.
topLevel.RemoveRightNode( );
topLevel.PrintDepthFirst( );
}
Example 11-9. Creating and managing a binary tree without using the BinaryTree class (continued)
Creating a Binary Search Tree
|
431
b
Contains Left: NULL
Contains Right: c
c
Contains Left: NULL
Contains Right: NULL
d
Contains Left: a
Contains Right: f
e
Contains Left: NULL
Contains Right: NULL
f
Contains Left: e
Contains Right: g
g
Contains Left: NULL
Contains Right: NULL
a
Contains Left: NULL

Contains Right: b
b
Contains Left: NULL
Contains Right: c
c
Contains Left: NULL
Contains Right: NULL
topLevel.Children: 6
one.Children: 1
three.Children: 2
six.Children: 0
topLevel.DepthFirstSearch(a): a
topLevel.DepthFirstSearch(b): b
topLevel.DepthFirstSearch(c): c
topLevel.DepthFirstSearch(d): d
topLevel.DepthFirstSearch(e): e
topLevel.DepthFirstSearch(f): f
d
Contains Left: NULL
Contains Right: f
e
Contains Left: NULL
Contains Right: NULL
f
Contains Left: e
Contains Right: g
g
Contains Left: NULL
Contains Right: NULL
d

Contains Left: NULL
Contains Right: NULL
432
|
Chapter 11: Data Structures and Algorithms
See Also
The “Queue Class” and “IComparable<T> Interface” topics in the MSDN
documentation.
11.5 Creating an n-ary Tree
Problem
You need a tree that can store a number of child nodes in each of its nodes. A binary
tree will work if each node needs to have only two children, but in this case, each
node needs to have a fixed number of child nodes greater than two.
Solution
Use the NTree<T> class shown in Example 11-10 to create the root node for the n-ary
tree.
Example 11-10. Generic NTree class
using System;
using System.Collections;
using System.Collections.Generic;
public class NTree<T> : IEnumerable<T>
where T : IComparable<T>
{
public NTree( )
{
maxChildren = int.MaxValue;
}
public NTree(int maxNumChildren)
{
maxChildren = maxNumChildren;

}
// The root node of the tree
private NTreeNode<T> root = null;
// The maximum number of child nodes that a parent node may contain
private int maxChildren = 0;
public void AddRoot(NTreeNode<T> node)
{
root = node;
}
public int MaxChildren
{
get {return (maxChildren);}
}
}
Creating an n-ary Tree
|
433
The methods defined in Table 11-5 are of particular interest to using an NTree<T>
object.
The
NTreeNodeFactory<T> class is used to create nodes for the n-ary tree. These nodes
are defined in the class
NTreeNode<U>, which is nested inside of the
NTreeNodeFactory<T> class. You are not able to create an NTreeNode<U> without the
use of this factory class, as shown in Example 11-11.
Table 11-5. Members of the NTree<T> class
Member Description
Overloaded constructor This constructor creates an NTree<T> object. Its syntax is:
NTree(int maxNumChildren)
where maxNumChildren is the maximum number of children that one node may have at

any time.
MaxChildren property A read-only property to retrieve themaximumnumberofchildrenanynodemayhave.Its syn-
tax is:
int MaxChildren {get;}
The value this property returns is set in the constructor.
AddRoot method Adds a node to the tree. Its syntax is:
AddRoot(NTreeNodeFactory<T>.NTreeNode<U> node)
where node is the node to be added as a child to the current node.
Example 11-11. Using the class to create the nodes for an n-ary tree
public class NTreeNodeFactory<T>
where T : IComparable<T>
{
public NTreeNodeFactory(NTree<T> root)
{
maxChildren = root.MaxChildren;
}
private int maxChildren = 0;
public int MaxChildren
{
get {return (maxChildren);}
}
public NTreeNode<T> CreateNode(T value)
{
return (new NTreeNode<T>(value, maxChildren));
}
// Nested Node class
public class NTreeNode<U>
where U : IComparable<U>
{
434

|
Chapter 11: Data Structures and Algorithms
public NTreeNode(U value, int maxChildren)
{
if (value != null)
{
nodeValue = value;
}
childNodes = new NTreeNode<U>[maxChildren];
}
protected U nodeValue = default(U);
protected NTreeNode<U>[] childNodes = null;
public int CountChildren
{
get
{
int currCount = 0;
for (int index = 0; index <= childNodes.GetUpperBound(0); index++)
{
if (childNodes[index] != null)
{
++currCount;
currCount += childNodes[index].CountChildren;
}
}
return (currCount);
}
}
public int CountImmediateChildren
{

get
{
int currCount = 0;
for (int index = 0; index <= childNodes.GetUpperBound(0); index++)
{
if (childNodes[index] != null)
{
++currCount;
}
}
return (currCount);
}
}
public NTreeNode<U>[] Children
Example 11-11. Using the class to create the nodes for an n-ary tree (continued)
Creating an n-ary Tree
|
435
{
get {return (childNodes);}
}
public NTreeNode<U> GetChild(int index)
{
return (childNodes[index]);
}
public U Value( )
{
return (nodeValue);
}
public void AddNode(NTreeNode<U> node)

{
int numOfNonNullNodes = CountImmediateChildren;
if (numOfNonNullNodes < childNodes.Length)
{
childNodes[numOfNonNullNodes] = node;
}
else
{
throw (new Exception("Cannot add more children to this node."));
}
}
public NTreeNode<U> DepthFirstSearch(U targetObj)
{
NTreeNode<U> retObj = default(NTreeNode<U>);
if (targetObj.CompareTo(nodeValue) == 0)
{
retObj = this;
}
else
{
for (int index=0; index<=childNodes.GetUpperBound(0); index++)
{
if (childNodes[index] != null)
{
retObj = childNodes[index].DepthFirstSearch(targetObj);
if (retObj != null)
{
break;
}
}

}
}
return (retObj);
}
Example 11-11. Using the class to create the nodes for an n-ary tree (continued)
436
|
Chapter 11: Data Structures and Algorithms
public NTreeNode<U> BreadthFirstSearch(U targetObj)
{
Queue<NTreeNode<U>> row = new Queue<NTreeNode<U>>( );
row.Enqueue(this);
while (row.Count > 0)
{
// Get next node in queue.
NTreeNode<U> currentNode = row.Dequeue( );
// Is this the node we are looking for?
if (targetObj.CompareTo(currentNode.nodeValue) == 0)
{
return (currentNode);
}
for (int index = 0;
index < currentNode.CountImmediateChildren;
index++)
{
if (currentNode.Children[index] != null)
{
row.Enqueue(currentNode.Children[index]);
}
}

}
return (null);
}
public void PrintDepthFirst( )
{
Console.WriteLine("this: " + nodeValue.ToString( ));
for (int index = 0; index < childNodes.Length; index++)
{
if (childNodes[index] != null)
{
Console.WriteLine("\tchildNodes[" + index + "]: " +
childNodes[index].nodeValue.ToString( ));
}
else
{
Console.WriteLine("\tchildNodes[" + index + "]: NULL");
}
}
for (int index = 0; index < childNodes.Length; index++)
{
if (childNodes[index] != null)
{
childNodes[index].PrintDepthFirst( );
}
Example 11-11. Using the class to create the nodes for an n-ary tree (continued)
Creating an n-ary Tree
|
437
The methods defined in Table 11-6 are of particular interest to using an
NTreeNodeFactory<T> object.

}
}
public List<U> IterateDepthFirst( )
{
List<U> tempList = new List<U>( );
for (int index = 0; index < childNodes.Length; index++)
{
if (childNodes[index] != null)
{
tempList.Add(childNodes[index].nodeValue);
}
}
for (int index = 0; index < childNodes.Length; index++)
{
if (childNodes[index] != null)
{
tempList.AddRange(childNodes[index].IterateDepthFirst( ));
}
}
return (tempList);
}
public void RemoveNode(int index)
{
// Remove node from array and compact the array.
if (index < childNodes.GetLowerBound(0) ||
index > childNodes.GetUpperBound(0))
{
throw (new ArgumentOutOfRangeException("index", index,
"Array index out of bounds."));
}

else if (index < childNodes.GetUpperBound(0))
{
Array.Copy(childNodes, index + 1, childNodes, index,
childNodes.Length - index - 1);
}
childNodes.SetValue(null, childNodes.GetUpperBound(0));
}
}
}
Example 11-11. Using the class to create the nodes for an n-ary tree (continued)
438
|
Chapter 11: Data Structures and Algorithms
The methods defined in Table 11-7 are of particular interest to using the nested
NTreeNode<U> object.
Table 11-6. Members of the NTreeNodeFactory<T> class
Member Description
Constructor
Creates a new NTreeNodeFactory<T> object that will create NTreeNode<U> objects
with the same number of
MaxChildren that the NTree<T> object passed in supports. Its
syntax is:
NTreeNodeFactory(NTree<T> root)
where root is an NTree<T> object.
MaxChildren property Read-only property that returns the maximum number of children that the NTree<T> object
supports. Its syntax is:
int MaxChildren {get;}
CreateNode method Overloaded method that returns a new NTreeNode object. Its syntax is:
CreateNode( )
CreateNode(IComparable value)

where value is the IComparable object this new node object will contain.
Table 11-7. Members of the NTreeNode<U> class
Member Description
Constructor
Creates a new NTreeNode<U> object from the NTreeNodeFactory<T>
object passed in to it. Its syntax is:
NTreeNode(T value, int maxChildren)
where value is an IComparable<T> object and maxChildren is the
total number of children allowed by this node.
NumOfChildren property Read-only property that returns the totalnumber of childrenbelow this node.
Its syntax is:
int NumOfChildren {get;}
Children property Read-only property that returns all of the non-null child-node objects in an
array that the current node contains. Its syntax is:
NTreeNode<U>[] Children {get;}
CountChildren property Recursively counts the number of non-null child nodes below the current
node and returns this value as an integer. Its syntax is:
CountChildren
CountImmediateChildren property Counts only the non-null child nodes contained in the current node. Its syn-
tax is:
CountImmediateChildren
GetChild method Uses an index to return the NTreeNode<U> contained by the current node.
Its syntax is:
GetChild(int index)
where index is the array index where the child object is stored.
Value method Returns an object of type T that the current node contains. Its syntax is:
Value( )
Creating an n-ary Tree
|
439

The code shown in Example 11-12 illustrates the use of the NTree<T>, NTree-
NodeFactory<T>
, and NTreeNode<U> classes to create and manipulate an n-ary tree.
AddNode method Adds a new child node to the current node. Its syntax is:
AddNode(NTreeNode<U> node)
where node is the child node to be added.
DepthFirstSearch method Attempts to locate an NTreeNode<U> by the IComparable<T> object
that it contains. An
NTreeNode<U> is returned if the IComparable<T>
object is located or a null if it is not. Its syntax is:
DepthFirstSearch(IComparable<T> targetObj)
where targetObj is the IComparable<T> object to locate in the tree.
Note that this search starts with the current node, which may or may not be
the root of the tree. The tree traversal is done in a depth-first manner.
BreadthFirstSearch method Attempts to locate an NTreeNode<U> by the IComparable<T> object
that it contains. An
NTreeNode<U> is returned if the IComparable<T>
object is located or a null if it is not. Its syntax is:
BreadthFirstSearch(IComparable<T> targetObj)
where targetObj is the IComparable<T> object to locate in the tree.
Note that this search starts with the current node, which may or may not be
the root of the tree. The tree traversal is done in a breadth-first manner.
PrintDepthFirst method Displays the tree structure on the console window starting with the current
node. Its syntax is:
PrintDepthFirst( )
This method uses recursion to display each node in the tree.
RemoveNode method Removes the child node at the specified index on the current node. Its syn-
tax is:
RemoveNode(int index)
where index is the array index where the child object is stored. Note that

when a node is removed, all of its children nodes are removed as well.
Example 11-12. Using the NTree<T>, NTreeNodeFactory<T>, and NTreeNode<U> classes
public static void TestNTree( )
{
NTree<string> topLevel = new NTree<string>(3);
NTreeNodeFactory<string> nodeFactory =
new NTreeNodeFactory<string>(topLevel);
NTreeNode<string> one = nodeFactory.CreateNode("One");
NTreeNode<string> two = nodeFactory.CreateNode("Two");
NTreeNode<string> three = nodeFactory.CreateNode("Three");
NTreeNode<string> four = nodeFactory.CreateNode("Four");
NTreeNode<string> five = nodeFactory.CreateNode("Five");
NTreeNode<string> six = nodeFactory.CreateNode("Six");
NTreeNode<string> seven = nodeFactory.CreateNode("Seven");
NTreeNode<string> eight = nodeFactory.CreateNode("Eight");
Table 11-7. Members of the NTreeNode<U> class (continued)
Member Description

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×