什么是数据结构
数据结构是计算机存储、组织和处理数据的方式,它允许数据的高效访问和修改。
简单来说,数据结构就是存储数据的容器,它可以帮助你以有组织的方式管理和组织数据。
常见的数据结构类型:
数组和列表:
线性数据结构,用于存储元素的集合。数组通常大小固定,而列表大小可变。
栈和队列:
特殊类型的列表,栈是后进先出(LIFO),队列是先进先出(FIFO)。
字典/哈希表:
存储键值对,提供快速的查找。
树:
模拟层次结构,每个节点可以有多个子节点,但只有一个父节点。
图:
由节点(或称顶点)和边组成,用于表示多对多的关系。
堆:
一种特殊的树状数据结构,通常用于实现优先队列。
数组的图示:
想象一排连续的邮政信箱,每个信箱代表数组中的一个元素位置:
+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 |
+---+---+---+---+---+
每个盒子(如0, 1, 2, 3, 4)代表数组中的一个索引位置。
盒子内可以放入相应的值(比如整数、对象等)。
所有盒子的大小都是一样的,代表数组中的每个元素占用相同的内存空间。
这排信箱是固定的,表明数组的大小在创建时就确定,之后不能改变。
列表的图示:
想象一系列的盒子,每个盒子都有一个手柄可以链接到下一个盒子:
+---+ +---+ +---+ +---+
| 0 |--->| 1 |--->| 2 |--->| 3 |
+---+ +---+ +---+ +---+
每个盒子代表列表中的一个元素。
盒子内可以放入相应的值。
每个盒子都有一个指针(或手柄)指向下一个盒子,代表列表元素之间的链接。
可以随时添加或移除盒子,代表列表的大小是可变的。
栈的图示
栈(Stack)是一种后进先出(LIFO)的数据结构,这意味着最后添加进去的元素会是第一个被移除的。想象一叠盘子或一堆书,你只能从顶部添加或移除它们。下面是栈的一个图示描述:
+---+
| 4 | <- 栈顶 (Top)
+---+
| 3 |
+---+
| 2 |
+---+
| 1 | <- 栈底 (Bottom)
+---+
在这个例子中:
添加(Push):当你添加一个元素(比如数字4),它被放置在栈的顶部。
移除(Pop):当你移除一个元素,栈顶的元素(这里是数字4)将被移除。
查看(Peek):你通常可以查看栈顶的元素,但不实际移除它,这在这个例子中将返回数字4。
队列图示:
队列(Queue)是一种先进先出(FIFO)的数据结构,这意味着最先添加进去的元素会是第一个被移除的。想象一下在商店里排队等候结账,第一个排队的人将是第一个得到服务的人。下面是队列的一个图示描述:
队尾 (Rear) 队首 (Front)
+---+---+---+---+
<- | 1 | 2 | 3 | 4 | <-
+---+---+---+---+
在这个例子中:
入队(Enqueue):当你添加一个元素(比如数字4),它被放置在队列的尾部。
出队(Dequeue):当你移除一个元素,队列前面的元素(这里是数字1)将被移除。
查看(Peek):你通常可以查看队列前面的元素,但不实际移除它,这在这个例子中将返回数字1。
字典的图示
字典(Dictionary)在C#中是一种非常实用的数据结构,它存储的是键(Key)和值(Value)的配对。每个键都是唯一的,每个键映射到一个特定的值。字典的内部通常是通过哈希表实现的,这允许快速查找。下面是一个简化的字典图示描述:
+-------+ +-----------------+
| Key | | Value |
+-------+ +-----------------+
| "one" | -> | 1 |
+-------+ +-----------------+
| "two" | -> | 2 |
+-------+ +-----------------+
| "three"| ->| 3 |
+-------+ +-----------------+
键(Key):唯一标识存储在字典中的每个值的标签。在这个例子中,键是一些字符串("one", "two", "three")。
值(Value):与键相关联的数据。在这个例子中,值是数字(1, 2, 3)。
当你想要根据键来快速检索值时,字典非常有用。例如:
添加(Add):将新的键值对添加到字典中。如果你尝试添加一个已经存在的键,会发生错误。
访问(Access):通过键来快速访问对应的值。例如,dictionary["one"]将返回1。
删除(Remove):通过键来移除对应的键值对。
查找(Find):检查某个键是否存在于字典中。
哈希表的图示
哈希表是一种使用哈希函数组织数据以支持快速插入和搜索的数据结构。在C#中,字典(Dictionary)通常是通过哈希表实现的,但为了理解哈希表的工作原理,我们可以看一个更简化的视图。
哈希表使用一个数组来存储数据,并使用哈希函数将每个键转换为数组的一个索引。下面是一个哈希表的简化图示:
数组
+--------+--------+--------+--------+
| Bucket | Bucket | Bucket | Bucket |
| 0 | 1 | 2 | 3 |
+--------+--------+--------+--------+
| | | |
v v v v
Null "two" "one" Null
| |
v v
"four" "three"
在这个图示中:
数组(Buckets):哈希表的主体是一个数组,每个元素称为"bucket"。
键(Key):要存储的元素(例如,"one", "two", "three", "four")。
哈希函数:一个将键转换成数组索引的函数。好的哈希函数会均匀分布键,并尽量减少冲突。
冲突(Collision):当两个键的哈希值相同,它们被分配到同一个bucket时,就会发生冲突。在这个例子中,假设"two"和"four"有相同的哈希值,并且都被分配到了Bucket 1。
链表(Chaining):解决冲突的一种常见方法是在每个bucket里维护一个链表。如果多个键映射到同一个bucket,它们会被存储在链表中。在查找键时,哈希表首先使用哈希函数定位bucket,然后遍历链表找到正确的键。
树的图示
树是一种非线性的数据结构,由节点组成,每个节点可以链接到多个子节点,但只有一个父节点(根节点除外,它没有父节点)。在计算中,树结构通常用于表示具有层次关系的数据,如文件系统的目录结构、组织结构图等。
下面是一个简化的树结构图示,展示了一个典型的树结构的组成部分:
Root
|
+-----+-----+
| |
Node1 Node2
| +---+---+
| | |
Node3 Node4 Node5
/ \
Node6 Node7
根节点(Root):树的顶部节点,没有父节点。
节点(Node):树的每个元素。每个节点包含数据和可能的子节点链接。
子节点(Child):从另一个节点延伸出的节点。
父节点(Parent):具有指向一个或多个节点的链接的节点。
叶节点(Leaf):没有子节点的节点(在这个例子中,Node3, Node5, Node6, 和 Node7是叶节点)。
边(Edge):连接节点和它们的子节点的线。
路径(Path):从一个节点到另一个节点的节点序列。
深度(Depth):从根节点到特定节点的路径长度。
高度(Height):树中所有节点的最大深度。
图的图示
图(Graph)是一种复杂的数据结构,用于表示由边连接的节点(或称顶点)的集合。图可以是有向的(边有方向)或无向的(边没有特定方向)。它们在表示和处理对象间复杂关系时非常有用,如社交网络、网络拓扑、地图导航等。
下面是一个简化的图结构图示,它展示了一个无向图的基本组成部分:
Node1 ------ Node2
| / \
| / \
| / \
Node4 ---- Node3 -- Node5
\ /
\ /
\ /
\ /
-------- Node6
节点(Node/Vertex):图中的元素。在这个例子中,有Node1, Node2, Node3, Node4, Node5和Node6。
边(Edge):连接两个节点的线。在无向图中,边没有方向(例如,Node1和Node2通过一条边连接,表示它们相互关联)。
路径(Path):从一个节点到另一个节点的边的序列。
邻接(Adjacency):如果两个节点通过一条边直接连接,则它们被称为相邻的。
在有向图中,边有明确的方向,通常用箭头表示。例如,如果有一个从Node1指向Node2的边,那就意味着你可以从Node1到达Node2,但不一定能反过来从Node2到达Node1。
堆的图示
堆(Heap)是一种特殊的完全二叉树。在这种树结构中,节点的值总是不大于或不小于其子节点的值,这取决于是最大堆还是最小堆。在最大堆中,父节点的值总是大于或等于其子节点的值。在最小堆中,父节点的值总是小于或等于其子节点的值。堆通常用于实现优先队列。
以下是一个最小堆的图示:
1
/ \
3 5
/ \ / \
7 6 9 8
/ \
15 17
在这个最小堆中:
每个节点的值都小于或等于其子节点的值。
根节点(顶部)是整个堆中的最小值。
堆是一个完全二叉树,意味着除了最后一层外,每层都被完全填满,且最后一层的节点都尽可能地集中在左侧。
操作堆通常涉及以下几个关键操作:
插入(Add):在堆中添加新元素。新元素被放置在树的底部,然后“上浮”到正确的位置以保持堆的顺序。
删除最小(或最大)(Extract Min/Max):移除并返回根节点的值(堆中的最小或最大值)。通常,最后一个元素被移动到根的位置,然后“下沉”到正确的位置以保持堆的顺序。
查找最小(或最大)(Find Min/Max):返回堆中的最小或最大值,而不移除它。这通常只涉及查看根节点的值。