LeetCode数据结构篇
并查集
并查集(Union-find Data Structure)是一种树型的数据结构。它的特点是由子结点找到父亲结点,用于处理一些不交集(Disjoint Sets)的合并及查询问题。
Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。
基础写法:(自己试一下,要能够独立写出)
1 | import java.util.HashMap; |
并发编程线程基础
LeetCode图篇
[TOC]
图篇的内容:
- 图的表示
- BFS
- DFS
- 最小生成树问题
- 最短路径问题
- 图的连通性
最短路
多源最短路
题目描述
在一个大城市中,环卫工人小C和他的团队负责清理城市的生活垃圾。整个城市被分为了若干个区域,每个区域被划分为m行n列的网格。环卫工人需要将每个居民区的垃圾运输到最近的垃圾回收站。这个任务要求计算所有居民区垃圾送到回收站的最小总距离。
输入
第一行为两个整数m和n,表示网格的行数和列数,其中m和n的范围均为[1,300)。接下来m行表示区域的矩阵布局,每行元素间以空格分隔。网格元素为以下几种:
-1
表示该区域有障碍物,不可通行。0
表示垃圾回收站。1
表示居民区,需要收集垃圾。2
表示空白区域,可以自由通行。
输出
输出一个整数,表示将所有居民区的垃圾送到最近的垃圾回收站所需的最小距离和。如果无法到达垃圾回收站的居民区将被忽略。
样例
1 | 输入: |
解答
以所有的回收站为起点,所有的小区为终点进行搜索。搜索完成后将所有最短路的距离相加即可。如果到达不了就忽略这个小区。
初始化距离数组用来存储从最近的垃圾回收站到每个点的最短距离,初始值为-1表示未访问。
创建队列用于BFS。将所有起点的位置加入队列,并初始化这些位置的距离为0。加入队列是因为需要作为起点来进行BFS。
部分核心代码:
1 | from collections import deque |
BFS
数组搜索
跳跃游戏 Ⅲ
这里有一个非负整数数组 arr
,你最开始位于该数组的起始下标 start
处。当你位于下标 i
处时,你可以跳到 i + arr[i]
或者 i - arr[i]
。
请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。
注意,不管是什么情况下,你都无法跳到数组之外。
示例 1:
1 | 输入:arr = [4,2,3,0,3,1,2], start = 5 |
示例 2:
1 | 输入:arr = [4,2,3,0,3,1,2], start = 0 |
示例 3:
1 | 输入:arr = [3,0,2,1,2], start = 2 |
提示:
1 <= arr.length <= 5 * 10^4
0 <= arr[i] < arr.length
0 <= start < arr.length
1 | class Solution { |
BFS的精髓就在于维护一个队列,保存将要进行BFS的节点,还有一个访问数组,记录节点是否被访问过。
Java中的Queue:
具有队列特性的接口,具有先进先出的特点。所有新元素都插入队列的末尾,移除元素都移除队列的头部
1 | public interface Queue<E> extends Collection<E> { |
DFS版供学习:
1 | class Solution { |
网格图
网格结构比二叉树结构稍微复杂一些,其实是一种简化的图结构。
在二叉树DFS遍历:
1 | void traverse(TreeNode root){ |
二叉树的DFS:访问相邻节点 、 判断base case
root == null 表示root指向的子树为空,不需要再往下遍历了,另一方面可以让后面的root.left和root.right操作不会出现空指针异常。
而对于网格上的格子,将会有四个相邻节点。而网格中DFS的base case就是网格中不需要继续遍历、超出网格范围的格子。
避免重复遍历
网格结构和二叉树最大的不同就在于遍历中可能会遇到遍历过的节点,因为网格结构本质上是一个图,并且是双向的。所以DFS有可能会死循环。
所以就需要标记已经遍历过的格子。
一般用0,1,2来区分。
DFS
主要应用:找连通块、判断是否有环
dfs函数的形参:保存图信息的数组,保存访问信息的数组,开始dfs的行、列
boolean[] visited = new boolean[节点个数];
1971.寻找图中是否存在路径
这里给出的是双向边数组,为了方便进行dfs,一开始考虑创建一个数组存放各边的连接关系。但题解提供的是ArrayList
1 | List<Integer>[] adj = new List[n]; |
与其他数据结构比较:
- 如果使用简单的数组来表示邻接关系,可能会面临以下问题:
- 数组的大小需要预先确定,并且在图的大小动态变化时不太方便。
- 对于稀疏图(即大多数节点之间没有连接),使用数组会浪费大量的空间。
- 使用
Map<Integer, List<Integer>>
也可以表示邻接关系,但可能会稍微复杂一些:
- 在代码实现上可能会更繁琐,需要处理键值对的操作。
- 对于简单的图遍历问题,使用数组的方式可能更加直观和高效。
所以对于边(x,y),只需要 adj[x].add(y)
即可。
797.所有可能的路径
1 | List<List<Integer>> list = new ArrayList<>();//保存全部路径 |
开始的理解是直接 list.add(innerlist);
,但这样会得到错误答案。原因:Java中的对象是通过引用进行传递的,当执行这行代码时,实际上是将innerList的引用添加到了外层的list中。这意味着如果后续对innerList进行修改,那么外层列表中存储的这个引用的对象也会发生变化。所以,我们希望添加到外层列表中的是一个独立的副本,而不是原始列表的引用,这样可以确保在后续对原始列表进行修改时,不会影响已经添加到外层列表中的内容。
因为需要查找的是所有可能的路径,那么就需要进行回溯。
1 | dfs(graph, graph[n][i]); |
移除最后一个节点,以便从另一个相邻节点开始探索。
SystemforSmartLogistics
本文字数: 0 阅读时长 ≈ 1 分钟
斐波那契数列——尾递归
LeetCode贪心篇
前后端分离项目部署
LeetCode数与位篇
本文字数: 0 阅读时长 ≈ 1 分钟