Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 88 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
88
Dung lượng
286,12 KB
Nội dung
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) [...]... the first parameter and a DateTime value containing the new timestamp as the second, and each returns void To set them all at once, use the ModifyFileTimestamps method: public static void ModifyFileTimestamps(string path) { File.SetCreationTime(path, DateTime.Parse(@"May 10, 2003")); File.SetLastAccessTime(path, DateTime.Parse(@"May 10, 2003")); File.SetLastWriteTime(path, DateTime.Parse(@"May 10, 2003"));... looks at typical file interactions like: • Creation • Reading and writing • Deletion • Attributes • 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 creation as well as renaming, deleting, and determining attributes The third set deals with the parsing of paths and... create and manage the tree of BinaryTreeNode objects, you can merely use the BinaryTreeNode 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 class Example 11-9 Creating and managing a binary tree without using the BinaryTree class public static void TestManagedTreeWithNoBinaryTreeClass( )... 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 is a simple class that contains only a constructor and three public methods Through this object, you can create an n-ary tree, set the root node, and obtain the root node in order to navigate and manipulate the tree An NTree object that can contain at... chapter: 449 using System; using System.IO; 12.1 Manipulating File Attributes Problem You need to display or manipulate a file’s attributes or timestamps Solution To display a file’s timestamps, you can use either the static methods of the File class or the instance properties of the FileInfo class The static methods are GetCreationTime, GetLastAccessTime, and GetLastWriteTime Each has a single parameter,... the heart of the binary tree and represents a single node in the tree This class contains all the members that are required to create and work with a binary tree The BinaryTreeNode class contains a protected field, nodeValue, which contains an object implementing the IComparable interface This structure allows you to perform searches and add nodes in the correct location in the tree The CompareTo... false This is equivalent to stating that (A ⊂ B) and (B ⊂ A) The following code creates and populates two HashSet objects: HashSet set1 = new HashSet( ); HashSet set2 = new HashSet( ); set1.Add(1); set1.Add(2); set1.Add(3); set1.Add(4); set1.Add(5); set1.Add(6); set2.Add(-10); set2.Add(2); set2.Add(40); The union operation can be performed by using the UnionWith method and passing... DepthFirstSearch(IComparable targetObj) where targetObj is the IComparable 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 by the IComparable object that it contains An NTreeNode is returned if the IComparable... create a node object and return it to the caller NTreeNode has one constructor that accepts two parameters: value, which is an object of type U used to store an object implementing the IComparable interface; and an integer value, maxChildren, which is used to define the total number of child nodes allowed It is the nodeValue field that you use when you are searching the tree for a particular... otherwise, it returns false Set B may contain elements not found in A 446 | Chapter 11: Data Structures and Algorithms Superset (A ⊃ B) Returns true if all elements of set A are contained in a second set B; otherwise, it returns false Set A may contain elements not found in B Equivalence (A == B) Returns true if both HashSet objects contain the same number of elements and the same value for each element; . 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); . 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. 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,