优缺点
数据结构 | 优点 | 缺点 |
---|---|---|
数组 | 通过索引方便快捷访问元素,适合随机访问。 | 插入和删除操作效率低,大小固定不变。 |
链表 | 插入和删除操作方便快捷,可以动态调整大小。 | 访问任意元素需要从头查找,效率低。 |
栈 | 先进后出,实现简单,可以避免溢出。 | 需要一定的内存空间,不支持随机访问。 |
队列 | 先进先出,实现简单,可以避免溢出。 | 需要一定的内存空间,不支持随机访问。 |
树 | 可以表示具有层级关系的数据,支持快速搜索、插入、删除操作。 | 需要占用大量内存,平衡树的实现复杂。 |
图 | 可以表示任意复杂的数据关系,支持快速搜索、遍历操作。 | 存储和操作复杂,算法较为复杂。 |
哈希表 | 插入和搜索操作效率高,不受数据规模影响。 | 空间复杂度高,需要考虑哈希冲突。 |
堆 | 可以快速找到最大或最小值,支持动态调整大小。 | 不支持随机访问,删除和插入操作效率较低。 |
字典树 | 可以高效存储和查找字符串,支持前缀搜索、模式匹配等操作。 | 空间复杂度高,插入和删除操作效率较低。 |
并查集 | 支持高效的集合操作,可以判断两个元素是否属于同一个集合。 | 实现较为复杂,不支持动态调整大小。 |
使用场景
数据结构 | 使用场景 |
---|---|
数组 | 存储固定大小的数据、排序、随机访问元素等 |
链表 | 插入和删除操作频繁、需要动态分配内存空间、不需要随机访问元素等 |
栈 | 函数调用、表达式求值、处理括号匹配等 |
队列 | 消息队列、任务队列、层次遍历二叉树、BFS等 |
树 | 数据分类、目录结构、XML/HTML解析等 |
图 | 社交网络、推荐系统、最短路径、拓扑排序、连通性检测等 |
哈希表 | 数据库索引、缓存、字典等 |
堆 | 优先级队列、堆排序、求TOP K等 |
字典树 | 高效地查找、存储字符串、字符串搜索等 |
并查集 | 连通分量、图的遍历、非递归的动态连通性问题等 |
数组
数组是一种线性数据结构,用于存储同一类型的一组数据。它的元素在内存中存储是连续的,可以通过索引快速访问元素,适合随机访问。
下面是一个包含5个元素的整型数组的内存结构示意图:
Index 0 1 2 3 4
-----------------------------------------------------
Element | 10 | -2 | 3 | 0 | 8 |
-----------------------------------------------------
链表
链表也是一种线性数据结构,用指针将一组数据按序连接起来,每个节点包含一个数据和一个指向下一个节点的指针。
+--------+ +--------+ +--------+
head | value | next | value | next | value | None
+--------+ +--------+ +--------+
| | |
V V V
+--------+ +--------+ +--------+
| node 1 |----> | node 2 |----> | node 3 |
+--------+ +--------+ +--------+
在上面的图中,链表由一个个节点组成,每个节点包含两个部分,一个是 value 表示节点存储的数据,另一个是 next 表示下一个节点的指针。链表的主体是由一个 head 指针指向链表的第一个节点,而最后一个节点的 next 则指向空地址 None。
栈
栈是一种先进后出的数据结构。
基本操作
- push:将元素压入栈顶
- pop:将栈顶元素弹出
- top:获取栈顶元素但不弹出
- isEmpty:判断栈是否为空
- size:获取栈的元素个数
基于数组
class ArrayStack:
def __init__(self, capacity):
self._capacity = capacity
self._data = [None] * self._capacity
self._size = 0
def push(self, val):
if self._size == self._capacity:
self._expand_capacity()
self._data[self._size] = val
self._size += 1
def _expand_capacity(self):
self._capacity *= 2
new_data = [None] * self._capacity
for i in range(self._size):
new_data[i] = self._data[i]
self._data = new_data
def pop(self):
if self.is_empty():
raise Exception("stack is empty")
self._size -= 1
return self._data[self._size]
def is_empty(self):
return self._size == 0
def top(self):
if self.is_empty():
raise Exception("stack is empty")
return self._data[self._size - 1]
def size(self):
return self._size
基于链表
class StackNode:
def __init__(self, val):
self.val = val
self.next = None
class ChainStack:
def __init__(self):
self._top_node = None
self._size = 0
def push(self, val):
new_node = StackNode(val)
new_node.next = self._top_node
self._top_node = new_node
self._size += 1
def pop(self):
if self.is_empty():
raise Exception("stack is empty")
val = self._top_node.val
self._top_node = self._top_node.next
self._size -= 1
return val
def is_empty(self):
return self._top_node is None
def top(self):
if self.is_empty():
raise Exception("stack is empty")
return self._top_node.val
def size(self):
return self._size
队列
队列是一种先进先出的数据结构,类似于排队。
基本操作
- 入队(enqueue):向队列尾部添加一个新元素。
- 出队(dequeue):从队列头部删除一个元素。
- 获取队列头部元素(peek):查看队列头部的元素,但不将其删除。
- 判断队列是否为空(isEmpty):检查队列是否为空,即队列中是否有元素。
- 获取队列中元素的数量(size):获取队列中元素的数量。
基于数组
class ArrayQueue:
def __init__(self, capacity):
self._capacity = capacity
self._data = [None] * capacity
self._head = self._tail = self._size = 0
def enqueue(self, val):
if self._size == self._capacity:
return False
self._data[self._tail] = val
self._tail = (self._tail + 1) % self._capacity
self._size += 1
return True
def dequeue(self):
if self.is_empty():
return None
val = self._data[self._head]
self._head = (self._head + 1) % self._capacity
self._size -= 1
return val
def is_empty(self):
return self._size == 0
def size(self):
return self._size
基于链表
class QueueNode:
def __init__(self, val):
self.val = val
self.next = None
class ChainQueue:
def __init__(self, capacity):
self._capacity = capacity
self._head = self._tail = None
self._size = 0
def enqueue(self, val):
if self._size == self._capacity:
return False
node = QueueNode(val)
if self.is_empty():
self._head = self._tail = node
else:
self._tail.next = node
self._tail = self._tail.next
self._size += 1
return True
def is_empty(self):
return self._size == 0
def dequeue(self):
if self.is_empty():
return None
val = self._head.val
self._head = self._head.next
self._size -= 1
return val
def size(self):
return self._size
树
名词概念
名词概念 | 定义 |
---|---|
根节点 | 树的顶层节点,没有父节点 |
叶节点 | 没有子节点的节点 |
子节点 | 某个节点连接的下一层节点 |
父节点 | 某个节点连接的上一层节点 |
父子关系 | 父节点连接一个或多个子节点,子节点连接一个父节点,它们之间的连结称为父子关系 |
节点深度 | 从根节点到该节点所经过的边的数量,根节点深度为0 |
子树 | 以一个节点为根的子树包含该节点及其所有子孙节点,该节点是子树的根节点 |
树的高度 | 树中节点深度的最大值 |
节点的度 | 节点子树的数量 |
特性
- 一棵非空的树有且只有一个根节点。
- 每个节点有零个或多个子节点。
- 每个非根节点有且仅有一个父节点。
- 如果节点A是节点B的子节点,则B是A的父节点。
- 如果一棵树的每个节点都不超过n个子节点,则该树称为n叉树。
常用特殊的树
特殊树 | 说明 | 使用场景 |
---|---|---|
二叉树 | 每个节点最多有两个子节点的树 | 算法题、排序、表达式求值等 |
二叉搜索树 | 一种特殊的二叉树,满足左子节点的值小于根节点,右子节点的值大于根节点 | 搜索、插入和删除操作等 |
平衡二叉树 | 一种特殊的二叉搜索树,通过旋转节点重新平衡树的高度以防止出现极端情况 | 搜索、排序、实现哈希表等 |
线段树 | 一种用于处理区间操作的树结构,可以高效地查询和修改区间中的元素 | 动态规划、区间最值查询、区间和查询等 |
字典树 | 一种特殊的树结构,常用于字符串匹配 | 自动补全、拼写检查、翻译软件等 |
哈夫曼树 | 一种利用贪心算法构建的树结构,用于压缩数据 | JPEG、MP3等文件压缩格式 |
Trie树 | 又称字典树,用于快速检索、查找与匹配字符串 | 快速匹配前缀、查找指定前缀字符串、在大量字符串集合中的查找等 |
KD树 | 是一种基于二叉树的数据结构,用于高效地处理多维空间中的数据 | 聚类和最近邻搜索等机器学习算法 |