"Sorting" a Tree Data Structure - c#

I made a Tree Data Structure and I want the Elements to sort like this:
10
/ \
5 12
/ \ / \
3 7 11 18
If the value of the added element is smaller than the value of the other element, it should be linked left, and if bigger, right. My problem is, that I just can't get the sorting method right.
class Tree
{
private class TElement
{
public int _value;
public TElement _left;
public TElement _right;
public TElement(int value)
{
_value = value;
}
}
private TElement RootElement;
public void Add(int value)
{
TElement newElement = new TElement(value);
TElement current = new TElement(value);
current = RootElement;
if (RootElement == null)
{
RootElement = newElement;
return;
}
SortNewElement(RootElement, RootElement, newElement, RootElement);
}
private void SortNewElement(TElement left, TElement right, TElement newElement, TElement RootElement)
{
if (newElement._value < RootElement._value && RootElement._left == null)
{
left._left = newElement;
return;
}
if (newElement._value > RootElement._value && RootElement._right == null)
{
right._right = newElement;
return;
}
if (newElement._value < left._value && left._left == null)
{
left._left = newElement;
return;
}
if (newElement._value > right._value && right._right == null)
{
right._right = newElement;
return;
}
SortNewElement(left._left, right._right, newElement, RootElement);
}
}
I know it doesn't work because it's trying to get the linked nodes of a null element.

From what i can understand from your question you are just trying to insert a new node in a binary search tree. Its inorder traversal will be a sorted array.
You can do so by the following simple pseudo code
insert_new( Node* node, value)
{
if(value > node->value)
{
if(node->right != null)
{
insert_new(node->right,value);
}
else
{
node->right = new Node(value);
return;
}
}
else
{
if(node->left != null)
{
insert_new(node->left,value)
}
else
{
node->left = new Node(value);
return;
}
}
}

