Java渗流模型(Percolation)运行空指针异常排查求助
渗流模型(Percolation)空指针异常排查与修复
问题现象
运行渗流模型统计代码时抛出空指针异常,错误栈如下:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "edu.princeton.cs.algs4.WeightedQuickUnionUF.find(int)" because "percolation.gridMap" is null at percolation.percolates(percolation.java:93) at PercolationStats.<init>(PercolationStats.java:17) at PercolationStats.main(PercolationStats.java:58)
核心原因分析
- 构造函数未正确初始化核心变量:
percolation类的带参构造函数是空实现,自定义的Percolation(int n)是普通方法(首字母大写、有返回值),调用new percolation(n)时根本不会执行gridMap等变量的初始化逻辑,导致gridMap始终为null。 - 静态变量滥用:
grid、gridMap、openCell等变量被定义为static,多次试验的实例会共享这些变量,既会引发空指针,也会造成数据混乱。 isOpen方法逻辑错误:直接返回grid[current_index],但current_index是上一次open操作的索引,并非当前(row,col)对应的索引,导致判断结果完全错误。PercolationStats变量作用域问题:构造函数内定义了局部result数组,未给类的静态result赋值,后续mean()等方法调用时result为null。open方法参数检查顺序错误:先修改网格状态和并查集,再检查参数是否越界,非法参数会导致无效操作甚至异常。open方法底部关联错误:当row == n时,错误地将当前索引与col关联,而非与bottom虚拟节点关联。
修复方案
1. 修复percolation类构造函数与变量作用域
- 将所有静态变量改为实例变量,确保每个渗流实例独立;
- 实现正确的带参构造函数,初始化
gridMap、grid等核心变量,并加入参数合法性检查。
2. 修正isOpen方法逻辑
根据传入的row和col计算对应的网格索引,再判断是否开放。
3. 调整open方法参数检查顺序
先检查row和col是否越界,非法参数直接抛出异常,再执行后续的开放操作。
4. 修复open方法底部节点关联逻辑
当row == n时,将当前索引与bottom虚拟节点执行union操作。
5. 修正PercolationStats类变量问题
- 将静态
result、trials改为实例变量; - 构造函数开头先执行参数合法性检查;
- 直接给类的
result数组赋值,避免局部变量覆盖。
修正后的代码
percolation.java
import edu.princeton.cs.algs4.WeightedQuickUnionUF; public class percolation { private boolean[] grid; private WeightedQuickUnionUF gridMap; private int openCell; private int top = 0; private int bottom; private int n; // 创建n×n网格,所有站点初始为阻塞状态 public percolation(int n) { if (n <= 0) { throw new IllegalArgumentException("网格大小不能小于或等于0"); } this.n = n; gridMap = new WeightedQuickUnionUF(n * n + 2); grid = new boolean[n * n + 2]; // 默认false,即阻塞 openCell = 0; bottom = n * n + 1; } // 开放指定(row, col)的站点(如果未开放) public void open(int row, int col) { // 先检查参数合法性 if (row < 1 || row > n || col < 1 || col > n) { throw new IllegalArgumentException("索引超出边界"); } int currentIndex = (row - 1) * n + (col - 1); if (grid[currentIndex]) { return; // 已经开放,直接返回 } // 标记站点为开放 grid[currentIndex] = true; openCell++; // 与顶部虚拟节点关联 if (row == 1) { gridMap.union(currentIndex, top); } // 与底部虚拟节点关联 if (row == n) { gridMap.union(currentIndex, bottom); } // 与上方开放站点关联 if (row > 1 && isOpen(row - 1, col)) { gridMap.union(currentIndex, currentIndex - n); } // 与下方开放站点关联 if (row < n && isOpen(row + 1, col)) { gridMap.union(currentIndex, currentIndex + n); } // 与左方开放站点关联 if (col > 1 && isOpen(row, col - 1)) { gridMap.union(currentIndex, currentIndex - 1); } // 与右方开放站点关联 if (col < n && isOpen(row, col + 1)) { gridMap.union(currentIndex, currentIndex + 1); } } // 判断站点(row, col)是否开放 public boolean isOpen(int row, int col) { if (row < 1 || row > n || col < 1 || col > n) { throw new IllegalArgumentException("索引超出边界"); } int index = (row - 1) * n + (col - 1); return grid[index]; } // 判断站点(row, col)是否为满(与顶部连通) public boolean isFull(int row, int col) { if (!isOpen(row, col)) { return false; } int index = (row - 1) * n + (col - 1); return gridMap.find(index) == gridMap.find(top); } // 返回开放站点的数量 public int numberOfOpenSites() { return openCell; } // 判断系统是否渗流(顶部与底部连通) public boolean percolates() { return gridMap.find(top) == gridMap.find(bottom); } }
PercolationStats.java
import edu.princeton.cs.algs4.StdRandom; import edu.princeton.cs.algs4.StdStats; public class PercolationStats { private final double[] result; private final int trials; // 对n×n网格执行trials次独立试验 public PercolationStats(int n, int trials) { // 先检查参数合法性 if (n <= 0 || trials <= 0) { throw new IllegalArgumentException("n和trials必须大于0"); } this.trials = trials; result = new double[trials]; for (int i = 0; i < trials; i++) { percolation per = new percolation(n); while (!per.percolates()) { // 生成1~n范围内的随机行和列 int testRow = StdRandom.uniformInt(1, n + 1); int testCol = StdRandom.uniformInt(1, n + 1); if (!per.isOpen(testRow, testCol)) { per.open(testRow, testCol); } } result[i] = (double) per.numberOfOpenSites() / (n * n); } } // 返回渗流阈值的样本均值 public double mean() { return StdStats.mean(result); } // 返回渗流阈值的样本标准差 public double stddev() { return StdStats.stddev(result); } // 返回95%置信区间的下限 public double confidenceLo() { return mean() - (1.96 * stddev()) / Math.sqrt(trials); } // 返回95%置信区间的上限 public double confidenceHi() { return mean() + (1.96 * stddev()) / Math.sqrt(trials); } // 测试客户端 public static void main(String[] args) { PercolationStats stats = new PercolationStats(50, 10000); System.out.println("均值: " + stats.mean()); System.out.println("标准差: " + stats.stddev()); System.out.println("95%置信区间: [" + stats.confidenceLo() + ", " + stats.confidenceHi() + "]"); } }
内容的提问来源于stack exchange,提问作者DTJ
相关产品推荐
相关产品推荐

