数据结构与算法(C#)-数据结构

参考:

菜鸟教程 - 数据结构与算法

Microsoft Learning - System Collections

C#高级--常用数据结构使用C#实现数据结构堆平衡二叉树硬核图解面试最怕的红黑树

从B树、B+树、B*树谈到R 树【数据结构】B树(B-树)和B+树

图论入门及基础概念(图篇)图论(一)基本概念C#实现图(Graph)排序算法——拓扑排序


1、基本概念

数据结构分类:

集合

无序、一对一

线性

有序、一对一

有序、一对多

有序、多对多

常见的数据结构:

数组

有序地存储同一类型的多个变量,内存地址也是连续的。

链表

有序地存储同一类型的多个变量,内存地址不必连续。

队列

特殊的线性表,只允许在表的一端进行插入操作,而在另一端进行删除操作。

特殊的线性表,只能在一个表的一个固定端进行数据结点的插入和删除操作。

一种递归的数据结构,n个结点的有限集,有一个特定的根结点。

可以看作是一个完全二叉树的数组对象,子节点总不大于/不小于其父节点。

哈希表

又称散列表,对存储的数据进行散列函数计算后分类,便于查找。

一系列结点以及连接这些节点的边组成。

:C#运行不安全代码,在项目->XXX属性->生成->勾选“允许使用unsafe{}关键字编译的代码”。在unsafe{ ... }中可是使用指针。

int[] arr = new int[10];
for(int i=0;  i<10; i++) { arr[i] = i; }
foreach(int i in arr) {
    unsafe {
        int* ptr = &i;
        IntPtr add = (IntPtr)ptr;
        Console.WriteLine(add.ToString("x"));    // 打印内存地址
    }
}

2、数组

优点:连续的内存地址使之可以随机访问。

缺点:插入数据效率低,每次插入需要分配新的内存。

2.1、普通数组、Array

int[] arr = new int[5] {0, 1, 2, 3, 4};
foreach(int i in arr) {
    unsafe {
        int* ptr = &i;
        IntPtr add = (IntPtr)ptr;
        Console.WriteLine("{0}: {1}", i, add.ToString("x"));   // 打印内存地址
    }
}

Array类提供了数组的创建、处理、搜索数组并对数组进行排序的方法,是所有数组的基类。

Array实现ICollection、IEnumerable、IList、IStructuralComparable、IStructuralEquatable、ICloneable。

using System.Collections;

Array array = Array.CreateInstance(typeof(int), 3, 4);      // 创建一个3行4列int的数组
for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)       // 下边界索引、上边界索引
{
    for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
    {
        array.SetValue((int)Math.Pow(10, i) + j, i, j);
    }
}
Console.WriteLine("Dimension: {0}", array.Rank);
Console.WriteLine("Length: {0}", array.Length);
void Print(Array myArray) {       // 按行输出数组
    IEnumerator myEnumerator = myArray.GetEnumerator();     // 返回一个枚举
    int i = 0;
    int cols = myArray.GetLength(myArray.Rank - 1);
    while (myEnumerator.MoveNext()) {
        if (i < cols) { i++; }
        else { Console.WriteLine(); i = 1; }
        Console.Write("\t{0}", myEnumerator.Current);
    }
    Console.WriteLine();
}

Print(array);

2.2、ArrayList

优点:大小根据需求会动态增加。

不建议使用 ArrayList 类进行新类的开发,建议改用泛型 List<T> 类。类 ArrayList 旨在保存对象的异类集合,任何对象都是object类型,值类型会进行装箱。

ArrayList实现ICollection、IEnumerable、IList、ICloneable。

using System.Collections;

ArrayList arrayList = new ArrayList();              // 实例化
for(int i = 0; i < 5; i++) { arrayList.Add(i); }
Console.Write("长度:{0}\t容量: {1}\t", arrayList.Count, arrayList.Capacity);

void Print(IEnumerable arrList)
{
    foreach (object obj in arrList)
    {
        Console.Write("{0} ", obj);
    }
}
Print(arrayList);

2.3、List<T>

是类 ArrayList 的泛型等效项,可通过索引访问对象的强类型列表,提供对列表进行搜索、排序和操作的方法。List<T>在大多数情况下性能更好,并且类型安全。但是,如果值类型用于类型 T,则需要考虑实现和装箱问题。

实现ICollection<T>、IEnumerable<T>、IList<T> IReadOnlyCollection<T>、IReadOnlyList<T>、ICollection、IEnumerable、IList。

