给定一个整数矩阵,请找出一个子矩阵,使得其数字之和等于0.输出答案时,请返回左上数字和右下数字的坐标。

样例

给定矩阵

[
[1 ,5 ,7],
[3 ,7 ,-8],
[4 ,-8 ,9],
]

返回 [(1,1), (2,2)]

挑战

O(n3) 时间复杂度

解题

直接暴露求解,时间复杂度O(N2*M2 )

public class Solution {
/**
* @param matrix an integer matrix
* @return the coordinate of the left-up and right-down number
*/
public int[][] submatrixSum(int[][] matrix) {
// Write your code here
int[][] res = new int[2][2];
int row = matrix.length;
if(row ==0){
return res;
}
int col = matrix[0].length;
if(col ==0){
return res;
}
for(int i = 0;i< row ;i++){
for(int j = 0;j<col;j++){
int sum = matrix[i][j];
res[0][0]=i;
res[0][1]=j;
for(int n=i+1;n<row;n++){
for(int m=j+1;m<col;m++){
sum+=matrix[n][m];
if(sum==0){
res[1][0] = n;
res[1][1] = m;
break;
}
}
}
}
}
return res;
}
}

Java Code

总耗时: 8203 ms

在编程之美上,看到下面的方法:

1.先去(0,0)到(i,j)内的子矩阵的部分和,和存放中点(i,j)

2.根据上面的子矩阵,求解任意两点和为0的子矩阵

如何求解(0,0)到(i,j)内的子矩阵之和

(i-1,j-1) (i-1,j)
(i,j-1) (i,j)

如上图的子矩阵和的矩阵PS

PS[i-1][j-1] PS[i-1][j] PS[i][j-1]是已经求出的子矩阵的和

显然我们知道PS[i][j-1] = PS[i-1][j-1] + A[i][j-1]  这里的A是原始要求的矩阵,A[i][j-1]也就是(i,j-1)点的值

同理:PS[i-1][j] = PS[i-1][j-1] + A[i-1][j]

很显然:PS[i][j] = PS[i-1][j-1] + A[i-1][j] + A[i][j-1] + A[i][j]

将上面的含有A矩阵元素的值消掉就得到下面的矩阵:

PS[i][j] = PS[i][j-1] + PS[i-1][j] - PS[i-1][j-1]

对于第0行和第0类,我单独处理的

PS[0][0] = A[0][0]

第0行:PS[0][j] =PS[0][j-1] + A[0][j]

第0列:PS[i][0] =PS[i-1][0] + A[i][0]

非0行非0列按照上面的迭代求解

在求解任意两点和为0的子矩阵

我们求的是(i,j)到(m,n)的子矩阵

当m 或者n其中一个为0的时候单独考虑

其他情况 m、n都是非0的时候

要考虑 i、j是否是0的情况

如下:

              int sum = 0;
if(i==0 && j==0){
sum = PS[m][n];
}else if(i==0 && j!=0){
sum = PS[m][n] - PS[m][j-1];
}else if(i!=0 && j==0){
sum = PS[m][n] -PS[i-1][n];
}else{
sum = PS[m][n] - PS[i-1][n] - PS[m][j-1] + PS[i-1][j-1];
}

这样计算的最终时间复杂度还是O(N2*M2)

最终程序

