【LeetCode】
元素和为目标值的子矩阵数量
一, 元素和为目标值的子矩阵数量
给出矩阵 matrix 和目标值 target,返回元素总和等于目标值的非空子矩阵的数量。
子矩阵 x1, y1, x2, y2 是满足 x1 <= x <= x2 且 y1 <= y <= y2 的所有单元 matrix[x][y] 的集合。
如果 (x1, y1, x2, y2) 和 (x1’, y1’, x2’, y2’) 两个子矩阵中部分坐标不同(如:x1 != x1’),那么这两个子矩阵也不同。
示例 1:
输入:matrix = [[0,1,0],[1,1,1],[0,1,0]], target = 0
输出:4
解释:四个只含 0 的 1x1 子矩阵。
示例 2:
输入:matrix = [[1,-1],[-1,1]], target = 0
输出:5
解释:两个 1x2 子矩阵,加上两个 2x1 子矩阵,再加上一个 2x2 子矩阵。
提示:
1 <= matrix.length <= 300
1 <= matrix[0].length <= 300
-1000 <= matrix[i] <= 1000
-10^8 <= target <= 10^8
自己做两天没有做出来 然后参考 LeetCode 上题解思路 这边的记录自己理解过程
二, 解题思路
哈希函数 + 前缀和
- 一行的数前面的和可以使用当前的下标的值保存 记录 matrix[y][x] - matrix[y][x-1]
- 一行中 x到 xx的所有数的和为 matrix[y][x] - matrix[y][xx -1];
- 列的问题就需要记录前面计算的值, 里为下面的计算铺路 下面是 记录的前面列的思路 hash 记录前面的值
难点在前缀和的计算下面是我的理解的流程
累加前面的数总和 方便矩阵的高度小 计算大小
1: x 到 xx 距离是 4 时的变化
####
#### ####
#### #### ####
#### #### #### ####
2: x 到 xx的距离是 3 时的变化
###
### ###
### ### ###
### ### ### ###
3: x 到 xx的距离是 2 时的变化
##
## ##
## ## ##
## ## ## ##
4: x到xx的距离是1时的变化
#
# #
# # #
# # # #
上面高度不停变大的
三, 哈希函数+ 前缀和的解题代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* 哈希节点
*/
typedef struct table_entry
{
unsigned long key;
int value;
struct table_entry * next;
} table_entry;
/**
* 哈希表的信息
*/
typedef struct hash
{
table_entry ** table; //哈希表 指针数组
unsigned long size; //哈希表的大小
unsigned long sizemask; //哈希因子
unsigned long used; //已经使用的哈希表的个数
}hash_map;
#define HASH_INIT_SIZE 128
/**
* 哈希表初始化
* @return
*/
hash_map* init_hash()
{
hash_map *map_ptr = malloc(sizeof( hash_map));
if (!map_ptr)
{
return NULL;
}
table_entry ** table_ptr = malloc(sizeof(struct table_entry*) * HASH_INIT_SIZE);
if (!table_ptr)
{
free(map_ptr);
return NULL;
}
for (int i = 0; i < HASH_INIT_SIZE; ++i)
{
table_ptr[i] = NULL;
}
map_ptr->size = HASH_INIT_SIZE;
map_ptr->sizemask = HASH_INIT_SIZE -1;
map_ptr->used = 0;
map_ptr->table = table_ptr;
return map_ptr;
}
/**
* 哈希函数
* @param map_ptr
* @param key
* @return
*/
unsigned long get_key(hash_map * map_ptr, unsigned long key)
{
if (!map_ptr)
{
return 0;
}
return key % map_ptr->sizemask;
}
/**
* hashmap 扩容
* @param map_ptr
* @return
*/
hash_map * resize_hash_map(hash_map *map_ptr)
{
map_ptr->size = map_ptr->size * 2;
map_ptr->sizemask = map_ptr->size -1;
table_entry** new_table_ptr = malloc( sizeof(struct table_entry*) * map_ptr->size);
if (!new_table_ptr)
{
return NULL;
}
//从新布局hashmap
for (int i = 0; i < (map_ptr->size/2); ++i)
{
table_entry * old_table_ptr = map_ptr->table[i];
while (old_table_ptr)
{
unsigned long key = get_key(map_ptr, (unsigned long)old_table_ptr->key);
table_entry * old_next_table_ptr = old_table_ptr->next;
if (new_table_ptr[key])
{
old_table_ptr->next = new_table_ptr[key];
}
else
{
old_table_ptr->next = NULL;
}
new_table_ptr[key] = old_table_ptr;
old_table_ptr = old_next_table_ptr;
}
}
if (map_ptr->table)
{
free(map_ptr->table);
map_ptr->table = NULL;
}
if (new_table_ptr)
{
map_ptr->table = new_table_ptr;
}
return map_ptr;
}
/**
* 查询
* @param map_ptr
* @param key
* @return
*/
int get_key_value(hash_map * map_ptr, unsigned long key)
{
unsigned long hash = get_key(map_ptr, key);
table_entry * cur_entry = map_ptr->table[hash];
while (cur_entry)
{
// 判断key是否相等
if (key == cur_entry->key)
{
return (cur_entry->value);
}
cur_entry = cur_entry->next;
}
return 0;
}
/**
* 插入
* @param map_ptr
* @param key
* @return
*/
table_entry * insert_entry(hash_map * map_ptr, unsigned long key)
{
unsigned long hash = get_key(map_ptr, key);
table_entry * cur_entry = map_ptr->table[hash];
while (cur_entry)
{
// 判断key是否相等
if (key == (unsigned long)(cur_entry->key))
{
return cur_entry;
}
cur_entry = cur_entry->next;
}
if (!cur_entry)
{
table_entry * entry_ptr = malloc(sizeof(struct table_entry));
if (!entry_ptr)
{
printf("alloc failed \n");
return NULL;
}
entry_ptr->key = key;
entry_ptr->value = 0;
entry_ptr->next = NULL;
if (map_ptr->table[hash])
{
entry_ptr->next = map_ptr->table[hash];
}
++map_ptr->used;
{
map_ptr->table[hash] = entry_ptr;
}
}
else
{
printf("[insert error]\n");
}
return map_ptr->table[hash];
}
/**
* 哈希map插入
* @param map_ptr
* @param key
* @param value
* @return
*/
table_entry* make_insert(hash_map * map_ptr, unsigned long key, int value)
{
unsigned long hash = get_key(map_ptr, key);
table_entry * cur_entry = map_ptr->table[hash];
while (cur_entry)
{
// 判断key是否相等
if (key == (unsigned long)(cur_entry->key))
{
break;
}
cur_entry = cur_entry->next;
}
if (!cur_entry)
{
//没有找到的处理
//看需不需要扩容
if (map_ptr->size <= map_ptr->used)
{
hash_map * new_map_ptr = resize_hash_map(map_ptr);
if (!new_map_ptr)
{
printf("[hash_map resize error ]\n");
return NULL;
}
}
cur_entry = insert_entry(map_ptr, key);
if (!cur_entry)
{
printf("entry alloc failed]\n");
return NULL;
}
}
// set value
cur_entry->value = value;
return cur_entry;
}
void hash_map_destroy(hash_map* map_ptr)
{
if (!map_ptr)
{
return;
}
if (!map_ptr->table)
{
free(map_ptr);
map_ptr = NULL;
return;
}
for (int i = 0; i < map_ptr->size; ++i)
{
table_entry * entry_ptr = map_ptr->table[i];
while (entry_ptr) //释放哈希表
{
table_entry * next_entry_ptr = entry_ptr->next;
free(entry_ptr);
entry_ptr = next_entry_ptr;
}
}
free(map_ptr->table);
map_ptr->table = NULL;
free(map_ptr);
map_ptr = NULL;
}
/**
* TODO@chensong 2020-11-09 warr 超出时间限制 我也是头大啊啊
* @param matrix
* @param matrixSize
* @param matrixColSize
* @param target
* @return
*/
// int numSubmatrixSumTarget(int** matrix, int matrixSize, int* matrixColSize, int target){
// int matrix_nums = 0;
// if (!matrix)
// {
// return matrix_nums;
// }
// for (int i = 0; i < matrixSize; ++i)
// {
// for (int j = 0; j < matrixColSize[i]; ++j)
// {
// for (int x = j; x>=0; --x)
// {
// for (int y = i; y>=0; --y)
// {
// int diff = 0;
// for (int yy = y; yy <= i; ++yy)
// {
// for (int xx = x; xx <= j; ++xx)
// {
// diff += matrix[yy][xx];
// }
// }
// if (diff == target )
// {
// ++matrix_nums;
// }
// }
// }
// }
// }
// return matrix_nums;
// }
/**
* 有符号问题 hash --> 是错误的了
*/
int numSubmatrixSumTargethash(int** matrix, int matrixSize, int* matrixColSize, int target)
{
int matrix_nums = 0;
// 把每一行的值累加起来 到当前x的数中
//比如 j 到 i 之和 就是 = matrix[y][i] - matrix[y][y-1]
for (int y = 0; y < matrixSize; ++y)
{
for (int x = 1; x < matrixColSize[y]; ++x)
{
matrix[y][x] +=matrix[y][x -1]; //
}
}
unsigned long * hash = NULL;
unsigned long hash_size = 16;
// hash_map * hash_map_ptr = NULL;
//x [x - xx]
//y [0 - matrixSize]
int colsize = matrixColSize[0];
for (int x = 0; x < colsize; ++x)
{
for (int xx = x; xx < colsize; ++xx)
{
if (!hash)
{
hash = malloc(sizeof(unsigned long)* hash_size);
}
memset(hash, 0, sizeof(unsigned long)*hash_size);
hash[0] = 1;
int sum = 0;
for (int y = 0; y < matrixSize; ++y)
{
if (x>0)
{
sum += matrix[y][xx] - matrix[y][x-1];
}
else
{
sum += matrix[y][xx];
}
if (((sum - target)>= 0) &&(sum - target) < hash_size)
{
matrix_nums += hash[sum-target];
}
if (sum >= hash_size)
{
hash_size *=2;
hash = realloc(hash, sizeof(unsigned long) *hash_size);
}
++hash[sum];
}
}
}
return matrix_nums;
}
/**
*
* @param matrix
* @param matrixSize
* @param matrixColSize
* @param target
* @return
*/
int numSubmatrixSumTarget(int** matrix, int matrixSize, int* matrixColSize, int target)
{
int matrix_nums = 0;
// 把每一行的值累加起来 到当前x的数中
//比如 j 到 i 之和 就是 = matrix[y][i] - matrix[y][y-1]
for (int y = 0; y < matrixSize; ++y)
{
for (int x = 1; x < matrixColSize[y]; ++x)
{
matrix[y][x] +=matrix[y][x -1]; //
}
}
hash_map * hash_map_ptr = NULL ;
//x [x - xx]
//y [0 - matrixSize]
int colsize = matrixColSize[0];
for (int x = 0; x < colsize; ++x)
{
for (int xx = x; xx < colsize; ++xx)
{
hash_map_destroy(hash_map_ptr);
hash_map_ptr = init_hash();
make_insert(hash_map_ptr, 0, 1);
int sum = 0;
for (int y = 0; y < matrixSize; ++y)
{
if (x>0)
{
sum += matrix[y][xx] - matrix[y][x-1];
}
else
{
sum += matrix[y][xx];
}
matrix_nums += get_key_value(hash_map_ptr, sum-target);
//累加前面的数总和 方便矩阵的高度小 计算大小
/**
* 例如: 统一矩阵的高度为 4
* 例1: x 到 xx 距离是 4 时的变化
* ####
* #### ####
* #### #### ####
* #### #### #### ####
* 上面高度不停变大的
*
* 例2: x 到 xx的距离是 3 时的变化
*
* ###
* ### ###
* ### ### ###
* ### ### ### ###
*
*
*
* 例3: x 到 xx的距离是 2 时的变化
*
* ##
* ## ##
* ## ## ##
* ## ## ## ##
*/
make_insert(hash_map_ptr, sum , get_key_value(hash_map_ptr, sum)+1);
}
}
}
hash_map_destroy(hash_map_ptr);
return matrix_nums;
}
void test_hash_map()
{
printf("test hash map \n");
hash_map * map_ptr = init_hash();
if (!map_ptr)
{
printf("init map failed\n");
return ;
}
else
{
printf("init map ok !!!\n");
}
for (int i = 0; i < 1000; ++i)
{
make_insert(map_ptr, i * 22/*这边乘以22是为了哈希冲突*/, i);
}
unsigned long hash_collisions = 0; //哈希表的冲突表的个数
for (unsigned long j = 0; j < map_ptr->size; ++j)
{
if (map_ptr->table[j])
{
if (map_ptr->table[j]->next)
{
++hash_collisions;
}
}
printf("j = %lu key = %d\n ", j, get_key_value(map_ptr, j*22));
}
printf("hash table size = %lu, use table size = %lu, hash_collisions = %lu\n", map_ptr->size, map_ptr->used, hash_collisions);
hash_map_destroy(map_ptr);
printf("hash map destroy ok\n");
}
四, 总结
总结: 前缀和比较难理解 也是比较重要的