问题描述
给定一个不包含重复元素的N行M列二维矩阵,求矩阵的极小值。极小值的定义如下:若一个值是极小值,四连通方向上的的不越界的邻居都比它大。
思路
二维比较难想,我们从一维说起。
对于一维数组来说,如何寻找极小值?
二分法,找到中间位置x,如果x-1处的元素比x+1处的元素小,那么0~x-1之间必然存在一个极小值,从而缩小了范围。
升到二维,这个问题等价于。在平原上,寻找一个坑。这其实非常简单,我们只需要任选一点,从这个点出发始终朝着低处走,一定能够找到极小值点。
升到二维,首先也要想到分治法,降低复杂度。
对于矩阵较长的那个轴,二分之,切一刀。在刀痕处寻找极小值,极小值所在的那一侧必然是包含极小值的。
总之,这个问题只有一个思路:目前发现的最小值所在的区域一定包含极小值。关键在于划分区域的时候要严格保证最小值不会流出区域。
代码
import java.util.Random;
class Main {
int[][] a;
Random r = new Random();
void generate() {
int rows = r.nextInt(10) + 5;
int cols = r.nextInt(10) + 5;
a = new int[rows][cols];
int b[] = new int[rows * cols];
for (int i = 0; i < b.length; i++) b[i] = i;
for (int i = 0; i < b.length; i++) {
int next = r.nextInt(b.length - i) + i;
int temp = b[i];
b[i] = b[next];
b[next] = temp;
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
a[i][j] = b[i * cols + j];
}
}
}
class Pos {
int x, y;
int value;
Pos(int x, int y, int value) {
this.x = x;
this.y = y;
this.value = value;
}
}
boolean legal(int x, int y) {
return x >= 0 && y >= 0 && x < a.length && y < a[0].length;
}
int dir[][] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
boolean isMinimum(int i, int j) {
for (int k = 0; k < dir.length; k++) {
int x = i + dir[k][0];
int y = j + dir[k][1];
if (legal(x, y)) {
if (a[x][y] < a[i][j]) {
return false;
}
}
}
return true;
}
Pos good(int fx, int fy, int tx, int ty, Pos min) {
if (fx + 1 == tx && fy + 1 == ty) return new Pos(fx, fy, a[fx][fy]);
if (tx - fx < ty - fy) {//二分长轴
int my = (ty + fy) >> 1;
for (int i = fx; i < tx; i++) {
if (a[i][my] < min.value) {
min.x = i;
min.y = my;
min.value = a[i][my];
}
}
int minx = min.x, miny = min.y;
for (int i = 0; i < dir.length; i++) {
int x = minx + dir[i][0], y = miny + dir[i][1];
if (legal(x, y) && a[x][y] < min.value) {
min.x = x;
min.y = y;
min.value = a[x][y];
}
}
if (min.y >= my) {
return good(fx, my, tx, ty, min);
} else {
return good(fx, fy, tx, my, min);
}
} else {
int mx = (tx + fx) >> 1;
for (int i = fy; i < ty; i++) {
if (a[mx][i] < min.value) {
min.x = mx;
min.y = i;
min.value = a[mx][i];
}
}
int minx = min.x, miny = min.y;
for (int i = 0; i < dir.length; i++) {
int x = minx + dir[i][0];
int y = miny + dir[i][1];
if (legal(x, y) && a[x][y] < min.value) {
min.x = x;
min.y = y;
min.value = a[x][y];
}
}
if (min.x >= mx) {
return good(mx, fy, tx, ty, min);
} else {
return good(fx, fy, mx, ty, min);
}
}
}
Main() {
for (int t = 0; t < 100; t++) {
generate();
Pos p = good(0, 0, a.length, a[0].length, new Pos(-1, -1, Integer.MAX_VALUE));
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
System.out.printf(String.format("%4d", a[i][j]));
}
System.out.println();
}
System.out.println(p.x + " " + p.y + " " + p.value);
if (!isMinimum(p.x, p.y)) {
throw new RuntimeException("error");
}
}
}
public static void main(String[] args) {
new Main();
}
}