说明
(此系列帖子记录数据结构学习成果,如侵犯视频作者权益,立删)
作者账号 哔哩哔哩:离忧夏天
本文实现队列需要用到单链表以及有序数组的相关知识,详细请看: C#数据结构专栏
目录
集合
集合是一种存储容器,它允许我们在存储的过程中,对于相同的元素,只保留一份,快速的帮助我们进行去重操作,自动过滤掉重复的元素
接口
interface ISet<T>
{
//集合元素数量
int Count { get; }
//判断是否为空
bool IsEmpty { get; }
//增
void Add(T t);
//删
void Remove(T t);
//是否包含某元素
bool Contains(T t);
}
基于无序链表实现集合
链表集合 | 时间复杂度 |
---|---|
Add | O(n) |
Remove | O(1) |
Contains | O(n) |
Count | O(1) |
IsEmpty | O(1) |
class LinkedList1Set<T> :ISet<T>
{
private LinkedList01<T> _list;
public LinkedList1Set()
{
_list = new LinkedList01<T>();
}
public int Count => _list.Count;
public bool IsEmpty => _list.IsEmpty;
public void Add(T t)
{
if (!_list.Contains(t))
_list.AddFirst(t);
}
public void Remove(T t)
{
_list.Remove(t);
}
public bool Contains(T t)
{
return _list.Contains(t);
}
}
基于有序数组实现集合
数组集合 | 时间复杂度 |
---|---|
Add | O(n) |
Remove | O(n) |
Contains | O(log n) |
class SortedArray1Set<Key> :ISet<Key> where Key :IComparable<Key>
{
private SortedArray1<Key> s;
public SortedArray1Set(int capacity)
{
s = new SortedArray1<Key>(capacity);
}
public SortedArray1Set()
{
s = new SortedArray1<Key>();
}
public int Count => s.Count;
public bool IsEmpty => s.IsEmpty;
public void Add(Key key)
{
s.Add(key);
}
public void Remove(Key key)
{
s.Remove(key);
}
public bool Contains(Key key)
{
return s.Contains(key);
}
}
映射(字典)
指两个元素呈现一一对应的关系,即一个key对应一个value,存储数据键值对
接口
interface IDictionary<Key,Value>
{
//查看键值对的数量
int Count { get; }
bool IsEmpty { get; }
void Add(Key key, Value value);
void Remove(Key key);
//查看是否与包含键
bool ContainsKey(Key key);
//获取键对应的值
Value Get(Key key);
//键对应的改为新值
void Set(Key key, Value newValue);
}
支持存储两个数据的链表(用于实现字典)
基于无序链表实现字典
链表字典 | 时间复杂度 |
---|---|
Add | O(n) |
Remove | O(n) |
ContainsKey | O(n) |
Get | O(n) |
Set | O(n) |
class LinkedList3Dictionary<Key ,Value> :IDictionary<Key,Value>
{
private LinkedList3<Key, Value> List;
public LinkedList3Dictionary()
{
List = new LinkedList3<Key, Value>();
}
public int Count => List.Count;
public bool IsEmpty => List.IsEmpty;
public void Add(Key key, Value value)
{
List.Add(key,value);
}
public void Remove(Key key)
{
List.Remove(key);
}
public bool ContainsKey(Key key)
{
return List.Contains(key);
}
public Value Get(Key key)
{
return List.Get(key);
}
public void Set(Key key, Value newValue)
{
List.Set(key,newValue);
}
}
基于有序数组实现字典
数组字典 | 时间复杂度 |
---|---|
Add | O(n) |
Remove | O(n) |
Contains | O(log n) |
class SortedArray2Dictionary<Key, Value> : IDictionary<Key, Value> where Key : IComparable<Key>
{
private SortedArray2<Key, Value> s2;
public SortedArray2Dictionary(int capacity)
{
s2 = new SortedArray2<Key, Value>(capacity);
}
public SortedArray2Dictionary()
{
s2 = new SortedArray2<Key, Value>();
}
public int Count =>s2.Count;
public bool IsEmpty =>s2.IsEmpty;
public void Add(Key key, Value value)
{
s2.Add(key,value);
}
public void Remove(Key key)
{
s2.Remove(key);
}
public bool ContainsKey(Key key)
{
return s2.Contains(key);
}
public Value Get(Key key)
{
return s2.Get(key);
}
public void Set(Key key, Value newValue)
{
s2.Add(key,newValue);
}
}
性能分析比较
TestHelper方法用于测试
//辅助测试类
class TestHelper
{
//私有的构造函数,不允许外部进行实例化
//换句话说就是不能通过new关键字创建一个TestHelper类对象
private TestHelper() { }
//读取名为filename的文件并将它进行简单分词后存入动态数组List中
public static List<string> ReadFile(string filename)
{
//使用FileStream类打开filename文件
FileStream fs = new FileStream(filename, FileMode.Open);
//使用StreamReader读取filename文件信息
StreamReader sr = new StreamReader(fs);
//将读取的单词存入动态数组words中
List<string> words = new List<string>();
while (!sr.EndOfStream)
{
string contents = sr.ReadLine().Trim();
int start = FirstCharacterIndex(contents, 0);
for (int i = start + 1; i <= contents.Length;)
{
if (i == contents.Length || !Char.IsLetter(contents[i]))
{
string word = contents.Substring(start, i - start).ToLower();
words.Add(word);
start = FirstCharacterIndex(contents, i);
i = start + 1;
}
else
i++;
}
}
fs.Close();
sr.Close();
return words;
}
// 寻找字符串s中,从start的位置开始的第一个字母字符的位置
private static int FirstCharacterIndex(string s, int start)
{
for (int i = start; i < s.Length; i++)
if (Char.IsLetter(s[i]))
return i;
return s.Length;
}
}
两种集合比较
public static long TestSet(ISet<string> set, List<string> words)
{
Stopwatch t = new Stopwatch();
t.Start();
foreach (var s in words)
{
set.Add(s);
}
t.Stop();
return t.ElapsedMilliseconds;
}
static void Main(string[] args)
{
Console.WriteLine("双城记");
List<string> words = TestHelper.ReadFile("测试文件1/双城记.txt");
Console.WriteLine("词汇量总数:" + words.Count);
Console.WriteLine();
Console.WriteLine("链表集合");
LinkedList1Set<string> linkedList1Set = new LinkedList1Set<string>();
long t1 = TestSet(linkedList1Set, words);
Console.WriteLine("不同单词的总数: "+linkedList1Set.Count);
Console.WriteLine("运行时间: " + t1 + "ms");
Console.WriteLine();
Console.WriteLine("有序数组集合");
SortedArray1Set<string> sortedArray1Set = new SortedArray1Set<string>();
long t2 = TestSet(sortedArray1Set, words);
Console.WriteLine("不同单词的总数: " + sortedArray1Set.Count);
Console.WriteLine("运行时间: " + t2 + "ms");
}
可以看到,由有序数组实现的集合性能比链式集合是非常优的
两种字典比较
//字典(链表和数组)
public static long TestDicionary(IDictionary<string, int> dic, List<string> words)
{
Stopwatch t = new Stopwatch();
t.Start();
foreach (var word in words)
{
//如果单词不存在字典中,说明是第一次遇见这个单词,频次设为1
if (!dic.ContainsKey(word))
dic.Add(word, 1);
else//如果已经存在在字典中,那么将单词对应的频次+1
dic.Set(word, dic.Get(word) + 1);
}
t.Stop();
return t.ElapsedMilliseconds;
}
可以看到,有序数组实现的字典,运行时间更短,这里的差距主要就体现在二分查找和有序查找的区别。