public class Solution {
/**
* @param matrix an integer matrix
* @return the coordinate of the left-up and right-down number
*/
public int[][] submatrixSum(int[][] matrix) {
// Write your code here
int[][] res = new int[2][2];
int row = matrix.length;
if(row ==0){
return res;
}
int col = matrix[0].length;
if(col ==0){
return res;
}
// 求解0 0 到i j的部分和
int PS[][] = new int[row][col];
PS[0][0] = matrix[0][0];
for(int i =1;i<row ;i++)
PS[i][0] =PS[i-1][0] + matrix[i][0];
for(int j =1;j<col ;j++)
PS[0][j] =PS[0][j-1] + matrix[0][j];
for(int i =1;i< row;i++){
for(int j=1;j<col;j++){
PS[i][j] = PS[i-1][j] + PS[i][j-1] - PS[i-1][j-1] + matrix[i][j];
}
}
// for(int i =0;i< row;i++){
// for(int j=0;j<col;j++){ // System.out.print(PS[i][j] +" ");
// }
// System.out.println();
// }
//对第0行和第0列单独判断
// 寻找i j 到 m n和为0的数组时间复杂度O(n*n*m*m)
//对第0行和第0列单独判断
// 第0列
for(int i =0;i< row ;i++){
res[0][0] = i;
res[0][1] = 0;
for(int j= i+1;j< row;j++){
if(i==0){
if(PS[j][0]==0){
res[1][0] = j;
res[1][1] = 0;
return res;
}
}
else if(PS[j][0]-PS[i-1][0] ==0){
res[1][0] = j;
res[1][1] = 0;
return res;
}
}
}
// 第0 行
for(int i =0;i< col ;i++){
res[0][0] = 0;
res[0][1] = i;
for(int j= i+1;j< col;j++){
if(i==0){
if(PS[0][j]==0){
res[1][0] = 0;
res[1][1] = j;
return res;
}
}
else if(PS[0][j]-PS[0][i-1] ==0){
res[1][0] = 0;
res[1][1] = j;
return res;
}
}
}
for(int i =0;i<row ;i++){
for(int j=0;j<col;j++){
res[0][0] = i;
res[0][1] = j;
for(int m = i+1;m<row; m++){
for(int n = j+1;n<col;n++){
int sum = 0;
if(i==0 && j==0){
sum = PS[m][n];
}else if(i==0 && j!=0){
sum = PS[m][n] - PS[m][j-1];
}else if(i!=0 && j==0){
sum = PS[m][n] -PS[i-1][n];
}else{
sum = PS[m][n] - PS[i-1][n] - PS[m][j-1] + PS[i-1][j-1];
}
if(sum==0){
res[1][0] = m;
res[1][1] = n;
return res; }
}
}
}
}
return res;
}
}

Java Code

总耗时: 7060 ms

上面参考的是编程之美

在九章的程序中,PS矩阵行列比原始的矩阵行列多1,在边界出来上不用那么复杂

同时在PS矩阵中求解任意两点的子矩阵时候,利用HashMap,对PS矩阵中任意两点的差值判断是否在HashMap中,当存在的时候就是答案了

    public class Solution {
/**
* @param matrix an integer matrix
* @return the coordinate of the left-up and right-down number
*/
public int[][] submatrixSum(int[][] matrix) {
int[][] result = new int[2][2];
int M = matrix.length;
if (M == 0) return result;
int N = matrix[0].length;
if (N == 0) return result;
// pre-compute: sum[i][j] = sum of submatrix [(0, 0), (i, j)]
int[][] sum = new int[M+1][N+1];
for (int j=0; j<=N; ++j) sum[0][j] = 0;
for (int i=1; i<=M; ++i) sum[i][0] = 0;
for (int i=0; i<M; ++i) {
for (int j=0; j<N; ++j)
sum[i+1][j+1] = matrix[i][j] + sum[i+1][j] + sum[i][j+1] - sum[i][j];
}
for (int l=0; l<M; ++l) {
for (int h=l+1; h<=M; ++h) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int j=0; j<=N; ++j) {
int diff = sum[h][j] - sum[l][j];
if (map.containsKey(diff)) {
int k = map.get(diff);
result[0][0] = l; result[0][1] = k;
result[1][0] = h-1; result[1][1] = j-1;
return result;
} else {
map.put(diff, j);
}
}
}
}
return result;
}
}

Java Code