using System.Collections;

public class Person {
    public string Name { set; get; }
    public int Age { set; get; }
    public override string ToString() 
    {         // 用于WriteLine,输出前调用对象的ToString()
        return "Name: " + Name + "; Age: " + Age;
    }
    public override bool Equals(object obj) 
    {        // 用于Remove时比较对象是否相等
        if (obj == null) return false;  // object对象为空
        Person objAsPerson = obj as Person; // 转换为Person为空,即非Person对象
        if (objAsPerson == null) return false;
        else return Equals(objAsPerson);
    }
    public bool Equals(Person other) {
        if (other == null) return false;
        return (this.Name.Equals(other.Name));  // 只比较Name属性
    }
}

public class Pragram
{
    public static void Main()
    {
        IList<Person> persons = new List<Person>();             // List
        persons.Add(new Person() { Name = "Tom", Age = 19 });
        persons.Add(new Person() { Name = "Jerry", Age = 18 });
        persons.Add(new Person() { Name = "Buly", Age = 20 });
        persons.Insert(2, new Person() {  Name = "aaa", Age= 21 });
        persons.Remove(new Person() { Name = "aaa" });
        Console.WriteLine(persons.Contains(new Person() { Name = "aaa" }));
        foreach (Person person in persons) { Console.WriteLine(person); }
    }
}

3、链表、队列、栈

3.1、LinkedList<T>

优点:插入、删除效率高,复杂度O(1)。

缺点:随机访问效率低,需要遍历链表,复杂度O(n)。

双重链表,增删节点时不会在堆上分配分配其他对象。

对象中的每个 LinkedList<T> 节点的类型为 LinkedListNode<T>。 由于LinkedList<T>是双向的,因此每个节点都指向前一个节点,Next向后指向后一个节点。

同时创建节点及其值时,包含引用类型的列表性能更好。 LinkedList<T> 接受 null 作为引用类型的有效 Value 属性,并允许重复值。如果 LinkedList<T>为空,则 First 和 Last 属性包含 null。

LinkedList<T>仅支持多线程读。

LinkedList实现ICollection<T>、IEnumerable<T>、IReadOnlyCollection<T>、ICollection、IEnumerable、IDeserializationCallback、ISerializable。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        string[] words = { "Hello", "World", "!", "C", "sharp", "!"};
        LinkedList<string> sentence = new LinkedList<string>(words);            // 双链表
        sentence.AddLast("Four-lettle Words!");
        sentence.AddFirst("Welcome!");
        sentence.RemoveLast();
        foreach (string word in sentence) { Console.Write("{0} ", word); }
        Console.WriteLine(sentence.Last);                                       // 最后一个节点
        LinkedListNode<string> currentNode = sentence.Find("Welcome!");
        Console.WriteLine(currentNode.Value);                                   // 节点的值
    }
}

3.2、Queue(队列)

对象先进先出(FIFO)的集合,容量动态扩展。

一般用于存放临时信息,在检索元素值后,可能需要放弃该元素。按照存储顺序访问时,使用Queue。与存储顺序相反时,使用Stack。

Queue 接受 null 为有效值,并允许重复元素。

如果需要同时从多个线程访问集合,使用 ConcurrentQueue<T>。

实现ICollection、IEnumerable、ICloneable。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        Queue queue = new Queue();          // 队列
        queue.Enqueue("Hello");
        queue.Enqueue("World");
        queue.Enqueue("!");
        queue.Dequeue();                    // 移除队首,并返回
        Console.WriteLine(queue.Contains("Hello"));
        foreach (var item in queue) { Console.Write($"{item} "); }
        Console.WriteLine(queue.Peek());    // 查询队首
    }
}

3.3、Stack(栈)

简单的对象后进先出 (LIFO) 的非泛型集合,容量动态扩展。

Stack 接受 null 为有效值并允许重复元素。

实现ICollection、IEnumerable、ICloneable。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        Stack stack = new();                    // 堆栈
        stack.Push("Hello");
        stack.Push("World");
        stack.Push("!");
        stack.Pop();                            // 移除堆顶,并返回
        Console.WriteLine(stack.Contains("Hello"));
        foreach (var item in stack) { Console.Write($"{item} "); }
        Console.WriteLine(stack.Peek());        // 查询堆顶
    }
}

4、集合

4.1、HashSet

