You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

核心原因分析

  1. 构造函数未正确初始化核心变量:percolation类的带参构造函数是空实现,自定义的Percolation(int n)是普通方法(首字母大写、有返回值),调用new percolation(n)时根本不会执行gridMap等变量的初始化逻辑,导致gridMap始终为null。
  2. 静态变量滥用:grid、gridMap、openCell等变量被定义为static,多次试验的实例会共享这些变量,既会引发空指针,也会造成数据混乱。
  3. isOpen方法逻辑错误:直接返回grid[current_index],但current_index是上一次open操作的索引,并非当前(row,col)对应的索引,导致判断结果完全错误。
  4. PercolationStats变量作用域问题:构造函数内定义了局部result数组,未给类的静态result赋值,后续mean()等方法调用时result为null。
  5. open方法参数检查顺序错误:先修改网格状态和并查集,再检查参数是否越界,非法参数会导致无效操作甚至异常。
  6. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:21:03