I made a Binary Tree for searching a best prefix for a phone number but when i have a largest list of prefixes, the follow code sometimes generate a StackOverflowException on StartsWith() function.
BTreeNode.cs
public class BTreeNode<T>
{
public BTreeNode(T item)
{
this.Item = item;
}
public T Item { get; set; }
public BTreeNode<T> Left { get; set; }
public BTreeNode<T> Right { get; set; }
}
BTree.cs
public class BTree
{
public BTreeNode<string> Root { get; set; }
public BTree(IEnumerable<string> enumerable)
{
if (enumerable == null)
{
throw new ArgumentNullException(nameof(enumerable));
}
using (IEnumerator<string> enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
AddNode(enumerator.Current);
}
}
}
public void AddNode(string key)
{
if (Root == null)
{
Root = new BTreeNode<string>(key);
}
else
{
AddNode(key, Root);
}
}
private void AddNode(string key, BTreeNode<string> current)
{
if (key.StartsWith(current.Item))
{
if (current.Left == null)
{
current.Left = new BTreeNode<string>(key);
}
else
{
AddNode(key, current.Left);
}
}
else
{
if (current.Right == null)
{
current.Right = new BTreeNode<string>(key);
}
else
{
AddNode(key, current.Right);
}
}
}
public string Search(string key)
{
if (Root == null)
{
return null;
}
return Search(key, Root, null);
}
private string Search(string key, BTreeNode<string> current, BTreeNode<string> match)
{
if (current != null)
{
if (current.Left != null)
{
if (key.StartsWith(current.Left.Item))
{
return Search(key, current.Left, current.Left);
}
}
if (current.Right != null)
{
if (key.StartsWith(current.Item))
{
return Search(key, current.Left, current);
}
if (key.Length >= current.Right.Item.Length)
{
if (long.Parse(key) >= long.Parse(current.Right.Item))
{
return Search(key, current.Right, match);
}
}
}
else
{
if (key.StartsWith(current.Item))
{
return Search(key, current.Left, current);
}
}
}
return match?.Item;
}
}
Sample data
StackOverflowException occurred for example if i'm searching on loop the all sample data that i load to Binary Tree.
Any solution?
I see no mechanism to balance this tree, so when you import a lot of data, some sub-branches can become very long. It is even possible that the whole tree is one linear branch, and doesn't split in multiple branches, especially when your list with sample data is ordered.
Some more advances trees, like a red-black tree, have a built-in mechanism to keep the tree balanced.
You have 35000 elements, which means a perfectly balanced tree should be not deeper than something around 15. (2^15 = 32768). But when the tree is completely unbalanced, you have one very long branch, which is almost the same as a single-linked list of 35000 elements, which are accessed recursively.
Related
I have created a custom Linked List , code given below .
Now trying to implement a sort (its what I told to do, not the best choice I know) , how we can do in best possible time complexity or the best approach
my custom linked list
my doubt is over the last node , in each phase of bubble sort should try to sort last and then start over from first node , how to handle the last node as its points to the first node
public class CustomCircularList<T> : ICollection<T>, IEnumerable<T>
{
Node<T> head = null;
Node<T> tail = null;
int count = 0;
readonly IEqualityComparer<T> comparer;
public int Count { get { return count; } }
public bool IsReadOnly { get { return false; } }
public void Add(T item)
{
this.AddLast(item);
}
AddLast...
}
}
my Node class has three properties
public T Value { get; private set; }
public Node<T> Next { get; set; }
public Node<T> Previous { get; set; }
I added IComparer to my class T like this and trying to work like below
public class Fund: IComparer<Fund>
{
public string fundname{ get; set; }
public int Compare([AllowNull] Fund x, [AllowNull] Fund y)
{
if (x == null || y == null)
{
return 0;
}
return x.fundname.CompareTo(y.fundname);
}
First of all, let me assume that Node<T> object has (at least) 2 standard properties: Value and Next:
public class Node<T> {
...
public T Value {get; set;}
public Node<T> Next {get;}
}
Since you have circular linked list,
tail.Next == head
we can enumerate all items except the last one as
for (Node<T> node = head; !ReferenceEquals(node.Next, head); node = node.Next) {
...
}
Just for reference, should we have a linked list (non circular), the loop would be (all we should do is to change head to null):
for (Node<T> node = head; !ReferenceEquals(node.Next, null); node = node.Next) {
...
}
The code can be
public void BubbleSort(IComparer<T> comparer = null) {
comparer ??= Comparer<T>.Default;
if (comparer is null)
throw new ArgumentNullException(nameof(comparer));
if (head is null)
return; // empty linked list
bool agenda = true;
while (agenda) {
agenda = false;
for (Node<T> node = head; !ReferenceEquals(node.Next, head); node = node.Next)
if (comparer.Compare(node.Value, node.Next.Value) > 0) {
agenda = true;
var help = node.Value;
node.Value = node.Next.Value;
node.Next.Value = help;
}
}
}
Edit: If you want to sort some custom type (T) you should either ensure the T implements IComparable<T>:
public class MyClass: IComparable<MyClass> {
...
public int CompareTo(MyClass other) {
TODO: return 0 if other == this, -1 if this < other, +1 if this > other
}
}
or you should implement a comparer IComparer<MyClass> which you should provide as an argument:
public class MyComparer<MyClass> {
public int Compare(MyClass x, MyClass y) {
//TODO: return 0 when x == y, -1 when x < y, when x > y
}
}
then
MyCircularLinkedList.BubbleSort(new MyComparer());
I am writing a term paper and am having trouble checking it. The point of this work is to create a circular two-way array of Client class objects. I sent it to the manager, and he said that some aspects of the program are not correct. He said:
Why does the Сlient class use an array of references private Operation[] operations; instead of an array of objects of the Operation class?
Why is the Bank class using an address list private DoublyLinkedList<Client> arrayClient; instead of an array-queue?
// Operation class
using System;
namespace DepositLibrary
{
// Класс операции
public class Operation
{
// Дата операции
public DateTime date;
// Депозит операции
public decimal deposit;
public Operation(DateTime dateIn, decimal depositIn)
{
date = dateIn;
deposit = depositIn;
}
// Получение даты операции
public DateTime GetDateTime { get => date; }
// Получение депозита операции
public decimal GetDeposit { get => deposit; }
}
}
// Client class
using System;
using System.Collections.Generic;
namespace DepositLibrary
{
//Класс клиент
public class Client
{
//Фамилия клиента
public string surname;
//Текущий депозит лицевого счета
public decimal currentDeposit;
// Совершенные операции клиента
private Operation[] operations;
public Client(string surnameIn)
{
surname = surnameIn;
operations = new Operation[0];
}
//Получить фамилию
public string GetSurname { get => surname; }
//Получить все совершенные операции
public Operation[] Operations { get => operations; }
//Получить текущий депозит лицевого счета
public decimal GetDeposit { get => currentDeposit; }
}
}
// Bank class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace DepositLibrary
{
// Класс банк
public class Bank
{
// Наименование банка
private string nameBank;
// Клиентские банковские книги
private DoublyLinkedList<Client> arrayClient;
public Bank(string _nameBank)
{
nameBank = _nameBank;
arrayClient = new DoublyLinkedList<Client>();
}
//Получить наименование банка
public string GetBankName { get => nameBank; }
//Получить клиентские банковские книги
public DoublyLinkedList<Client> GetListClients { get => arrayClient; }
}
public class DoublyNode<T>
{
public DoublyNode(T data)
{
Data = data;
}
public T Data { get; set; }
public DoublyNode<T> Previous { get; set; }
public DoublyNode<T> Next { get; set; }
}
public class DoublyLinkedList<T> : IEnumerable<T> // двусвязный список
{
public DoublyNode<T> head; // головной/первый элемент
public DoublyNode<T> tail; // последний/хвостовой элемент
int count; // количество элементов в списке
// добавление элемента
public void Add(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);
if (head == null)
head = node;
else
{
tail.Next = node;
node.Previous = tail;
// for looped array
node.Next = head;
head.Previous = node;
}
tail = node;
count++;
}
public void AddFirst(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);
DoublyNode<T> temp = head;
node.Next = temp;
head = node;
if (count == 0)
tail = head;
else
{
temp.Previous = node;
// for looped
tail.Next = node;
node.Previous = tail;
}
count++;
}
// удаление
public bool Remove(T data)
{
DoublyNode<T> current = head;
// поиск удаляемого узла
do
{
if (current.Data.Equals(data))
{
break;
}
current = current.Next;
} while (!current.Data.Equals(head.Data));
if (current != null)
{
// если узел не последний
if (!current.Next.Data.Equals(head.Data))
{
current.Next.Previous = current.Previous;
}
else
{
// если последний, переустанавливаем tail
tail = current.Previous;
// for looped
tail.Next = head;
}
// если узел не первый
if (!current.Previous.Data.Equals(tail.Data))
{
current.Previous.Next = current.Next;
}
else
{
// если первый, переустанавливаем head
head = current.Next;
// for looped
head.Previous = tail;
}
count--;
return true;
}
return false;
}
public int Count { get { return count; } }
public bool IsEmpty { get { return count == 0; } }
public void Clear()
{
head = null;
tail = null;
count = 0;
}
public bool Contains(T data)
{
DoublyNode<T> current = head;
do
{
if (current.Data.Equals(data))
return true;
current = current.Next;
} while (!current.Data.Equals(head.Data));
return false;
}
IEnumerator GetEnumerator()
{
return ((IEnumerable)this).GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
DoublyNode<T> current = head;
do
{
yield return current.Data;
current = current.Next;
} while (!current.Data.Equals(head.Data));
}
public IEnumerable<T> BackEnumerator()
{
DoublyNode<T> current = tail;
do
{
yield return current.Data;
current = current.Previous;
} while (!current.Data.Equals(tail.Data));
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}
I tried for a very long time to understand what he wants from me, but didn't come to a normal result. I would be grateful for any help and advice. Thank you!
I am looking for a job. Periodically I come across the requirement to perform some kind of test task. Recently, I received a reject (which in itself is not critical for me) based on a test solution. Unfortunately, they did not provide feedback to me, so I decided to write here in an attempt to figure out the reason for the rejection and the correctness of the formulation of the tasks themselves. Personally, I have suspicions regarding part 3. Task text:
Original task text
Написать функцию, которая преобразует строку с римским числом в целое (иными словами, написать тело функции public int RomanToInt(string s)). Римское число не больше 3000.
Проверить сбалансированность скобочной структуры в произвольном выражении ((1+3)()(4+(3-5)))
Реализовать двусвязный список и написать функцию, переворачивающую его, т.е. изменяющую порядок элементов на обратный.
public interface DoubleLinkedListNode<T>
{
T Value { get; set; }
DoubleLinkedNode<T> Next { get; set; }
DoubleLinkedNode<T> Prev { get; set; }
}
public interface DoubleLinkedList<T>
{
DoubleLinkedNode<T> First { get; set; }
DoubleLinkedNode<T> Last { get; set; }
void Reverse();
//insert new DoubleLinkedListNode with given value at the start of the list
void AddFirst(T value);
//insert new DoubleLinkedListNode with given value at the end of the list
void AddLast(T value);
}
Все решения должны быть сопровождены юнит-тестами.
Task translation
Write a function that converts a string with a Roman number to an integer (in other words, write the body of a function public int RomanToInt (string s)). Roman number no more than 3000.
Check the balance of the bracket structure in an arbitrary expression ((1 + 3) () (4+ (3-5)))
Implement a doubly linked list and write a function that reverses it, i.e. reverse the order of elements.
The third part caused me dissonance, first of all, by non-compliance with the convention. In addition, in the text of the assignment there are no points that would regulate the use of these interfaces in the required solution. Just a piece of code and that’s it.
I have a question for you how these interfaces can be used in the implementation of doubly linked lists and what advantages this can give (in comparison with more common solutions). Maybe I'm not so strong in OOD, but I personally only think of circumventing the limitations of lack of multiple inheritance in C#.
My solution
public class DoublyNode<T>
{
public DoublyNode()
{}
public DoublyNode(T data)
{
Data = data;
}
public T Data { get; set; }
public DoublyNode<T> Previous { get; set; }
public DoublyNode<T> Next { get; set; }
}
public class DoublyLinkedList<T> : IEnumerable<T>
{
private DoublyNode<T> head;
private DoublyNode<T> tail;
private int count;
public void AddLast(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);
if (head == null)
{
head = node;
}
else
{
tail.Next = node;
node.Previous = tail;
}
tail = node;
count++;
}
public void AddFirst(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);
DoublyNode<T> temp = head;
node.Next = temp;
head = node;
if (count == 0)
{
tail = head;
}
else
{
temp.Previous = node;
}
count++;
}
public bool Remove(T data)
{
DoublyNode<T> current = head;
while (current != null)
{
if (current.Data.Equals(data))
{
break;
}
current = current.Next;
}
if (current != null)
{
if (current.Next != null)
{
current.Next.Previous = current.Previous;
}
else
{
tail = current.Previous;
}
if (current.Previous != null)
{
current.Previous.Next = current.Next;
}
else
{
head = current.Next;
}
count--;
return true;
}
return false;
}
public int Count { get { return count; } }
public bool IsEmpty { get { return count == 0; } }
public void Clear()
{
head = null;
tail = null;
count = 0;
}
public bool Contains(T data)
{
DoublyNode<T> current = head;
while (current != null)
{
if (current.Data.Equals(data))
{
return true;
}
current = current.Next;
}
return false;
}
public void Reverse()
{
var buffer = head;
head = tail;
tail = buffer;
DoublyNode<T> current;
current = head;
while (current != null)
{
current.Next = buffer;
current.Next = current.Previous;
current.Previous = buffer;
current = current.Next;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this).GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
DoublyNode<T> current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
public IEnumerable<T> BackEnumerator()
{
DoublyNode<T> current = tail;
while (current != null)
{
yield return current.Data;
current = current.Previous;
}
}
}
Hi guys help me please.
I wrote the code for a binary search tree, but for some reason I'm wildly stupid in post-traverse and searching. Can someone help with writing a postorder and search.
Thank you and sorry for bad english. Thank you very much again.
I don't show you my Remove and Contains method because it does not important. If you want to see them , i can show it.
My BinaryTreeNode class
public class BinaryTreeNode<T> : IComparable<T>
where T : IComparable
{
public BinaryTreeNode(T value)
{
Value = value;
}
public BinaryTreeNode<T> Left { get; set; }
public BinaryTreeNode<T> Right { get; set; }
public T Value { get; }
public int CompareTo(T other)
{
return Value.CompareTo(other);
}
public IEnumerator<T> GetEnumerator()
{
var leftEnumerable = (IEnumerable<T>) Left ?? new T[0];
var rightEnumerable = (IEnumerable<T>) Right ?? new T[0];
return leftEnumerable.Concat(new[] {Value})
.Concat(rightEnumerable)
.GetEnumerator();
}
}
My BinaryTree class :
public class BinaryTree<T>
where T : IComparable
{
private BinaryTreeNode<T> _head;
public int Count { get; private set; }
public void Add(T value)
{
if (_head == null)
_head = new BinaryTreeNode<T>(value);
else
AddTo(_head, value);
Count++;
}
private void AddTo(BinaryTreeNode<T> node, T value)
{
if (value.CompareTo(node.Value) < 0)
{
if (node.Left == null)
node.Left = new BinaryTreeNode<T>(value);
else
AddTo(node.Left, value);
}
else
{
if (node.Right == null)
node.Right = new BinaryTreeNode<T>(value);
else
AddTo(node.Right, value);
}
}
public IEnumerable<T> Preorder(BinaryTreeNode<T> root)
{
var stack = new Stack<BinaryTreeNode<T>>();
stack.Push(root);
while (stack.Count > 0)
{
var node = stack.Pop();
yield return node.Value;
if (node.Right != null)
stack.Push(node.Right);
if (node.Left != null)
stack.Push(node.Left);
}
}
public IEnumerable<T> InOrder(BinaryTreeNode<T> root)
{
var stack = new Stack<BinaryTreeNode<T>>();
while (root != null)
{
while (root.Left != null)
{
stack.Push(root);
root = root.Left;
}
yield return root.Value;
while (root.Right == null && stack.Count > 0)
{
root = stack.Pop();
yield return root.Value;
}
root = root.Right;
}
}
public IEnumerator<T> GetEnumerator()
{
return _head.GetEnumerator();
}
public void Clear()
{
_head = null;
Count = 0;
}
}
I am building a treeview with a list of ScanItem. The class of ScanItem is actually:
public class ScanItem
{
public string FullPath { get; set; }
public string Name
{
get
{
return Path.GetFileName(FullPath);
}
}
public DateTime ModifiedDate { get; set; }
public DateTime CreatedDate { get; set; }
public FileAttributes Attributes { get; set; }
public bool IsDirectory { get; set; }
public string Extension
{
get
{
if (IsDirectory)
return "Folder";
else
return Path.GetExtension(Name);
}
}
public UInt64 Size { get; set; }
}
In order for me to create a treeview I needed to create two other classes in order to distinguish the folders and files in my treeview:
public class ScanFile : ScanItem
{
}
public class ScanDir : ScanItem
{
public List<ScanItem> Items { get; set; }
public ScanDir()
{
Items = new List<ScanItem>();
}
}
Note that the class ScanFile is just like the ScanItem and the ScanDir class has an extra property called Items and will contain a list of items of itself.
So if I where to iterate through this direcotory (C:\Temp):
my List will actually contain:
note that if I expand one ScanDir object I will get another List:
in order to populate the following treeview:
So I was able to populate this list using recursion by searching for files and directories in a specific path.
I just wanted to explain my situation because there are several places in the internet that enable you to filter a treeview and that is what I actually want to do. But it will be nice if I can iterate through each item in List and then remove it if some criteria is not met:
I have actually tried using the following recursive method to filter my results.
public List<ScanItem> search(List<ScanItem> items)
{
var filter = new List<ScanItem>();
foreach (var item in items)
{
if (!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
if (item.IsDirectory)
{
search(((ScanDir)item).Items);
}
}
return filter;
}
I think that if an item is found I need to add all the parent root directories and that's why it does not work. The reason why I want to build my own recursion method is because I want to be able to filter the treeview based on spesific criteria.
EDIT:
In other words if I want to have all the items that contain "X.txt" in my listview I want to just see:
I would do it like this: create public abstract ScanItem Seach(string s) on your ScanItem. You can then call it with the string you want to search for.
The actual implementation would look like this:
ScanFile:
public override ScanItem Seach(string s)
{
if (Name.Contains(s))
return this;
return null;
}
ScanDir:
public override ScanItem Seach(string s)
{
var results = Items.Select(i => i.Seach(s)).Where(i => i != null).ToList();
if (results.Any())
{
var result = (ScanDir)MemberwiseClone();
result.Items = results;
return result;
}
return null;
}
The implementation in ScanFile is easy: if the file matches, return it, else return null. In ScanDir, call Search on all child items recursively. If any of them returned non-null, create a copy of the current object and set the Items of the copy only to those that matched. If none matched, return null.
Note that this will search only through the names of files, not directories. But if you want to do that, such modification is going to be straight-forward.
You should treat the directories a little different because now, if the root directory does not meet the criteria the routine will exit immediately.
Try this: change your ScanItem a little:
public class ScanItem {
...
public virtual bool IsDirectory { get; }
...
}
add this to your scanFile:
public class ScanFile : ScanItem {
public override bool IsDirectory {
get { return false; }
}
}
and this to your scanDir:
public class ScanDir : ScanItem {
public List<ScanItem> Items { get; set; }
public ScanDir() {
Items = new List<ScanItem>();
}
public ScanDir CopyWithoutChildren() {
return new ScanDir() {
FullPath = this.FullPath,
ModifiedDate = this.ModifiedDate,
CreatedDate = this.CreatedDate,
Attributes = this.Attributes,
Size = this.Size
};
}
public override bool IsDirectory {
get { return true; }
}
}
Now do the filtering on the files, omitting empty directories:
public List<ScanItem> search(List<ScanItem> items) {
var filter = new List<ScanItem>();
foreach(var item in items) {
if(item.IsDirectory) {
List<ScanItem> potential = search(((ScanDir)item).Items);
if(potential.Count > 0) {
ScanDir dir = ((ScanDir)item).CopyWithoutChildren();
dir.Items.AddRange(potential);
filter.Add(dir);
}
} else {
if(!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
}
}
return filter;
}
I didn't test it, but I guess that should do what you want.
I realized my comment to your post might not have been descriptive enough, so I've written some C#-ish pseudocode to demonstrate what I was getting at.
Here's an example of using the Visitor pattern to implement search in a polymorphic, loosely-coupled way:
interface FilesystemVistor
{
void Visit (FilesystemItem item);
}
interface FilesystemItem
{
void Accept(FilesystemVistor visitor);
string Name;
}
class Directory : FilesystemItem
{
private FilesystemItem[] _children;
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
foreach(FilesystemItem item in _children)
{
visitor.Visit(item);
}
}
}
class File : FilesystemItem
{
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
}
}
class FilesystemSearcher : FilesystemVistor
{
private List<string> _results;
public void Visit(FilesystemItem item) {
if (item.Name == "Foo") { _results.Add(item.Name); }
}
}
This "visitor pattern"-based design will allow you to implement any kind of search without having the search algorithm having to "know" anything about the structure of the file system and the file system doesn't need an extra property like "IsDirectory" to expose its implementation details.
so If I am looking for the files that contain foo this method will populate the files that contain foo in the list 'newList' . I would have to set that list equal to a new list before calling that method. I am obviously missing basic implementation such as changing foo for a parameter etc. I am also missing to remove the empty directories I am working on that.
private List<ScanDir> history = new List<ScanDir>();
private ScanDir LastDir;
private List<ScanItem> newList = new List<ScanItem>();
public void Search(List<ScanItem> allItems) //adds files that contain foo
{
bool updateLastDir = false;
foreach(ScanItem s in allItems)
{
if (updateLastDir)
{
history = (from a in history
select a).Distinct().ToList();
LastDir = null;
for (int i = history.Count - 1; i >= 0; i--)
{
if (history[i].FullPath == Directory.GetParent(s.FullPath).ToString())
{
LastDir = history[i];
break;
}
}
updateLastDir = false;
}
if (s.IsDirectory)
{
var temp = new ScanDir { FullPath = s.FullPath, IsDirectory = true, comparePath = s.comparePath, Attributes = s.Attributes };
if (LastDir == null)
{
newList.Add(temp);
}
else
{
LastDir.Items.Add(temp);
}
LastDir = temp;
history.Add(LastDir);
Search(((ScanDir)s).Items);
history.RemoveAt(history.Count - 1);
updateLastDir = true;
}
else
{
if (s.Name.Contains("Foo")) // then add it
{
if (LastDir == null)
newList.Add(s);
else
LastDir.Items.Add(s);
}
}
}
}