Generic BinaryTree class private int counter = 0; // Number of nodes in tree private BinaryTreeNode root = null; // Pointer to root node in this tree public void AddNodeT value... Basi
Trang 1This code displays the following:
Key: 0 Value: zero :
Key: 1 Value: one :
Key: 2 Value: two :
Key: 3 Value: three : duplicate three : duplicate three :
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)
Trang 2Key: 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 calledmapthat containsthe actual mapping of keysto values Several
of theMultiMap<T,U>methodsare delegated to the methodson themap Dictionary<T,List<U>> object.
ADictionary<T,U> operates on a one-to-one principle: only one key may be 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 theMultiMap<T,U>class The privatemapfield associates a key with a singleList<U>of values, which allows multi- ple mappingsof valuesto a single key and mappingsof a single value to multiple keys As an added feature, a key can also be mapped to anull value.
associ-Here’s what happens when key-value pairs are added to aMultiMap<t,U> object:
1 The MultiMap<T,U>.Add method iscalled with a key and value provided as parameters.
2 The Add method checksto see whether key exists in the map Dictionary<T,List<U>> object.
3 If keydoesnot exist, it isadded asa key in the map Dictionary<T, List<U>>
object This key is associated with a newList<U>as the value associated withkey
in thisHashtable.
4 If the key doesexist, thekeyislooked up in the map Dictionary<T, List<U>>
object, and thevalue is added to thekey’sList<U>.
Trang 3To remove a key using the Remove method, the key and List<U> pair are removed from the mapDictionary<T, List<U>> This allows removal of all values associated with a single key The MultiMap<T,U>.Remove method callsthe RemoveSingleMap
method, which encapsulates this behavior Removal of key “0”, and all values mapped to this key, is performed with the following code:
{
Console.Write("Key: " + entry.Key.ToString( ) + "\tValue: ");
foreach (string str in myMap[entry.Key])
Trang 4Key: 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 methodsthat allow searching of the MultiMap<T,U>object areContainsKeyand
ContainsValue The ContainsKey method searches for the specified key in the mapDictionary<T, List<U>> TheContainsValuemethod searches for the specified value
in a List<U> in the map Dictionary<T, List<U>> Both methodsreturn trueif the key-value was found orfalse 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 theContainsKey andContainsValue methods are both case-sensitive.
Solution
To implement a binary tree of the type described in the Problem statement, each node must be an object that inherits from theIComparable<T>interface Thismeans that every node to be included in the binary tree must implement the CompareTo
method Thismethod will allow one node to determine whether it islessthan, greater than, or equal to another node.
Use theBinaryTree<T>class shown in Example 11-6, which contains all of the nodes
in a binary tree and lets you traverse it.
Trang 5Example 11-6 Generic BinaryTree class
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)
Trang 6TheBinaryTreeNode<T> shown in Example 11-7 encapsulates the data and behavior
of a single node in the binary tree.
Example 11-7 Generic BinaryTreeNode class
public class BinaryTreeNode<T>
private T nodeValue = default(T);
private BinaryTreeNode<T> leftNode = null; // leftNode.nodeValue < Value
private BinaryTreeNode<T> rightNode = null; // rightNode.nodeValue >= Value
public int Children
Trang 7bool isUnique = true;
Example 11-7 Generic BinaryTreeNode class (continued)
Trang 8Example 11-7 Generic BinaryTreeNode class (continued)
Trang 9public void RemoveLeftNode( )
Example 11-7 Generic BinaryTreeNode class (continued)
Trang 10The methods defined in Table 11-3 are of particular interest to using aBinaryTree<T>
wherevalue 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)
wherevalue is the object to be added andid is the node index Use this method ifthe tree will be flattened
AddNode method Adds a node to the tree Its syntax is:
AddNode(T value)
wherevalue is the object to be added Use this method if the tree will not be tened
flat-SearchDepthFirst method Searches for and returns aBinaryTreeNode<T> object in the tree, if one exists
This method searches the depth of the tree first Its syntax is:
SearchDepthFirst(T value)
wherevalue 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 theBinaryTreeNode<T> object that is the root of the tree Its syntax is:
RootTreeSize 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)
Trang 11The 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
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 theIComparable<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)
wherenodeis the node to be added Duplicate nodes may be added using this method
AddUniqueNode method Adds a new node recursively to either the left side or the right side Its syntax is:
AddUniqueNode(BinaryTreeNode<T> node)
wherenode is the node to be added Duplicate nodes may not be added using thismethod 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 aBinaryTreeNode<T>object in the tree, if one exists This
method searches the depth of the tree first Its syntax is:
DepthFirstSearch(T targetObj)
wheretargetObj 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( )
Trang 12The output for this method is shown here:
Contains Left: NULL
Contains Right: NULL
d
Contains Left: a
Contains Right: f
e
Contains Left: NULL
Contains Right: NULL
Trang 13f
Contains Left: e
Contains Right: g
g
Contains Left: NULL
Contains Right: NULL
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
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
Trang 14Trees are data structures in which each node has exactly one parent and possibly
many children The root of the tree isa single node that branchesout into one or
more child nodes A node isthe part of the tree structure that containsdata and tains the branches (or in more concrete terms, references) to its children node(s).
con-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 nodesfor every node in the tree A binary tree node can never have more than two child nodes; thisiswhere thistype of tree getsitsname (There
are other types of trees For instance, the n-ary tree can have zero to n nodesfor 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 treesthan the one implemented here For example, if you need to store key/value pairs, you can look at using theSortedListor theSortedDictionaryclasses 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 Isthe object to be added to the tree lessthan (less than isdetermined 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 containsan object or value greater than (or equal to, if the binary tree can con- tain duplicates) the current node.
Trang 15Searching 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 lessthan the current node’sobject?” If it is, follow the left branch to the next node in the tree If it isstill 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 TheBinaryTree<T>class isnot a part of the actual tree; rather, it actsasa 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>, isthe heart of the binary tree and representsa single node in the tree This class contains all the members that are required to cre- ate and work with a binary tree.
TheBinaryTreeNode<T>class contains a protected field,nodeValue, which containsan object implementing theIComparable<T>interface Thisstructure allowsyou to per- form searches and add nodes in the correct location in the tree The CompareTo
method of theIComparable<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 graphs—to see this in action.
para-There are two methodsto add nodesto the tree, AddNode and AddUniqueNode The
AddNode method allowsduplicatesto be introduced to the tree, whereasthe
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 returnsnull.
It isinteresting to note that even though theBinaryTree<T>class is provided to ate and manage the tree of BinaryTreeNode<T> objects, you can merely use the
cre-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");
Trang 16The 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
Trang 17b
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
Contains Left: NULL
Contains Right: NULL
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
Trang 18// 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)
Trang 19The methods defined in Table 11-5 are of particular interest to using an NTree<T>
object.
TheNTreeNodeFactory<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
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)
wherenode 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>
private int maxChildren = 0;
public int MaxChildren
// Nested Node class
public class NTreeNode<U>
where U : IComparable<U>
{
Trang 20public NTreeNode(U value, int maxChildren)
protected U nodeValue = default(U);
protected NTreeNode<U>[] childNodes = null;
public int CountChildren
public NTreeNode<U>[] Children
Example 11-11 Using the class to create the nodes for an n-ary tree (continued)
Trang 21{
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)
Trang 22public NTreeNode<U> BreadthFirstSearch(U targetObj)
// Get next node in queue
NTreeNode<U> currentNode = row.Dequeue( );
// Is this the node we are looking for?
if (targetObj.CompareTo(currentNode.nodeValue) == 0) {
Trang 23The methods defined in Table 11-6 are of particular interest to using an
List<U> tempList = new List<U>( );
for (int index = 0; index < childNodes.Length; index++)
throw (new ArgumentOutOfRangeException("index", index,
"Array index out of bounds."));
Trang 24The 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
Constructor Creates a newNTreeNodeFactory<T> object that will createNTreeNode<U> objects
with the same number ofMaxChildren that theNTree<T> object passed in supports Itssyntax is:
NTreeNodeFactory(NTree<T> root)
whereroot is anNTree<T> object
MaxChildren property Read-only property that returns the maximum number of children that theNTree<T> object
supports Its syntax is:
int MaxChildren {get;}
CreateNode method Overloaded method that returns a newNTreeNode object Its syntax is:
CreateNode( )
CreateNode(IComparable value)
wherevalue is theIComparable object this new node object will contain
Table 11-7 Members of the NTreeNode<U> class
Constructor Creates a newNTreeNode<U> object from theNTreeNodeFactory<T>
object passed in to it Its syntax is:
NTreeNode(T value, int maxChildren)
wherevalue is anIComparable<T> object andmaxChildren is thetotal number of children allowed by this node
NumOfChildren property Read-only property that returns the total number of children below 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:
CountChildrenCountImmediateChildren property Counts only the non-null child nodes contained in the current node Its syn-
tax is:
CountImmediateChildrenGetChild method Uses an index to return theNTreeNode<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( )
Trang 25The code shown in Example 11-12 illustrates the use of the NTree<T>, NodeFactory<T>, andNTreeNode<U> classes to create and manipulate an n-ary tree.
NTree-AddNode method Adds a new child node to the current node Its syntax is:
AddNode(NTreeNode<U> node)
wherenode is the child node to be added
DepthFirstSearch method Attempts to locate anNTreeNode<U> by theIComparable<T> object
that it contains AnNTreeNode<U> is returned if theIComparable<T>
object is located or anull if it is not Its syntax is:
DepthFirstSearch(IComparable<T> targetObj)
wheretargetObj is theIComparable<T> object to locate in the tree.Note that this search starts with the current node, which may or may not bethe root of the tree The tree traversal is done in a depth-first manner
BreadthFirstSearch method Attempts to locate anNTreeNode<U> by theIComparable<T> object
that it contains AnNTreeNode<U> is returned if theIComparable<T>
object is located or anull if it is not Its syntax is:
BreadthFirstSearch(IComparable<T> targetObj)
wheretargetObj is theIComparable<T> object to locate in the tree.Note that this search starts with the current node, which may or may not bethe 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 specifiedindex on the current node Its
syn-tax is:
RemoveNode(int index)
whereindex is the array index where the child object is stored Note thatwhen 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)
Trang 26The output for this method is shown here:
topLevel.GetRoot().DepthFirstSearch("Four").Value( ).ToString( )); Console.WriteLine("topLevel.DepthFirstSearch(Five): " +
topLevel.GetRoot().DepthFirstSearch("Five").Value( ).ToString( )); Console.WriteLine("\r\n\r\nBreadth First Search:");
Console.WriteLine("topLevel.BreadthFirstSearch(One): " +
topLevel.GetRoot().BreadthFirstSearch("One").Value( ).ToString( )); Console.WriteLine("topLevel.BreadthFirstSearch(Two): " +
topLevel.GetRoot().BreadthFirstSearch("Two").Value( ).ToString( )); Console.WriteLine("topLevel.BreadthFirstSearch(Three): " +
topLevel.GetRoot().BreadthFirstSearch("Three").Value( ).ToString( )); Console.WriteLine("topLevel.BreadthFirstSearch(Four): " +
topLevel.GetRoot().BreadthFirstSearch("Four").Value( ).ToString( ));}
Example 11-12 Using the NTree<T>, NTreeNodeFactory<T>, and NTreeNode<U> classes
Trang 28Breadth First Search:
An n-ary tree isone that hasno limitation on the number of children each parent
node may contain This is in contrast to the binary search tree in Recipe 11.4, in which each parent node may contain only two children nodes.
NTree<T>is a simple class that contains only a constructor and three public methods.
Through thisobject, you can create an n-ary tree, set the root node, and obtain the
root node in order to navigate and manipulate the tree AnNTree<T>object that can contain at most three children is created in the following manner:
NTree<string> topLevel = new NTree<string>(3);
An NTree<T> object that can contain at most int.MaxValue children, which allows greater flexibility, is created in the following manner:
NTree<string> topLevel = new NTree<string>( );
The real work isdone in theNTreeNodeFactory<T>object and theNTreeNode<U>object, which isnested in theNTreeNodeFactory<T>class TheNTreeNodeFactory<T>class is an object factory that facilitatesthe construction of allNTreeNode<U>objects When the factory object iscreated, theNTree<T>object is passed in to the constructor, as shown here:
NTreeNodeFactory<string> nodeFactory = new NTreeNodeFactory<string>
(topLevel);
Therefore, when the factory object iscreated, it knowsthe maximum number of dren that a parent node may have The factory object providesa public method,
chil-CreateNode, that allowsfor the creation of an NTreeNode<U> object If an
IComparable<T>type object is passed into this method, theIComparable<T>object will
be contained within thisnew node in thenodeValuefield If anullis passed in, the newNTreeNode<U>object will contain the objectUwith it initialized using the default keyword The String object can be passed in to this parameter with no modifica- tions Node creation is performed in the following manner:
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");
NTreeNode<string> nine = nodeFactory.CreateNode("Nine");
Trang 29The NTreeNode<U> class is nested within the factory class; it is not supposed to be used directly to create a node object Instead, the factory will create a node object and return it to the caller.NTreeNode<U>hasone constructor that acceptstwo param- eters: value, which isan object of typeUused to store an object implementing the
IComparable<T>interface; and an integer value,maxChildren, which isused to define the total number of child nodesallowed It isthenodeValuefield that you use when you are searching the tree for a particular item.
Adding a root node to theTopLevel NTree<T>object isperformed using theAddRoot
method of theNTree<T> object:
topLevel.AddRoot(one);
Each NTreeNode<U> object containsa field called childNodes Thisfield isan array containing all child nodesattached to thisparent node object The maximum num- ber of children—obtained from the factory class—provides this number, which is used to create the fixed-size array This array is initialized in the constructor of the
NTreeNode<U> object.
The following code shows how to add nodes to this tree:
// Add nodes to root
to the end of the method.
If theRemoveNodemethod is successful, the array containing all child nodes of the rent node iscompacted to prevent fragmentation, which allowsnodesto be added later in a much simpler manner TheAddNodemethod only hasto add the child node
cur-to the end of this array as opposed cur-to searching the array for an open element The following code shows how to remove a node:
// Remove all nodes below node 'Two'
// Nodes 'Five' and 'Six' are removed
topLevel.GetRoot( ).BreadthFirstSearch("Two").RemoveNode(0);
Trang 30// Remove node 'Three' from the root node.
• Union of the items contained by the two container objects
• Intersection of the items contained by the two container objects
• Difference of the items contained by the two container objects
Solution
Use the built inHashSet<T> object.
The methods defined in Table 11-8 are of particular interest to using a HashSet<T>
whereobj is the object of type T to add to thisHashSet
Remove method Removes an existing object from the currentHashSet<T> object Its syntax is:
Remove(T obj)
whereobj is the object of type T to remove from thisHashSet
RemoveWhere method Removes an existing object from the currentHashSet<T> object Its syntax is:
RemoveWhere(Predicate<T> match)
wherematch is the condition in which must be satisfied in order to remove an item
or items from thisHashSet
Trang 31Contains method Returns a Boolean indicating whether the object passed in exists within this
HashSet<T>object If a true is returned, the object exists; otherwise, it does not Itssyntax is:
Contains(T obj)
whereobj is the object of typeT to be searched for
UnionWith method Performs a union operation on the currentHashSet<T> object and a second
HashSet<T> object The currentHashSet<T> object is modified to contain theunion of these twoHashSet<T> objects Its syntax is:
UnionWith(HashSet<T> set)
whereset is the secondHashSet<T> object
IntersectionWith method Performs an intersection operation on the currentHashSet<T>object and a second
HashSet<T> object The currentHashSet<T> object is modified to contain theintersection of these twoHashSet<T> objects Its syntax is:
IntersectionWith(HashSet<T> set)
whereset is the secondHashSet<T> object
ExceptWith method Removes all elements in the passed inHashSet<T> object from theHashSet<T>
object on which this method was called The currentHashSet<T>object is modified
to contain the result of this operation Its syntax is:
ExceptWith(HashSet<T> set)
whereset is the secondHashSet<T> object
SymmetricExceptWith method Performs a difference operation on the currentHashSet<T> object and a second
HashSet<T> object The currentHashSet<T> object is modified to contain thedifference of these twoHashSet<T> objects Its syntax is:
SymmetricExceptWith(HashSet<T> set)
whereset is the secondHashSet<T> object
SetEquals method Returns a Boolean indicating whether a secondHashSet<T> object is equal to the
currentHashSet<T> object Its syntax is:
SetEquals(HashSet<T> set)
whereset is the secondHashSet<T> object
IsSubsetOf method Returns a Boolean indicating whether the currentHashSet<T>object is a subset of
a secondHashSet<T> object Its syntax is:
IsSubsetOf(Set<T> set)
whereset is the secondHashSet<T> object
IsSupersetOf method Returns a Boolean indicating whether the currentHashSet<T> object is a superset
of a secondHashSet<T> object Its syntax is:
IsSupersetOf(HashSet<T> set)
whereset is the secondHashSet<T> object
IsProperSubsetOf method Returns a Boolean indicating whether the currentHashSet<T> object is a proper
subset of a secondHashSet<T> object Its syntax is:
IsProperSubsetOf(Set<T> set)
whereset is the secondHashSet<T> object
Table 11-8 Members of the HashSet<T> class (continued)
Trang 32Sets are containersthat hold a group of homogeneousobject types
Variousmathe-matical operations can be performed on sets, including the following:
Union
(A ∪ B)
Combinesall elementsof setAand setBinto a resultingHashSet<T>object If an object exists in both sets, the resulting unionedHashSet<T>object containsonly one of those elements, not both.
Intersection
(A ∩ B)
Combinesall elementsof setAand setBthat are common to bothAandBinto a resultingHashSet<T>object If an object exists in one set and not the other, the element is not added to the intersectionedHashSet<T> object.
Difference
(A–B)
Combinesall elementsof setA, except for the elementsthat are also membersof setB, into a resultingHashSet<T>object If an object exists in both setsAandB, it isnot added to the final differencedHashSet<T>object The difference isequiva- lent to taking the union of both sets and the intersection of both sets and then removing all elements in the unioned set that exist in the intersectioned set.
Subset
(A ⊂ B)
Returnstrueif all elementsof setAare contained in a second setB; otherwise, it returnsfalse SetB may contain elements not found inA.
IsProperSupersetOf method Returns a Boolean indicating whether the currentHashSet<T> object is a proper
superset of a secondHashSet<T> object Its syntax is:
IsProperSupersetOf(HashSet<T> set)
whereset is the secondHashSet<T> object
Overlaps method Returns a Boolean indicating whether the currentHashSet<T> object overlaps a
secondHashSet<T> object Its syntax is:
Overlaps(HashSet<T> set)
whereset is the secondHashSet<T> object
Table 11-8 Members of the HashSet<T> class (continued)
Trang 33The following code creates and populates twoHashSet<T> objects:
HashSet<int> set1 = new HashSet<int>( );
HashSet<int> set2 = new HashSet<int>( );
The union operation can be performed by using theUnionWithmethod and passing in
a HashSet<T>with which to union the current HashSet<T> Essentially, the resulting set contains elements that exist in either of the two HashSet<T> objectsor both
HashSet<T> objects The following code demonstrates the union operation:
set1.UnionWith(set2);
The intersection operation is set up similarly to the union operation To perform an intersection between twoHashSet<T>objects, use theIntersectWithmethod Essen- tially, an element must be in bothHashSet<T> AandHashSet<T> Bin order for it to be placed in the resultingHashSet<T>object The following code demonstrates the inter- section operation:
set1.IntersectionOf(set2);
The difference operation isperformed through the SymmetricExceptWith method Essentially, only elements in either set, but not both, are placed in the resulting set The following code demonstrates the difference operation:
set1.SymmetricExceptWith(set2);
Trang 34The subset operation is performed only through a single method calledIsSubsetOf The superset operation is also performed using a single method calledIsSupersetOf The following code demonstrates this operation:
bool isSubset = set1.IsSubsetOf(set2);
bool isSuperset = set1.IsSupersetOf(set2);
The equivalence operation isperformed by using theSetEqualsmethod The ing code demonstrates this operation:
bool isEqual = set1.Equals(set2);
See Also
The “HashSet and LINQ Set Operations” topics in the MSDN documentation.
Trang 35• Encoding methods for character data.
• Selecting the correct way (based on usage) to access files via streams.
The second set looks at directory- or folder-based programming tasks such as file ation as well as renaming, deleting, and determining attributes The third set deals with the parsing of paths and the use of temporary files and paths The fourth set deals with more advanced topics in filesystem I/O, such as:
cre-• Asynchronous reads and writes.
• Monitoring for certain file system actions.
• Version information in files.
• Using P/Invoke to perform file I/O.
The file-interactions section comes first since it sets the stage for many of the recipes
in the temporary file and advanced sections This is fundamental knowledge that will help you understand the other file I/O recipes and how to modify them for your pur- poses The various file and directory I/O techniques are used throughout the more advanced examplesto help show a couple of different waysto approach the prob- lems you will encounter working with file system I/O.
Unless otherwise specified, you need the followingusingstatements in any program that uses snippets or methods from this chapter:
Trang 36To display a file’s timestamps, you can use either the static methods of theFileclass
or the instance properties of the FileInfo class The static methods are
GetCreationTime,GetLastAccessTime, andGetLastWriteTime Each hasa single eter, the path and name of the file for which timestamp information is to be returned, and returnsa DateTime value containing the relevant timestamp For example:
public static void DisplayFileTimestamps(string path)
The instance properties of theFileInfo class areCreationTime, LastAccessTime, and
LastWriteTime Each returnsaDateTimevalue containing the respective timestamp of the file represented by the FileInfo object The DisplayFileInfoTimestamps exten- sion method allows you to report those values directly from aFileInfo:
public static void DisplayFileInfoTimestamps(this FileInfo fileInfo)
To modify a file’s timestamps, you can use either the static methods of theFileclass
or the instance properties of the FileInfo class The static methods are
SetCreationTime,SetLastAccessTime, andSetLastWriteTime All of them take the path and name of the file for which the timestamp isto be modified asthe first parameter and aDateTimevalue containing the new timestamp as the second, and each returns
void To set them all at once, use theModifyFileTimestamps method:
public static void ModifyFileTimestamps(string path)
Trang 37The instance properties are the same as the properties used to display timestamp information:CreationTime,LastAccessTime, orLastWriteTime To set the timestamp, assign a value of typeDateTimeto the relevant timestamp property To set all of these properties at once, use theModifyTimestamps extension method forFileInfo:
public static void ModifyTimestamps(this FileInfo fileInfo, DateTime dt)
FileInfo fileInfo = new FileInfo(path);
// Display whether this file is hidden
Console.WriteLine("Is file hidden? = " +
((fileInfo.Attributes & FileAttributes.Hidden) ==
In addition to timestamp information, a file’s attributes may also be obtained and modified This is accomplished through the use of the public instance Attributes
property found on a FileInfo object Thisproperty returnsor modifiesa
FileAttributes enumeration The FileAttributes enumeration ismade up of bit flags that can be turned on or off through the use of the bitwise operators&,|, or^ Table 12-1 lists each of the flags in theFileAttributes enumeration.
Trang 38In many cases, more than one of these flags can be set at one time One case in which thisisnot the case isfor theNormalflag, which must be used alone (see description for more details).
Fileclass or the instance MoveTomethod of theFileInfoclass The staticFile.Move
method can be used to rename a file in the following manner:
Table 12-1 FileAttributes enumeration values
Member name Description
Archive Represents the file’s archive status that marks the file for backup or removal
Compressed Indicates that the file is compressed
Device This option is reserved for future use
Directory Indicates that this is a directory
Encrypted Indicates that a file or directory is encrypted In the case of a file, its contents are encrypted In
the case of a directory, newly created files will be encrypted by default
Hidden Indicates a hidden file
Normal Indicates that the file has no other attributes; as such, this attribute cannot be used in
combina-tion with others
NotContentIndexed Indicates that the file is excluded from the content index service
Offline Indicates that the state of the file is offline and its contents will be unavailable
ReadOnly Indicates that the file is read-only
ReparsePoint Indicates a reparse point, a block of data associated with a directory or file.
SparseFile Indicates a sparse file, which may take up less space on the filesystem than its reported size
because zeros in the file are not actually allocated on disk
System Indicates that the file is a system file
Temporary Indicates a temporary file It may reside entirely in memory
Trang 39public static void RenameFile(string originalName, string newName)
{
File.Move(originalName, newName);
}
This code has the effect of renaming theoriginalName file tonewName.
TheFileInfo.MoveToinstance method can also be used to rename a file, and this can
be exposed directly from the FileInfo instance using an extension method The
Rename extension method gives easy access to rename functionality right from the
Trang 40The NET Framework providestheEnvironment.NewLineconstant, which represents
a newline on the given platform This is the newline string used by all of the work providedWriteLine methods internally (includingConsole,Debug, andTrace) There are a few different scenarios in which this could be useful:
frame-• Formatting a block of text with newlines embedded within it:
// Remember to use Environment.NewLine on every block of text
// we format that we want platform-correct newlines at the end of
string line;
line = String.Format("FirstLine {0} SecondLine {0} ThirdLine {0}",
Environment.NewLine);
// Get a temp file to work with
string file = Path.GetTempFileName( );
using (FileStream stream = File.Create(file))
• You need to use a different newline character than the default one used by
StreamWriter(which happensto beEnvironment.NewLine) You can set the line that a StreamWriterwill use once so that all WriteLinesperformed by the
new-StreamWriter use that newline instead of having to manually do it each time: // Set up a text writer and tell it to use a certain newline
// string
// Get a new temp file
file = Path.GetTempFileName( );
line = "Double spaced line";
using (StreamWriter streamWriter = new StreamWriter(file))
{
// Make this always write out double lines
streamWriter.NewLine = Environment.NewLine + Environment.NewLine;
// WriteLine on this stream will automatically use the newly specified // newline sequence (double newline, in our case)
• NormalWriteLine calls:
// Just use any of the normal WriteLine methods, as they use the
// Environment.NewLine by default