lintcode 中等题:Submatrix sum is 0 和为零的子矩阵的更多相关文章

  1. lintcode 中等题:k Sum ii k数和 II

    题目: k数和 II 给定n个不同的正整数,整数k(1<= k <= n)以及一个目标数字. 在这n个数里面找出K个数,使得这K个数的和等于目标数字,你需要找出所有满足要求的方案. 样例 ...

  2. lintcode 中等题:A + B Problem A + B 问题

    题目: 中等 A + B 问题 给出两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符. 如果 a=1 并且 b=2,返回3 注意 你不需要从输入流读入数据,只需要根据aplusb的两个参数 ...

  3. lintcode 中等题:partition array 数组划分

    题目 数组划分 给出一个整数数组nums和一个整数k.划分数组(即移动数组nums中的元素),使得: 所有小于k的元素移到左边 所有大于等于k的元素移到右边 返回数组划分的位置,即数组中第一个位置i, ...

  4. lintcode 中等题:permutations II 重复数据的全排列

    题目 带重复元素的排列 给出一个具有重复数字的列表,找出列表所有不同的排列. 样例 给出列表 [1,2,2],不同的排列有: [ [1,2,2], [2,1,2], [2,2,1] ] 挑战 使用递归 ...

  5. lintcode 中等题:permutations 全排列

    题目 全排列 给定一个数字列表,返回其所有可能的排列. 您在真实的面试中是否遇到过这个题? Yes 样例 给出一个列表[1,2,3],其全排列为: [ [1,2,3], [1,3,2], [2,1,3 ...

  6. lintcode 中等题:Divide Two Integers 两个数的除法

    题目 两个整数相除 将两个整数相除,要求不使用乘法.除法和 mod 运算符. 如果溢出,返回 2147483647 . 样例 给定被除数 = 100 ,除数 = 9,返回 11 解题  15%的通过率 ...

  7. lintcode 中等题:和大于S的最小子数组

    题目 和大于S的最小子数组 给定一个由 n 个整数组成的数组和一个正整数 s ,请找出该数组中满足其和 ≥ s 的最小长度子数组.如果无解,则返回 -1. 样例 给定数组 [2,3,1,2,4,3]  ...

  8. lintcode 中等题:find the missing number 寻找缺失的数

    题目 寻找缺失的数 给出一个包含 0 .. N 中 N 个数的序列,找出0 .. N 中没有出现在序列中的那个数. 样例 N = 4 且序列为 [0, 1, 3] 时,缺失的数为2. 注意 可以改变序 ...

  9. lintcode 中等题: Implement Trie

    题目 Implement Trie Implement a trie with insert, search, and startsWith methods. 样例   注意 You may assu ...

随机推荐

  1. svn迁移

    一.       VisualSVN服务器迁移 场景:把A服务器备份到B服务器 方法: 1.拷贝A上Repositories文件夹到B上 2.打开B上VisualSVN Server Manager ...

  2. Oracle中排序列中值相同引发的问题(译)

    This queston came up on the Oracle newsgroup a few days ago: 这个问题在Oracle的新闻中心被提出了一段时间: I have a tabl ...

  3. Redis 一:安装篇

    .安装环境,虚拟机 + centos6. PS::前提已经安装了yum的情况下 第一步:安装 mkdir /usr/redis 新建redis目录 cd /usr/redis 进入目录 wget ht ...

  4. 解析php mysql 事务处理回滚操作(附实例)

    其实用PHP来处理mysql的事务回滚并不难,下面小编就详细的为大家介绍一下.相信大家看完之后都知道如何使用 很多新手在进行项目过程中,会碰到这样一种情况,如:论坛扣币项目中,用户支付论坛币的时候如果 ...

  5. RichTextBox 自动滚动到最后

    RichTextBox.AppendText($"[{DateTime.Now.ToString("hh:mm:ss")}] {msg}\n"); RichTe ...

  6. 一道关于比赛胜负的Sql查询题目

    以前做过一道题目,一直没有来得及总结下来.贴图: 记得以前曾经找到了两种方法,今天试了一下,还是可以的,贴出过程: 下面是具体的查询方法: 原来放的是图片,今天又练习了一下,附代码: create T ...

  7. LINQ.CS

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Zdso ...

  8. iOS中使用自定义字体

    1.确定你的项目工程的Resources下有你要用的字体文件(.ttf或者.odf). 2.然后在你的工程的Info.plist文件中新建一行,添加key为:UIAppFonts,类型为Array或D ...

  9. spring中Bean的注入类型

    1.属性注入    即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式.    属性注入要求Bean提供 ...

  10. constructor(构造器)

    当我们创建一个类的时候,如果不自定义构造器,则系统会自动创建一个默认的构造器,也是一个无参构造器用于初始化. 当我们在类的里面创建了自己的构造器,则系统将不会创建默认的构造器,由于需求条件不同,构造器 ...