class element{
public:
int value;
*element left;
*element right;
element(int value)
value = value;
public add(&element a)
if (a != null)
{
if (left!=null){
left = a;
}
else{
if (left.value > a.value){
right = left;
left= a;
}
else{
right=a;
}
}

Related

Remove function of BST in C#

I am trying to write the remove function for BST in C#. I have some NUnit tests that need to pass. All of them pass except two. I have two scenarios for which the test fails.
I need to remove 10 for these two trees. But it's failing:
100, 10, 5
100, 10, 20
So I'm guessing my code doesn't splice the 10 out. And the error is in the RemoveNonRootNode method. And specifically with the "else if" statements that are looking at 1 child situation.
Here is my code. I appreciate some help!
public class BST
{
private BSTNode m_top;
private class BSTNode
{
private int m_data;
private BSTNode m_left;
private BSTNode m_right;
public int Data
{
get { return m_data; }
set { m_data = value; }
}
public BSTNode Left
{
get { return m_left; }
set { m_left = value; }
}
public BSTNode Right
{
get { return m_right; }
set { m_right = value; }
}
public BSTNode(int data)
{
m_data = data;
}
}
public void AddValue(int v)
{
if (m_top == null)
{
m_top = new BSTNode(v);
}
else
{
BSTNode cur = m_top;
while (true)
{
if (v < cur.Data)
{
if (cur.Left == null)
{
cur.Left = new BSTNode(v);
return;
}
else
cur = cur.Left;
}
else if (v > cur.Data)
{
if (cur.Right == null)
{
cur.Right = new BSTNode(v);
return;
}
else
cur = cur.Right;
}
else
throw new DuplicateValueException("Value " + v + " is already in the tree!");
}
}
}
public void Print()
{
Console.WriteLine("=== Printing the tree ===");
if (m_top == null)
Console.WriteLine("Empty tree!");
else
PrintInternal(m_top);
}
private void PrintInternal(BSTNode cur)
{
if (cur.Left != null)
PrintInternal(cur.Left);
Console.WriteLine(cur.Data);
if (cur.Right != null)
PrintInternal(cur.Right);
}
public bool Find(int target)
{
return FindNode(target) != null;
}
private BSTNode FindNode(int target)
{
BSTNode cur = m_top;
while (cur != null)
{
if (cur.Data == target)
return cur;
else if (target < cur.Data)
cur = cur.Left;
else if (target > cur.Data)
cur = cur.Right;
}
return null;
}
public void Remove(int target)
{
// if the tree is empty:
if (m_top == null)
return;
if (m_top.Data == target)
{
RemoveRootNode(target);
}
else
{
RemoveNonrootNode(target);
}
}
private void RemoveNonrootNode(int target)
{
BSTNode cur = m_top;
BSTNode parent = null;
//First, find the target node that we need to remove
// we'll have the 'parent' reference trail the cur pointer down the tree
// so when we stop, cur is the node to remove, and parent is one above it.
while (cur!= null && cur.Data != target)
{
parent = cur;
if (target > cur.Data)
cur = cur.Right;
else
cur = cur.Left;
}
// Next, we figure out which of the cases we're in
// Case 1: The target node has no children
if (cur.Left == null && cur.Right == null)
{
if (parent.Left==cur)
parent.Left = null;
else
parent.Right = null;
}
// Case 2: The target node has 1 child
// (You may want to split out the left vs. right child thing)
else if (cur.Left == null)
{
BSTNode cur2 = cur;
cur = cur.Right;
cur2 = null;
return;
}
else if (cur.Right == null)
{
BSTNode cur2 = cur;
cur = cur.Right;
cur2 = null;
return;
}
// Case 3: The target node has 2 children
BSTNode removee = FindAndRemoveNextSmallerValue(target, cur);
cur.Data = removee.Data;
return;
}
private void RemoveRootNode(int target)
{
// If we're here, it's because we're removing the top-most node (the 'root' node)
// Case 1: Root has no children
if (m_top.Left == null && m_top.Right == null)
{
m_top = null; // Therefore, the tree is now empty
return;
}
// Case 2: Root has 1 child
else if (m_top.Left == null)
{
// 1 (right) child, OR zero children (right may also be null)
m_top = m_top.Right; // Right is null or another node - either way is correct
return;
}
else if (m_top.Right == null)
{
// If we're here, Left is not null, so there MUST be one (Left) Child
m_top = m_top.Left;
return;
}
// Case 3: Root has two children - this is where it gets interesting :)
else
{
// 2 children - find (and remove) next smaller value
// use that data to overwrite the current data.
BSTNode removee = FindAndRemoveNextSmallerValue(target, m_top);
m_top.Data = removee.Data;
return;
}
}
/// <summary>
/// This method takes 1 step to the left, then walks as far to the right
/// as possible. Once that right-most node is found, it's removed & returned.
/// Note that the node MAY be immediately to the left of the <B>startHere</B>
/// parameter, if startHere.Left.Right == null
/// </summary>
/// <param name="smallerThanThis"></param>
/// <param name="startHere"></param>
/// <returns></returns>
private BSTNode FindAndRemoveNextSmallerValue(int smallerThanThis, BSTNode startHere)
{
BSTNode parent = startHere;
BSTNode child = startHere.Left;
if (child.Data == smallerThanThis)
{
child = null;
}
while (child.Right != null)
{
parent = child;
child = child.Right;
}
parent = child;
child = null;
return parent;
}
// Given the value of a node, find (and remove) the predessor node in the tree
// returns the value of the predecessor node, or Int32.MinValue if no such value was found
public int TestFindAndRemoveNextSmallest(int sourceNode)
{
BSTNode startAt = this.FindNode(sourceNode);
// sourceNode should == startAt.Data, unless startAt is null)
BSTNode removed = FindAndRemoveNextSmallerValue(sourceNode, startAt);
if (removed != null)
return removed.Data;
else
return Int32.MinValue;
}
}
At first sight, there seems to be this bug:
else if (cur.Left == null)
{
BSTNode cur2 = cur;
cur = cur.Right;
cur2 = null;
return;
}
else if (cur.Right == null)
{
BSTNode cur2 = cur;
// cur = cur.Right; // THIS SHOULD BE .Left, because .Right is NULL
cur = cur.Left; // THIS IS THE FIX
cur2 = null;
return;
}
But your actual problem is; updating the cur reference to something else does not change the pointer to the same object (cur) held by its parent. Actually, you did it right in Case 1, but somehow missed it in Case 2. Therefore; the full fix is: (only fixing the failing test. Promising no more).
// Case 2: The target node has 1 child
// (You may want to split out the left vs. right child thing)
else if (cur.Left == null)
{
if (parent.Left == cur)
{
parent.Left = cur.Right;
}
else
{
parent.Right = cur.Right;
}
return;
}
else if (cur.Right == null)
{
if(parent.Left == cur)
{
parent.Left = cur.Left;
}
else
{
parent.Right = cur.Left;
}
return;
}

Printing Tree and skipping certain values

here i have the following code and input that prints a tree structure. My question is how can i make it so that the nodes and leafs that have the value "Unavailable" are skipped from being printed.
namespace Tree{public class TreeNode<T>
{
private T value;
private bool hasParent;
private List<TreeNode<T>> children;
public TreeNode(T value)
{
if (value == null)
{
throw new ArgumentNullException("Cannot insert null value");
}
this.value = value;
this.children = new List<TreeNode<T>>();
}
public T Value
{
get
{
return this.value;
}
set
{
this.value = value;
}
}
public int ChildrenCount
{
get
{
return this.children.Count;
}
}
public void AddChild(TreeNode<T> child)
{
if (child == null)
{
throw new ArgumentNullException("Cannot insert null value");
}
if (child.hasParent)
{
throw new ArgumentException("The node already has a parent");
}
child.hasParent = true;
this.children.Add(child);
}
public TreeNode<T> GetChild(int index)
{
return this.children[index];
}
}
public class Tree<T>
{
private TreeNode<T> root;
public Tree(T value)
{
if (value == null)
{
throw new ArgumentNullException("Cannot insert null value");
}
this.root = new TreeNode<T>(value);
}
public Tree(T value, params Tree<T>[] children) : this(value)
{
foreach (Tree<T> child in children)
{
this.root.AddChild(child.root);
}
}
public TreeNode<T> Root
{
get
{
return this.root;
}
}
private void PrintDFS(TreeNode<T> root, string spaces)
{
if (this.root == null)
{
return;
}
Console.WriteLine(spaces + root.Value);
TreeNode<T> child = null;
for (int i = 0; i < root.ChildrenCount; i++)
{
child = root.GetChild(i);
PrintDFS(child, spaces + " ");
}
}
public void TraverseDFS()
{
this.PrintDFS(this.root, string.Empty);
}
}
public static class TreeExample
{
static void Main()
{
Tree<string> tree =
new Tree<string>("John",
new Tree<string>("Jasmine",
new Tree<string>("Jay"),
new Tree<string>("Unavailable")),
new Tree<string>("Unavailable",
new Tree<string>("Jack"),
new Tree<string>("Jeremy")),
new Tree<string>("Johanna")
);
tree.TraverseDFS();
}
}}
right now it prints :(John, (Jasmine, (Jay), (Unavailable)), (Unavailable, (Jack, (Jeremy))), (Johanna))
I need it to print :(John, (Jasmine, (Jay)), (Johanna))
So basically skip every leaf with the value "Unavailable" and every node with the value "Unavailable" and all children from that node
Thanks !
This should work:
private void PrintDFS(TreeNode<T> root, string spaces)
{
if (this.root == null
|| "Unavailable" == root.Value.ToString())
{
return;
}
...
The accepted answer is a literally correct answer to the question, but it bakes in logic about what to do with the tree into the tree itself. A tree is a kind of collection or data structure, and you don't often see a List or Dictionary that is able to print itself. Instead the collection provides the right methods to get or change its contents so that you can do what you want.
In your case, you could do something like the following:
public enum TreeVisitorResult {
SkipNode,
Continue
}
// the following two methods inside Tree<T>:
public void VisitNodes(Func<TreeNode<T>, int, TreeVisitorResult> visitor) {
VisitNodes(0, this.root, visitor);
}
private void VisitNodes(int depth, TreeNode<T> node,
Func<TreeNode<T>, int, TreeVisitorResult> visitor) {
if (node == null) {
return;
}
var shouldSkip = visitor(node, depth);
if (shouldSkip == TreeVisitorResult.SkipNode) {
return;
}
TreeNode<T> child = null;
for (int i = 0; i < node.ChildrenCount; i++) {
child = node.GetChild(i);
VisitNodes(depth + 1, child, visitor);
}
}
If you had this method, you could write the Print method outside of the Tree classes, as:
tree.VisitNodes((treeNode, depth) => { // <- this lambda will be called for every node
if (treeNode.Value == "Unavailable") { // <- no need to ToString or cast here, since
// we know that T is string here
return TreeVisitorResult.SkipNode;
} else {
var spaces = new string(' ', depth * 3);
Console.WriteLine(spaces + treeNode.Value);
}
});

Why is my if statement in my contains method unreachable? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 4 years ago.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Improve this question
When I run my program my 'if' statement is compiled as unreachable code which in turn, causes my contains method to continuously print false even though the number exists within the tree. I can't figure out why could any one help me please? I'm fairly new to programming.
Here is my node class.
class Node<T> where T : IComparable
{
private T data;
private int balanceFactor = 0; //added for AVLTree
public Node<T> Left, Right;
public Node(T item)
{
data = item;
Left = null;
Right = null;
}
public T Data
{
set { data = value; }
get { return data; }
}
}
Here is my binary search tree class.
class BSTree<T> : BinTree<T> where T : IComparable
{ //root declared as protected in Parent Class – Binary Tree
public BSTree()
{
root = null;
}
public void InsertItem(T item)
{
insertItem(item, ref root);
}
private void insertItem(T item, ref Node<T> tree)
{
if (tree == null)
tree = new Node<T>(item);
else if (item.CompareTo(tree.Data) < 0)
insertItem(item, ref tree.Left);
else if (item.CompareTo(tree.Data) > 0)
insertItem(item, ref tree.Right);
}
public int Height()
//Return the max level of the tree
{
return Height(root);
}
protected int Height(Node<T> current)
{
if (current == null) return 0;
return 1 + Math.Max(Height(current.Left), Height(current.Right));
}
public int Count()
//Return the number of nodes in the tree
{
return Height(root);
}
public int Count(ref Node<T> current)
//Return the number of nodes in the tree
{
int counter = 0;
if (current == null)
{
return 0;
}
else if (current.Left != null)
{
counter += Count(ref current.Left);
counter++;
}
if (current.Right != null)
{
counter += Count(ref current.Right);
counter++;
}
return counter;
}
public Boolean Contains(T item)
//Return true if the item is contained in the BSTree, false //otherwise.
{
Node<T> current = root;
if (current.Data.CompareTo(item) == 0)
{
{
return true;
}
if (current.Data.CompareTo(item) > 0)
{
current = current.Left;
}
if (current.Data.CompareTo(item) < 0)
{
current = current.Right;
}
}
return false;
}
private T leastItem(Node<T> tree)
{
if (tree.Left == null)
return tree.Data;
else
return leastItem(tree.Left);
}
}
Lastly my main class.
class Program
{
static void Main(string[] args)
{
BSTree<int> treeBranch = new BSTree<int>();
treeBranch.InsertItem(77);
treeBranch.InsertItem(20);
treeBranch.InsertItem(37);
treeBranch.InsertItem(15);
treeBranch.InsertItem(22);
treeBranch.InsertItem(30);
Console.WriteLine(treeBranch.Count());
Console.WriteLine(treeBranch.Height());
Console.WriteLine(treeBranch.Contains(15));
string InOrder = "in order :";
treeBranch.InOrder(ref InOrder);
Console.WriteLine(InOrder);
Console.WriteLine("\n");
Console.ReadKey();
}
}
if (current.Data.CompareTo(item) == 0)
{
{ <==== here
return true;
}
if (current.Data.CompareTo(item) > 0)
{
current = current.Left;
}
if (current.Data.CompareTo(item) < 0)
{
current = current.Right;
}
}
There's no condition to return true;. Change your code to this:
if (current.Data.CompareTo(item) == 0)
{
return true;
}
else if (current.Data.CompareTo(item) > 0)
{
current = current.Left;
}
else if (current.Data.CompareTo(item) < 0)
{
current = current.Right;
}
To many { and } in this code
if (current.Data.CompareTo(item) == 0)
{
{
return true;
}

C# - Binary Search Tree Contains/Is Present Method

I'm really struggling to get this method to work, I was wondering if you could help me out. I've been using the ref keyword, so I'll keep using it. I've been searching the web and it's been some help, but I've tried everything I can possibly think of. Both my count and height methods work, however, I'm just really struggling to get this Contain method to work. Many examples on the web have showed both a public and private method of contains (I understand as to why) but I'm sure it could be done within one method? Surely, right? Also, please ignore the RemoveItem method, unless you wish to give me a head-start, that's at your discretion. I know it's tricky as I've looked up on it earlier during the week.
Node class-
class Node<T> where T : IComparable
{
private T data;
public Node<T> Left, Right;
public Node(T item)
{
data = item;
Left = null;
Right = null;
}
public T Data
{
set { data = value; }
get { return data; }
}
}
BinTree Class-
class BinTree<T> where T : IComparable
{
protected Node<T> root;
public BinTree() //creates an empty tree
{
root = null;
}
public BinTree(Node<T> node) //creates a tree with node as the root
{
root = node;
}
//I've deleted my preOrder, inOrder and postOrder methods just to save you some time
}
BSTree Class-
class BSTree<T> : BinTree<T> where T : IComparable
{
public BSTree()
{
root = null;
}
private void insertItem(T item, ref Node<T> tree)
{
if (tree == null)
{
tree = new Node<T>(item);
}
else if (item.CompareTo(tree.Data) < 0)
{
insertItem(item, ref tree.Left);
}
else if (item.CompareTo(tree.Data) > 0)
insertItem(item, ref tree.Right);
}
public void InsertItem(T item)
{
insertItem(item, ref root);
}
public int Height(ref Node<T> tree)
//Return the max level of the tree
{
if (tree == null)
return 0;
return (1 + Math.Max(Height(ref tree.Left), Height(ref tree.Right)));
}
public int Count(ref Node<T> tree)
//Return the number of nodes in the tree
{
int counter = 0;
if (tree == null)
{
return 0;
}
else if (tree.Left != null)
{
counter += Count(ref tree.Left);
counter++;
}
if (tree.Right != null)
{
counter += Count(ref tree.Right);
counter++;
}
return counter;
}
public Boolean Contains(T item, ref Node<T> tree)
//Return true if the item is contained in the BSTree, false //otherwise.
{
if (tree == null)
{
return false;
}
if (item.CompareTo(tree.Data) < 0)
{
return Contains(ref tree.Left);
if (item.CompareTo(tree.Data) > 0)
{
return Contains(ref tree.Right);
return true;
}
}
}
public void RemoveItem(T item)
{
}
}
Thank you in advance.
To check if a node is in the tree, you have a few options:
The node you want is in the left sub-tree
The node you want is in the right sub-tree
The node you want is where you are right now
So your Contains method should look more like this:
public Boolean Contains(T item, ref Node<T> tree)
{
if (tree == null)
{
return false;
}
if (tree.data == item)
{
return true;
}
if (item.CompareTo(tree.Data) < 0)
{
return Contains(item, ref tree.Left);
}
if (item.CompareTo(tree.Data) > 0)
{
return Contains(item, ref tree.Right);
}
}
public static bool Contains(Node root, int value)
{
if(root == null)
return false;
if(root.Value == value)
return true;
if(value < root.Value)
return Contains( root.Left,value);
else
return Contains( root.Right,value);
}

How would one bind a SortedSet in WPF?

I am building a WPF Application which contains three ListBox elements bound to either an ISet, SortedSet or custom class (not sure). I came to this conclusion based on former Java experience and because I require the following constraints:
Needs to sort on add, keeping performance in mind
No duplicates should be included
After visiting MSDN to see which interfaces ObservableCollection uses, I figured I would do something like this:
public class ObservableSortedSet<T> : SortedSet<T>, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public override bool Add(T item)
{
bool result = base.Add(item);
OnCollectionChanged(NotifyCollectionChangedAction.Add);
return true;
}
private void OnCollectionChanged(NotifyCollectionChangedAction action)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(action));
}
}
The ObservableCollection class in .NET inspired me to make an ObservableSortedSet, but apparently in C# you use virtual, override, and new keywords, so since SortedSet doesn't have a virtual add method, I cannot do it like above. So my question now is how would I make a SortedSet or what .NET class would provide the features I need? I suppose I could create my own SortedSet class, but that seems like something that should be in the .NET framework.
UPDATE
If anyone wants something like I described above, I went ahead and made one. I have been using it in my project for a while and it seems quite stable. I took a linked list implementation I made a long time ago and tailored it to work as a sorted set. This allows me to preform sorts fast.
Here's the code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
namespace Conquest.Collections {
public class OrderedSet<T> : IEnumerable<T>, INotifyCollectionChanged where T : IComparable<T> {
private Node<T> head;
private Node<T> tail;
public OrderedSet() {
Count = 0;
}
public OrderedSet(IEnumerable<T> enumerable) {
Count = 0;
foreach (T element in enumerable)
Add(element);
}
public T First {
get { return (head == null) ? default(T) : head.Datum; }
}
public T Last {
get { return (tail == null) ? default(T) : tail.Datum; }
}
public int Count { get; private set; }
public IEnumerator<T> GetEnumerator() {
return new OrderedSetEnumerator<T>(this);
}
IEnumerator IEnumerable.GetEnumerator() {
return new OrderedSetEnumerator<T>(this);
}
private void InsertNodeBefore(Node<T> insertingNode, Node<T> here) {
insertingNode.Next = here;
insertingNode.Previous = here.Previous;
if (here == head)
head = insertingNode;
else
here.Previous.Next = insertingNode;
here.Previous = insertingNode;
insertingNode.Index = here.Index;
for (Node<T> n = here; n != null; n = n.Next)
n.Index++;
}
private void InsertNodeAfter(Node<T> insertNode, Node<T> here) {
insertNode.Previous = here;
insertNode.Next = here.Next;
if (here == tail)
tail = insertNode;
else
here.Next.Previous = insertNode;
here.Next = insertNode;
insertNode.Index = here.Index + 1;
for (Node<T> n = insertNode.Next; n != null; n = n.Next)
n.Index++;
}
private bool IsNodeSorted(Node<T> node) {
if (Count == 1)
return true;
return (node == head && node.Next.Datum.CompareTo(node.Datum) > 0) ||
(node == tail && node.Previous.Datum.CompareTo(node.Datum) < 0) ||
(node != tail && node != head && node.Next.Datum.CompareTo(node.Datum) > 0 &&
node.Previous.Datum.CompareTo(node.Datum) < 0);
}
private void RemoveNode(Node<T> node) {
if (node == head)
head = node.Next;
else
node.Previous.Next = node.Next;
if (node == tail)
tail = node.Previous;
else
node.Next.Previous = node.Previous;
for (Node<T> n = node.Next; n != null; n = n.Next)
n.Index--;
Count--;
}
private void SortNodeLeft(Node<T> node) {
// Unlink
RemoveNode(node);
Count++;
for (Node<T> currentNode = node.Previous; currentNode != null; currentNode = currentNode.Previous) {
int compareResult = currentNode.Datum.CompareTo(node.Datum);
if (compareResult < 0) {
// Link
InsertNodeAfter(node, currentNode);
return;
}
}
InsertNodeBefore(node, head);
}
private void SortNodeRight(Node<T> node) {
// Unlink
RemoveNode(node);
Count++;
for (Node<T> currentNode = node.Next; currentNode != null; currentNode = currentNode.Next) {
int compareResult = currentNode.Datum.CompareTo(node.Datum);
if (compareResult > 0) {
// Link
InsertNodeBefore(node, currentNode);
return;
}
}
InsertNodeAfter(node, tail);
}
public bool Add(T item) {
var node = new Node<T>(item);
int index = 0;
if (head == null) {
head = node;
tail = head;
}
else {
for (Node<T> currentNode = head; currentNode != null; currentNode = currentNode.Next, index++) {
int compareResult = currentNode.Datum.CompareTo(item);
if (compareResult == 0)
return false;
else if (compareResult > 0) {
InsertNodeBefore(node, currentNode);
break;
}
// if so, make node the new tail
else if (currentNode == tail) {
InsertNodeAfter(node, tail);
index++;
break;
}
}
}
Count++;
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
node.Index = index;
node.PropertyChanged += Sort;
return true;
}
/// <summary>
/// If the nodes datum changes state, this function ensures
/// the set remains sorted.
/// </summary>
/// <param name="sender">The node</param>
/// <param name="e">The property that changed</param>
private void Sort(object sender, PropertyChangedEventArgs e) {
var node = (Node<T>) sender;
int index = node.Index;
// Check if node is still in correct spot
if (IsNodeSorted(node))
return;
if (node.Previous == null || (node.Previous.Datum.CompareTo(node.Datum) < 0))
SortNodeRight(node);
else
SortNodeLeft(node);
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, node.Datum,
node.Index, index));
}
public bool Remove(T item) {
if (head == null)
return false;
for (Node<T> node = head; node != null; node = node.Next) {
if (node.Datum.CompareTo(item) == 0) {
RemoveNode(node);
NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,
item, node.Index));
return true;
}
}
return false;
}
#region INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs args) {
if (CollectionChanged != null)
CollectionChanged(this, args);
}
#endregion
#region Enumerator
private class OrderedSetEnumerator<U> : IEnumerator<U> where U : IComparable<U> {
private readonly OrderedSet<U> set;
private OrderedSet<U>.Node<U> current;
public OrderedSetEnumerator(OrderedSet<U> set) {
this.set = set;
current = null;
}
public U Current {
get { return (current == null) ? default(U) : current.Datum; }
}
Object IEnumerator.Current {
get { return (current == null) ? null : (Object) current.Datum; }
}
public bool MoveNext() {
current = current == null ? set.head : current.Next;
return (current != null);
}
public void Reset() {
current = null;
}
public void Dispose() {
current = null;
}
}
#endregion
private class Node<U> : INotifyPropertyChanged {
public Node(U datum) {
Datum = datum;
var flagObserveChanges = datum as INotifyPropertyChanged;
if (flagObserveChanges != null)
flagObserveChanges.PropertyChanged += NotifySet;
}
public Node<U> Next { get; set; }
public Node<U> Previous { get; set; }
public int Index { get; set; }
public U Datum { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifySet(object sender, PropertyChangedEventArgs e) {
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
}
}
And of course I made some test cases, simple ones, so you might want to expand :)
using System.Linq;
using Conquest.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.ComponentModel;
using System;
namespace Conquest.Test.Collections
{
[TestClass]
public class OrderedSetTest
{
private class Department : INotifyPropertyChanged, IComparable<Department> {
private string name;
public string Name {
get { return name; }
set {
name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public int CompareTo(Department other) {
return String.Compare(this.Name, other.Name, StringComparison.OrdinalIgnoreCase);
}
}
[TestMethod]
public void TestAddSingleItem()
{
var set = new OrderedSet<int> {1};
Assert.AreEqual(set.First, 1, "Head points to correct location");
Assert.AreEqual(set.Count, 1, "Correct Size");
Assert.AreEqual(set.Last, 1, "Tail points to correct location");
}
[TestMethod]
public void TestAddHead()
{
var set = new OrderedSet<int> {2, 1};
Assert.AreEqual(set.First, 1, "Head points to correct value");
}
[TestMethod]
public void TestAddTail()
{
var set = new OrderedSet<int> {1, 2};
Assert.AreEqual(set.Last, 2, "Tail points to correct value");
Assert.AreEqual(set.Count, 2, "Correct Size");
}
[TestMethod]
public void TestAddDuplicationItems()
{
var set = new OrderedSet<int> {1, 1};
Assert.IsTrue(1 == set.Count);
}
[TestMethod]
public void TestAddMultipleItems()
{
var set = new OrderedSet<int> {3, 2, 4, 1, 5};
int expected = 1;
bool worked = true;
foreach (int i in set)
{
if (i != expected)
{
worked = false;
break;
}
expected++;
}
Assert.IsTrue(worked, "Multiple Items");
}
[TestMethod]
public void TestRemoveSingleItem()
{
var set = new OrderedSet<int> {1};
set.Remove(1);
Assert.AreEqual(set.Count, 0, "Removed single item");
Assert.AreEqual(set.First, 0, "Head does not point to anything");
Assert.AreEqual(set.Last, 0, "Tail does not point to anything");
}
[TestMethod]
public void TestRemoveHead()
{
var set = new OrderedSet<int> {1, 2};
set.Remove(1);
Assert.AreEqual(set.Count, 1, "Removed head with more than one item in set");
Assert.AreEqual(set.First, 2, "Head points to the next value in set");
}
[TestMethod]
public void TestRemoveTail()
{
var set = new OrderedSet<int> {1, 2};
set.Remove(2);
Assert.AreEqual(set.Count, 1, "Removed tail with more than one item in set");
Assert.AreEqual(set.Last, 1, "Tail points to the previous value in set");
}
[TestMethod]
public void TestRemoveItem()
{
var set = new OrderedSet<int> {1, 2, 3};
Assert.IsTrue(set.Remove(2), "Remove correct value");
Assert.IsTrue(set.Count == 2, "Removed item");
int sum = set.Sum();
Assert.AreEqual(4, sum, "Remove correctly relinked adjacent nodes");
}
[TestMethod]
public void TestSortOnAdd()
{
var test = new Queue<int>();
for (int i = 0; i < 10; i++)
test.Enqueue(i);
var set = new OrderedSet<int> {5, 4, 3, 7, 2, 8, 1, 9, 0, 6};
var worked = set.All(i => i == test.Dequeue());
Assert.IsTrue(worked, "Sorted on Add");
}
[TestMethod]
public void TestSortOnEdit()
{
var dep1 = new Department
{
Name = "Test"
};
var dep2 = new Department
{
Name = "Test2"
};
var set = new OrderedSet<Department> {dep1, dep2};
dep2.Name = "Hello";
var e = set.GetEnumerator();
e.MoveNext();
Assert.AreEqual("Hello", e.Current.Name, "Swaped tail to head on edit");
dep1.Name = "Abc";
e = set.GetEnumerator();
e.MoveNext();
Assert.AreEqual("Abc", e.Current.Name, "Verified integrity of node linkage");
var dep3 = new Department
{
Name = "Test3"
};
set.Add(dep3);
dep3.Name = "Cat";
e = set.GetEnumerator();
e.MoveNext();
bool correctOrder = e.Current.Name == "Abc";
e.MoveNext();
correctOrder = correctOrder && e.Current.Name == "Cat";
Assert.IsTrue(correctOrder, "Moved item to the left");
dep1.Name = "Dad";
e = set.GetEnumerator();
e.MoveNext();
correctOrder = e.Current.Name == "Cat";
e.MoveNext();
correctOrder = correctOrder && e.Current.Name == "Dad";
Assert.IsTrue(correctOrder, "Moved item to the right");
}
}
}
You can change your class to have this declaration...
public class ObservableSortedSet<T> : INotifyCollectionChanged, ISet<T>
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
private readonly SortedSet<T> _sortedSet;
public ObservableSortedSet()
{
_sortedSet = new SortedSet<T>();
}
public bool Add(T item)
{
bool result = _sortedSet.Add(item);
OnCollectionChanged(NotifyCollectionChangedAction.Add);
return true;
}
private void OnCollectionChanged(NotifyCollectionChangedAction action)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(action));
}
// all the rest of ISet implementation goes here...
}
This approach, although more verbose and arguably overkill, will give the behaviour you are after. The coding drill should take about 20 minutes.

Categories