【愚公系列】2023年12月 数据结构(三)-列表
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
🚀前言
数据结构是计算机科学中的一个重要概念,它描述了数据之间的组织方式和关系,以及对这些数据的访问和操作。常见的数据结构有:数组、链表、栈、队列、哈希表、树、堆和图。
-
数组(Array):是一种线性数据结构,它将一组具有相同类型的数据元素存储在一起,并为每个元素分配一个唯一的索引。数组的特点是具有随机访问的能力。
-
链表(Linked List):也是一种线性数据结构,它由一系列的节点组成,每个节点包含数据和指向下一个节点的引用。链表的特点是可以动态地插入或删除节点,但访问某个节点时需要从头开始遍历。
-
栈(Stack):是一种后进先出(LIFO)的数据结构,它只能在栈顶进行插入和删除操作。栈通常用于实现递归算法、表达式求值和内存管理等场景。
-
队列(Queue):是一种先进先出(FIFO)的数据结构,它可以在队尾插入元素,在队头删除元素。队列通常用于数据的缓存、消息队列和网络通信等场景。
-
哈希表(Hash Table):也称为散列表,它是一种根据关键字直接访问数据的数据结构。哈希表通常由数组和散列函数组成,可以在常数时间内进行插入、删除和查找操作。
-
树(Tree):是一种非线性数据结构,它由一系列的节点组成,每个节点可以有若干个子节点。树的特点是可以动态地插入或删除节点,常见的树结构包括二叉树、平衡树和搜索树等。
-
堆(Heap):是一种特殊的树结构,它通常用于实现优先队列和堆排序等算法。堆分为最大堆和最小堆,最大堆的每个节点的值都大于等于其子节点的值,最小堆则相反。
-
图(Graph):是一种由节点和边组成的非线性数据结构,它可以用来表示各种实体之间的关系,如社交网络、路线图和电路图等。图的遍历和最短路径算法是常见的图算法。
🚀一、列表
🔎1.基本思想
列表是一种线性数据结构,它由一系列元素组成,每个元素可以有一个前驱和一个后继。列表的基本思想是将元素按照一定顺序组织起来,并且支持在列表中插入、删除和遍历元素。
列表可以使用数组或链表实现。在数组实现中,列表的元素在内存中是连续的,而在链表实现中,元素可以在内存中任意位置。列表的一个重要特点是支持快速随机访问,因为元素在数组实现中是连续存储的。
列表的操作包括插入、删除、遍历等。在数组实现中,插入和删除操作需要将后续元素进行移动,所以时间复杂度为O(n)。而在链表实现中,插入和删除操作只需要修改节点的指针,时间复杂度为O(1)。遍历列表需要将每个元素依次访问,时间复杂度为O(n)。
列表具有广泛的应用,例如存储数组、字符串等数据、实现队列、栈、哈希等数据结构,以及其它需要按序访问元素的场合。
🔎2.列表常用操作
🦋2.1 初始化列表
1、自定义列表的初始化
C#中的列表可以使用以下语法进行初始化:
- 使用花括号{}进行初始化,每个元素用逗号分隔:
List<int> myList = new List<int>() { 1, 2, 3, 4, 5 };
- 使用Add方法添加元素:
List<int> myList = new List<int>();
myList.Add(1);
myList.Add(2);
myList.Add(3);
- 使用数组初始化列表:
int[] myArray = {1,2,3,4,5};
List<int> myList = new List<int>(myArray);
注意,以上初始化方法都需要在列表变量声明时进行。如果需要在后续添加元素,可以使用Add方法进行添加。
2、内置列表的初始化
当然C#中链表的初始化可以使用LinkedList类。以下是示例代码:
LinkedList<int> list = new LinkedList<int>();
这将创建一个空链表,你可以通过使用AddLast()方法将元素添加到末尾,使用AddFirst()方法将元素添加到开头。你可以使用foreach循环遍历链表中的元素。例如:
list.AddLast(1);
list.AddLast(2);
list.AddFirst(3);
foreach (int item in list)
{
Console.WriteLine(item);
}
这将输出:
3
1
2
🦋2.2 访问元素
在C#中,可以通过以下方式来访问列表中的元素:
- 通过索引访问元素:
可以使用方括号和元素的索引值来访问特定位置的元素。例如,myList[0]
将访问列表中的第一个元素。
- 遍历列表中的元素:
可以使用循环遍历整个列表中的元素。例如,使用foreach
循环可以遍历列表中的所有元素:
foreach (var item in myList)
{
Console.WriteLine(item);
}
- 列表的LINQ操作:
C#中的LINQ操作可用于查询和操作列表。可以使用Where
、Select
、OrderBy
等函数进行筛选、投影和排序等操作。例如,以下代码将从列表中选择所有大于10的元素:
var newList = myList.Where(x => x > 10).ToList();
🦋2.3 插入与删除元素
C#中的列表类(List)提供了许多方法来插入和删除元素。以下是一些常用的方法:
- Add():向列表的末尾添加一个元素。
List<int> myList = new List<int>();
myList.Add(1);
- Insert():在指定索引处插入一个元素。
List<int> myList = new List<int>();
myList.Insert(0, 1);
- Remove():删除列表中的指定元素。
List<int> myList = new List<int>{1, 2, 3};
myList.Remove(2); // myList变成了{1, 3}
- RemoveAt():删除列表中指定索引处的元素。
List<int> myList = new List<int>{1, 2, 3};
myList.RemoveAt(1); // myList变成了{1, 3}
- Clear():清空列表中的所有元素。
List<int> myList = new List<int>{1, 2, 3};
myList.Clear(); // myList变成了{}
🦋2.4 遍历列表
在C#中,有多种方法可以遍历列表元素:
- 使用 for 循环:
您可以使用 for 循环来迭代列表中的元素。这是最基本的方法,因为您可以通过索引号访问列表元素。例如:
List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
- 使用 foreach 循环:
使用 foreach 循环可以更加简洁的实现对列表元素的迭代。例如:
List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
foreach (var item in list)
{
Console.WriteLine(item);
}
- 使用 LINQ:
使用LINQ的查询语句,可以方便地过滤、排序、映射和聚合列表数据。例如:
List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
var filteredList = from i in list
where i > 3
select i;
foreach (var item in filteredList)
{
Console.WriteLine(item);
}
- 使用 Lambda 表达式:
您可以使用 Lambda 表达式来创建委托,以便在遍历列表时执行特定操作。例如:
List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
list.ForEach(item =>
Console.WriteLine(item));
🦋2.5 拼接列表
在C#中进行列表拼接的方法有以下几种:
1.使用List.AddRange方法
List.AddRange方法可以将一个列表中的元素全部添加到另外一个列表中。示例如下:
List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 4, 5, 6 };
list1.AddRange(list2);
2.使用LINQ的Concat方法
使用LINQ的Concat方法可以将两个列表连接起来。示例如下:
List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 4, 5, 6 };
List<int> result = list1.Concat(list2).ToList();
3.使用List.InsertRange方法
使用List.InsertRange方法可以在指定位置插入一个列表中的元素。示例如下:
List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 4, 5, 6 };
list1.InsertRange(1, list2);
以上方法都可以实现列表的拼接操作,具体使用哪种方法可以根据实际情况选择。
🦋2.6 排序列表
可以使用List<T>类的Sort()方法来对列表进行排序。该方法接受一个参数,即一个委托,用于比较两个元素的大小关系。
例如,对一个包含整数的List进行升序排序:
List<int> myList = new List<int>{ 4, 2, 8, 1, 5 };
myList.Sort((a, b) => a.CompareTo(b));
也可以使用lambda表达式实现同样的功能:
myList.Sort((a, b) => a - b);
如果要进行降序排序,只需将比较委托中的参数顺序交换即可:
myList.Sort((a, b) => b.CompareTo(a)); //或者 myList.Sort((a, b) => b - a);
🔎3.列表实现
在C#中,可以通过自定义一个类来实现列表的功能,以下是一个简单的实现示例:
public class MyList<T>
{
private T[] elements;
private int count;
public MyList(int capacity)
{
elements = new T[capacity];
count = 0;
}
public int Count
{
get { return count; }
}
public void Add(T element)
{
if (count == elements.Length)
{
Array.Resize(ref elements, elements.Length * 2);
}
elements[count++] = element;
}
public T Get(int index)
{
if (index < 0 || index >= count)
{
throw new IndexOutOfRangeException();
}
return elements[index];
}
public void Set(int index, T element)
{
if (index < 0 || index >= count)
{
throw new IndexOutOfRangeException();
}
elements[index] = element;
}
public void RemoveAt(int index)
{
if (index < 0 || index >= count)
{
throw new IndexOutOfRangeException();
}
for (int i = index; i < count - 1; i++)
{
elements[i] = elements[i + 1];
}
count--;
}
}
以上定义了一个类型参数为T的泛型类MyList<T>,其中包含了增加元素、获取元素、设置元素、删除元素等常见的列表操作方法。使用示例:
MyList<string> myList = new MyList<string>(10);
myList.Add("hello");
myList.Add("world");
myList.Add("!");
Console.WriteLine(myList.Get(1)); //输出:world
myList.Set(0, "hi");
myList.RemoveAt(2);
Console.WriteLine(myList.Count); //输出:2
🔎4.优点和缺点
列表(List)是一种基本的数据结构,可以存储一组元素,并按照一定的顺序排列。列表的优点和缺点如下:
优点:
- 灵活性:列表可以动态添加和删除元素,适用于需要频繁修改元素的场景。
- 可附加的元信息:列表中的元素可以携带附加信息,如元素的唯一标识符、元素的创建时间等,便于后续对元素的处理。
- 支持索引:列表支持按照下标访问元素,方便对元素进行读取和修改操作。
- 空间效率高:列表在存储元素时只需要按顺序排列,不需要为每个元素预留空间。
缺点:
- 访问效率低:在大型列表中查找和访问元素时效率较低,需要遍历整个列表。
- 插入和删除效率低:由于需要维护元素的顺序,插入和删除操作比较耗时。
- 空间浪费:由于列表内部存储的元素是连续的,当需要插入或删除元素时,可能需要移动大量元素,导致空间浪费。
- 不适合高并发场景:由于列表对于并发访问的支持较弱,不适合高并发的场景。
🔎5.应用场景
列表在数据结构中被广泛应用,并且是一种基本的数据结构类型之一。以下是列表的一些应用场景:
-
数据存储:列表可以用于存储一系列数据元素,例如存储学生的成绩、存储商品的信息、存储音乐播放列表等。
-
数据处理:列表可以用于对一组数据进行处理,例如排序、筛选、搜索等。
-
栈和队列的实现:栈和队列都可以通过列表来实现。
-
迭代器:列表可以被用作迭代器,使得可以对数据进行迭代处理。
-
图的遍历:图的遍历算法中,可以使用列表来存储已访问和未访问的节点。
-
数据结构的实现:列表也可以用于实现其他数据结构,例如链表和哈希表等。
列表是一种非常常用的数据结构类型,它可以用于各种不同的应用场景,帮助我们更方便地管理和处理数据。
🚀二、列表扩展
🔎1.Array
数组在C#中最早出现的。在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单。
<span style="font-family:SimSun;font-size:18px;">//数组
string[] s=new string[2];
//赋值
s[0]="a";
s[1]="b";
//修改
s[1]="a1";
</span>
优点:数组在内存中是连续存储的、所以它的索引速度是非常快的、时间复杂度为O(1)、而且它的赋值/修改/获取元素也是非常简单的。
缺点:
1、定义数组的时候需要指定数组的长度(过长会造成内存浪费、过短会导致程序异常System.IndexOutOfRangeException:“索引超出数组界限”)
2、插入和删除元素效率低、也比较麻烦。
在不清楚数组长度的时候、就很尴尬了。 所以C#提供了ArrayList了来处理这些问题…
🔎2.ArrayList
使用大小会根据需要动态增加的数组。
//初始化
ArrayList list = new ArrayList();
//添加元素
list.Add(1);
list.Add("A");
list.Add(0.1);
//修改元素
list[2] = "B";
//指定索引插入元素
list.Insert(1, "ABC");
//移除元素
list.RemoveAt(1);
优点:
1、ArrayList大小会根据需要动态增加的数组。
2、实现了IList接口、可以方便的对数据进行添加、插入和删除。
缺点:
1、ArrayList会把插入的数据都当做object类型来存储、在操作数据的时候可能会因为类型不匹配而出现异常、它是非类型安全的对象。
2、由于存储的是object类型、在使用的时候进行类型转换、会造成装箱拆箱、从而损耗性能。
装箱:把值类型转换成引用类型;
拆箱:把引用类型转换成值类型。
//装箱
int i = 1;
object obj = (object)i;
//拆箱
int j = (int)obj;
由于ArrayList存在类型不安全、装箱拆箱损耗性能。.NET Framework 2.0 推出了List<T>
🔎3.List<T>
表示可通过索引访问的对象的强类型列表。 提供用于对列表进行搜索、排序和操作的方法。
//初始化
List<int> list = new List<int>();
//添加
list.Add(12);
list.Add(34);
//编译器会进行类型验证、下面代码编译失败
//list.Add("ABC");
//修改
list[0] = 1;
//移除
list.RemoveAt(0);
优点:由于泛型List是强类型、编译器会验证类型安全。这样就避免了类型的不安全、以及数据强制转换导致装箱拆箱损耗性能。
备注:哈希表(散列),就是数组的升级版通过hash运算快速查找到值,数组下标就是哈希值。(前512是int,后才是哈希)
🚀感谢:给读者的一封信
亲爱的读者,
我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。
如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。
我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。
如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。
再次感谢您的阅读和支持!
最诚挚的问候, “愚公搬代码”
- 点赞
- 收藏
- 关注作者
评论(0)