LeetCode-Notes 数据结构篇:哈希表与二叉树
文章概要的内容包括哈希表的实现原理、冲突解决策略及其在LeetCode题目中的应用,以及二叉树的基础知识、遍历方式和实战题目解析。通过代码示例和流程图,详细讲解了哈希表和二叉树的核心概念与操作。
哈希表的实现与应用
哈希表(Hash Table)是一种高效的数据结构,广泛应用于数据存储和快速查找场景。它通过哈希函数将键映射到存储位置,从而实现平均时间复杂度为 O(1) 的插入、删除和查找操作。本节将详细介绍哈希表的实现原理及其在 LeetCode 题目中的应用。
哈希表的基本原理
哈希表的核心思想是通过哈希函数将键(Key)转换为数组的索引(Index),从而直接访问对应的存储位置。以下是哈希表的关键组成部分:
- 哈希函数:将键映射到数组索引的函数。理想的哈希函数应满足均匀分布,避免冲突。
- 冲突解决:当多个键映射到同一索引时,需采用冲突解决策略,常见方法有:
- 链地址法:每个索引位置存储一个链表,冲突的键值对存储在链表中。
- 开放地址法:冲突时寻找下一个空闲位置存储。
哈希表的实现示例
以下是一个基于链地址法的哈希表实现(Python):
class HashTable:
def __init__(self, size=10):
self.size = size
self.table = [[] for _ in range(size)]
def _hash(self, key):
return hash(key) % self.size
def insert(self, key, value):
index = self._hash(key)
for pair in self.table[index]:
if pair[0] == key:
pair[1] = value
return
self.table[index].append([key, value])
def get(self, key):
index = self._hash(key)
for pair in self.table[index]:
if pair[0] == key:
return pair[1]
raise KeyError(f"Key {key} not found")
def remove(self, key):
index = self._hash(key)
for i, pair in enumerate(self.table[index]):
if pair[0] == key:
self.table[index].pop(i)
return
raise KeyError(f"Key {key} not found")
哈希表的应用场景
哈希表在算法题目中的应用非常广泛,以下是几个典型的应用场景:
- 快速查找:如统计频率、去重等。
- 缓存设计:如 LRU 缓存机制。
- 字符串处理:如判断字符串是否为变位词。
示例题目分析
题目:两数之和(LeetCode 1)
给定一个整数数组 nums 和一个目标值 target,请你在数组中找出和为目标值的两个整数,并返回它们的索引。
哈希表解法:
def two_sum(nums, target):
hash_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hash_map:
return [hash_map[complement], i]
hash_map[num] = i
return []
时间复杂度分析:
- 插入和查找操作均为 O(1),整体时间复杂度为 O(n)。
哈希表的性能优化
- 选择合适的哈希函数:减少冲突概率。
- 动态扩容:当负载因子(元素数量/表大小)超过阈值时,扩容哈希表。
- 冲突处理优化:如使用更高效的链表或红黑树替代普通链表。
总结
哈希表是一种高效的数据结构,适用于需要快速查找和插入的场景。通过合理的哈希函数设计和冲突解决策略,可以显著提升其性能。在 LeetCode 题目中,哈希表常用于解决查找、统计和缓存设计等问题。
二叉树的基础知识与遍历
二叉树是数据结构中非常重要的一种非线性结构,广泛应用于算法设计和问题求解中。本节将介绍二叉树的基础知识,包括二叉树的定义、性质以及常见的遍历方式(前序、中序、后序和层次遍历),并通过代码示例和流程图帮助读者深入理解。
二叉树的定义与性质
二叉树(Binary Tree)是每个节点最多有两个子节点的树结构,通常称为左子节点和右子节点。它具有以下性质:
- 节点数量关系:对于一棵非空二叉树,如果其叶子节点数为 ( n_0 ),度为 2 的节点数为 ( n_2 ),则满足 ( n_0 = n_2 + 1 )。
- 高度与节点数:高度为 ( h ) 的二叉树最多有 ( 2^h - 1 ) 个节点。
- 完全二叉树:除了最后一层外,其他层的节点数都达到最大值,且最后一层的节点都集中在左侧。
代码示例:二叉树的节点定义
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
二叉树的遍历方式
二叉树的遍历是指按照某种顺序访问树中的所有节点。常见的遍历方式包括:
- 前序遍历(Preorder Traversal):根节点 → 左子树 → 右子树。
- 中序遍历(Inorder Traversal):左子树 → 根节点 → 右子树。
- 后序遍历(Postorder Traversal):左子树 → 右子树 → 根节点。
- 层次遍历(Level Order Traversal):按层次从上到下、从左到右访问节点。
前序遍历示例代码
def preorder_traversal(root):
if not root:
return []
return [root.val] + preorder_traversal(root.left) + preorder_traversal(root.right)
中序遍历示例代码
def inorder_traversal(root):
if not root:
return []
return inorder_traversal(root.left) + [root.val] + inorder_traversal(root.right)
层次遍历示例代码
from collections import deque
def level_order_traversal(root):
if not root:
return []
queue = deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
遍历方式对比
| 遍历方式 | 访问顺序 | 应用场景 |
|---|---|---|
| 前序遍历 | 根 → 左 → 右 | 复制二叉树、表达式树求值 |
| 中序遍历 | 左 → 根 → 右 | 二叉搜索树的有序输出 |
| 后序遍历 | 左 → 右 → 根 | 删除二叉树、计算表达式树 |
| 层次遍历 | 按层次从上到下、从左到右 | 计算二叉树的高度、广度优先搜索 |
遍历流程图
以下是一个二叉树的遍历流程图示例(前序遍历):
总结
通过本节的学习,读者应掌握二叉树的基本概念、性质以及四种常见的遍历方式。每种遍历方式都有其独特的应用场景,理解它们的实现原理有助于在实际问题中灵活运用。接下来的练习题目将帮助巩固这些知识。
二叉搜索树与并查集
二叉搜索树(Binary Search Tree, BST)和并查集(Union-Find)是两种在算法和数据结构中广泛应用的工具。它们分别用于高效的数据查询和动态连通性问题。本文将深入探讨它们的原理、实现方式以及典型应用场景。
二叉搜索树
二叉搜索树是一种特殊的二叉树,满足以下性质:
- 左子树的所有节点值小于根节点的值。
- 右子树的所有节点值大于根节点的值。
- 左右子树也分别为二叉搜索树。
基本操作
- 插入:从根节点开始,比较插入值与当前节点值,决定向左或向右子树递归插入。
- 查找:类似插入,根据比较结果决定搜索方向。
- 删除:分为三种情况:
- 无子节点:直接删除。
- 有一个子节点:用子节点替换当前节点。
- 有两个子节点:用右子树的最小节点替换当前节点。
应用场景
- 动态数据集合的快速查找、插入和删除。
- 实现有序映射或集合。
并查集
并查集是一种用于处理不相交集合合并及查询问题的数据结构。它支持两种操作:
- Find:查询某个元素所属的集合。
- Union:合并两个集合。
实现方式
- 路径压缩:优化Find操作,使树更扁平。
- 按秩合并:优化Union操作,将小树合并到大树下。
应用场景
- 图的连通性问题。
- 社交网络中的好友关系管理。
代码示例
二叉搜索树插入
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def insert(root, val):
if not root:
return TreeNode(val)
if val < root.val:
root.left = insert(root.left, val)
else:
root.right = insert(root.right, val)
return root
并查集实现
class UnionFind:
def __init__(self, size):
self.parent = list(range(size))
self.rank = [0] * size
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # Path compression
return self.parent[x]
def union(self, x, y):
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return
if self.rank[x_root] < self.rank[y_root]:
self.parent[x_root] = y_root
else:
self.parent[y_root] = x_root
if self.rank[x_root] == self.rank[y_root]:
self.rank[x_root] += 1
性能对比
| 数据结构 | 查找复杂度 | 插入复杂度 | 删除复杂度 | 空间复杂度 |
|---|---|---|---|---|
| 二叉搜索树 | O(log n) | O(log n) | O(log n) | O(n) |
| 并查集 | O(α(n)) | O(α(n)) | - | O(n) |
注:α(n)为反阿克曼函数,通常认为其值小于5。
通过本文的介绍,读者可以掌握二叉搜索树和并查集的核心原理与实现方法,并在实际问题中灵活运用。
实战题目解析与练习
在数据结构的学习中,哈希表和二叉树是两个非常重要的部分。哈希表以其高效的查找和插入操作著称,而二叉树则是许多高级数据结构和算法的基础。本节将通过具体的LeetCode题目,深入解析哈希表和二叉树的应用场景和解题技巧,并提供相关练习题目,帮助读者巩固所学知识。
哈希表实战题目解析
题目1:两数之和(LeetCode 1)
题目描述:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
解题思路:
- 使用哈希表存储数组中每个元素的值及其索引。
- 遍历数组,对于每个元素,检查目标值减去当前元素的差是否在哈希表中。
- 如果存在,则返回两个元素的索引。
代码示例:
def twoSum(nums, target):
hash_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hash_map:
return [hash_map[complement], i]
hash_map[num] = i
return []
复杂度分析:
- 时间复杂度:O(n),只需遍历一次数组。
- 空间复杂度:O(n),哈希表存储最多n个元素。
题目2:无重复字符的最长子串(LeetCode 3)
题目描述:给定一个字符串,找出其中不含有重复字符的最长子串的长度。
解题思路:
- 使用滑动窗口和哈希表记录字符的最新位置。
- 维护一个窗口,窗口内无重复字符,每次移动右边界。
- 如果遇到重复字符,移动左边界到重复字符的下一个位置。
代码示例:
def lengthOfLongestSubstring(s):
char_map = {}
left = max_len = 0
for right, char in enumerate(s):
if char in char_map and char_map[char] >= left:
left = char_map[char] + 1
char_map[char] = right
max_len = max(max_len, right - left + 1)
return max_len
复杂度分析:
- 时间复杂度:O(n),遍历字符串一次。
- 空间复杂度:O(min(m, n)),m为字符集大小。
二叉树实战题目解析
题目1:二叉树的中序遍历(LeetCode 94)
题目描述:给定一个二叉树的根节点,返回它的中序遍历。
解题思路:
- 递归法:按照左子树、根节点、右子树的顺序遍历。
- 迭代法:使用栈模拟递归过程。
代码示例(递归法):
def inorderTraversal(root):
result = []
def helper(node):
if not node:
return
helper(node.left)
result.append(node.val)
helper(node.right)
helper(root)
return result
复杂度分析:
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(n),递归栈的深度。
题目2:对称二叉树(LeetCode 101)
题目描述:给定一个二叉树,检查它是否是镜像对称的。
解题思路:
- 递归法:比较左右子树的对称节点。
- 迭代法:使用队列或栈逐层比较。
代码示例(递归法):
def isSymmetric(root):
def helper(left, right):
if not left and not right:
return True
if not left or not right:
return False
return left.val == right.val and helper(left.left, right.right) and helper(left.right, right.left)
return helper(root.left, root.right) if root else True
复杂度分析:
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(n),递归栈的深度。
练习题目
| 题目编号 | 题目名称 | 难度 | 相关知识点 |
|---|---|---|---|
| 1 | 两数之和 | 简单 | 哈希表 |
| 3 | 无重复字符的最长子串 | 中等 | 哈希表、滑动窗口 |
| 94 | 二叉树的中序遍历 | 简单 | 二叉树、递归 |
| 101 | 对称二叉树 | 简单 | 二叉树、递归 |
通过以上题目和解析,读者可以更好地理解哈希表和二叉树的应用场景,并通过练习题目巩固所学知识。
总结
哈希表和二叉树是数据结构中非常重要的两种结构,哈希表适用于快速查找和插入的场景,而二叉树则是许多高级算法的基础。通过本文的学习,读者应掌握哈希表的设计与优化方法,以及二叉树的遍历与应用技巧。结合实战题目解析和练习,可以进一步巩固这些知识并提升算法解题能力。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