集是不包含重复元素且其元素没有特定顺序的集合。

HashSet<T> 类提供高性能集合操作。动态容量扩展。

HashSet<T> 提供了集合运算,例如UnionWith(集)、IntersectWith(集)、ExceptWith(集)、SymmetricExceptWith(集)。

实现ICollection<T>、IEnumerable<T>、IReadOnlyCollection<T>、ISet<T>、IEnumerable、IReadOnlySet<T>、IDeserializationCallback、ISerializable。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        HashSet<int> nums = new HashSet<int> { 1, 2, 3, 4, 5, 6 };
        HashSet<int> evens = new HashSet<int>();
        for (int i = 1; i <= 6; i++) { evens.Add( 2 * i ); }
        // 并集
        HashSet<int> unions = new HashSet<int>(nums);
        unions.UnionWith(evens);
        // 交集
        HashSet<int> intersects = new HashSet<int>(nums);
        intersects.IntersectWith(evens);
        // 差集
        HashSet<int> excepts = new HashSet<int>(nums);
        excepts.ExceptWith(evens);
        // 补集
        HashSet<int> symmExcepts = new HashSet<int>(nums);
        symmExcepts.SymmetricExceptWith(evens);

        HashSet<int>[] result = { nums, evens, unions, intersects, excepts, symmExcepts };
        foreach(var i in result) { Display(i); }
        void Display(HashSet<int> hs) {
            Console.Write($"{
   {");
            foreach (int i in hs) { Console.Write($" {i}"); }
            Console.WriteLine(" }");
        }
    }
}

4.2、SortedSet(有序集合)

不包含重复元素且其元素有一定顺序的集合。

SortedSet<T> 对象在插入和删除元素时维护排序顺序,而不会影响性能。同样可以使用HashSet中的并交叉补的集合运算方法。

通过实现IComparer<T>中的Compare(T x, T y)方法,完成比较器的定义。

实现ICollection<T>、IEnumerable<T>、IReadOnlyCollection<T>、ISet<T>、ICollection、IEnumerable、IReadOnlySet<T>、IDeserializationCallback、ISerializable。

public class Pragram
{
    public static void Main()
    {
        SortedSet<int> defaultOrder = new SortedSet<int>();
        SortedSet<int> descendOrder = new SortedSet<int>(new ByDescend());
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int t = random.Next(0, 1000);
            defaultOrder.Add(t);
            descendOrder.Add(t);
        }
        foreach (int s in defaultOrder) { Console.Write($"{s} "); }
        Console.WriteLine();
        foreach (int s in descendOrder) { Console.Write($"{s} "); }
    }
    public class ByDescend: IComparer<int> {  // 比较器,定义类型为比较两个对象而实现的方法
        public int Compare(int x, int y)
        {   // 比较两个对象,并返回一个int来表明相对大小;<0表示x小于y,0表示相等,>0表示x大于y
            if (x > y) { return -1; }        // 降序
            else if (x < y) { return 1; }
            else { return 0; }
        }
    }
}

自定义类型的比较器。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        SortedSet<Person> persons = new SortedSet<Person>(new ByNameAge()) {
            new Person("Tom", 21),
            new Person("Tom's Master", 55),
            new Person("Jerry", 18), 
            new Person("Jerry's Cousin", 35), 
            new Person("Jerry's Cousin", 32),
            new Person("Buly", 20)  };

        foreach (Person person in persons) {
            Console.WriteLine($"name: {person.Name}; age: {person.Age}");
        }
    }
    public class Person {
        private string name;
        private int age;
        public string Name { get { return name; } }
        public int Age { get { return age; } }
        public Person(string name, int age) { this.name = name; this.age = age; }
    }
    public class ByNameAge: IComparer<Person>
    {    // 比较器
        public int Compare(Person x, Person y)
        {   // 比较两个对象,并返回一个int来表明相对大小;<0表示x小于y,0表示相等,>0表示x大于y
            if (x.Name.CompareTo(y.Name) !=0 ) { return -x.Name.CompareTo(y.Name); }        // 降序
            else if (x.Age.CompareTo(y.Age) != 0 ) { return x.Age.CompareTo(y.Age); }       // 默认升序
            else { return 0; }
        }
    }
}

5、哈希表

5.1、Dictionary(字典)

表示键和值的集合,值是无序的,键是唯一的。动态容量扩展。

使用键检索值的速度非常快,接近O(1) ,因为 Dictionary<TKey,TValue> 类是作为哈希表实现的。

如果键的类型TValue为引用类型,则键不能为null,但值可以为null 。

实现ICollection<KeyValuePair<TKey,TValue>>、IDictionary<TKey,TValue>、IEnumerable<KeyValuePair<TKey,TValue>>、IEnumerable<T>、IReadOnlyCollection<KeyValuePair<TKey,TValue>>、IReadOnlyDictionary<TKey,TValue>、ICollection、IDictionary、IEnumerable、IDeserializationCallback、ISerializable。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        Dictionary<string, int> dic = new Dictionary<string, int>();
        dic.Add("Tom", 19);
        dic.Add("Jerry", 18);
        dic.Add("Buly", 20);
        // 增加相同key的异常处理
        try { dic.Add("Tom", 20); }                     
        catch (ArgumentException) { Console.WriteLine("Key Alreadly Exists!"); }
        // 赋值不存在的key的异常处理
        try { dic["aaa"] = 20; }
        catch (KeyNotFoundException) { Console.WriteLine("Key is not found"); }
        // 若经常获取/访问不存在的键,使用TryGetValue()方法
        int valueAge;
        if (dic.TryGetValue("Tom", out valueAge)) {
            Console.WriteLine($"Key[Tom]'s value is {valueAge}");
        } else {
            Console.WriteLine("Key[Tom]'s value is not found");
        }
        // ContainsKey()方法来检查Key
        if (!dic.ContainsKey("bbb")) {                  
            dic.Add("bbb", 222);
        }
        // 在foreach中字典的枚举元素类型是KeyValuePair键值对
        foreach(KeyValuePair<string, int> kvp in dic) { 
            Console.WriteLine($"Key is {kvp.Key}; Value is {kvp.Value}");
        }
        // Values属性可以只获取值,是强类型的
        Dictionary<string, int>.ValueCollection dicValue = dic.Values;  
    }
}

5.2、HashTable(哈希表)

根据键的哈希代码进行组织的键/值对的集合,动态容量扩展。

Hashtable 中任何元素都被当做object处理,存在装修、拆箱

重写方法Object.Equals(或IHashCodeProvider接口)和方法Object.GetHashCode (或IComparer接口) 需要Hashtable用作键的对象。 方法和接口的实现必须以相同的方式处理区分大小写。

Hashtable 中的每个键对象都必须提供自己的哈希函数,可通过调用 GetHash来访问该函数。

将元素添加到 Hashtable时,该元素将基于键的哈希代码放入存储桶中。随后的键查找使用键的哈希代码在一个特定存储桶中搜索,从而大大减少了查找元素所需的键的比较数。元素与存储桶的最大比率决定了Hashtable 的负载因子。 较小的负载因子的查找更快,但是内存消耗更多。 默认负载系数 1.0 通常提供速度和内存大小之间的最佳平衡。 创建 Hashtable 时还可以指定不同的负载因子。

实现ICollection、IDictionary、IEnumerable、ICloneable、IDeserializationCallback、ISerializable。

using System.Collections;

public class Pragram
{
    public static void Main()
    {
        Hashtable ht = new Hashtable();
        ht.Add("Tom", 19);
        ht.Add("Jerry", 18);
        ht.Add("Buly", 20);

        try { ht.Add("Tom", 20); }
        catch { ht["Tom"] = 20; }
        // 默认的Item属性
        ht["aaa"] = 30;
        // 判断是否包含某个键
        if (!ht.ContainsKey("bbb")) { ht.Add("bbb", 40); }
        // 遍历哈希表的键值对
        foreach (DictionaryEntry kv in ht) { Console.WriteLine($"Key is {kv.Key}; Value is {kv.Value}"); }
        // 删除键
        ht.Remove("bbb");
        // 遍历哈希表的键
        ICollection htKeys = ht.Keys;
        foreach (string key in htKeys) { Console.WriteLine($"Key is {key}"); }
    }
}

5.3、SortedDictionary

根据键进行排序的键/值对的集合。

SortedDictionary<TKey,TValue> 泛型类是具有 O(logn) 检索的二进制搜索树,与 SortedList<TKey,TValue> 泛型类类似,但是,SortedList使用的内存少于 SortedDictionary,SortedDictionary具有更快的未排序数据的插入和删除操作。如果一次性从已排序的数据填充列表, SortedList 比 SortedDictionary快。

每个键/值对都可以作为

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值