⭐算法入门⭐《哈希表》中等01 —— LeetCode 525. 连续数组
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了⭐算法入门⭐《哈希表》中等01 —— LeetCode 525. 连续数组相关的知识,希望对你有一定的参考价值。
🙉饭不食,水不饮,题必须刷🙉
C语言免费动漫教程,和我一起打卡! 🌞《光天化日学C语言》🌞
LeetCode 太难?先看简单题! 🧡《C语言入门100例》🧡
数据结构难?不存在的! 🌳《画解数据结构》🌳
LeetCode 太简单?算法学起来! 🌌《夜深人静写算法》🌌
一、题目
1、题目描述
给定一个长度不会超过 1 0 5 10^5 105 的二进制数组
nums
, 找到含有相同数量的 0 0 0 和 1 1 1 的最长连续子数组,并返回该子数组的长度。。
样例输入:nums = [0,1]
样例输出:2
2、基础框架
- C语言版本 给出的基础框架代码如下:
int findMaxLength(int* nums, int numsSize){
}
3、原题链接
( 1 ) (1) (1) LeetCode 525. 连续数组
( 2 ) (2) (2) 剑指 Offer II 011. 0 和 1 个数相同的子数组
二、解题报告
1、思路分析
考虑数组前
n
n
n 个数中 1 的个数为
s
[
n
]
=
∑
i
=
0
n
−
1
n
u
m
s
[
i
]
s[n] = \\sum_{i=0}^{n-1} nums[i]
s[n]=∑i=0n−1nums[i],且
s
[
−
1
]
=
0
s[-1] = 0
s[−1]=0,根据部分和的原理,对于 第
i
i
i 个数到第
j
j
j 个数中
1
1
1 的个数就是
s
[
j
]
−
s
[
i
−
1
]
s[j] - s[i-1]
s[j]−s[i−1] 第
i
i
i 个数到 第
j
j
j 个数,一共
j
−
i
+
1
j - i + 1
j−i+1 个数,所以其中
0
0
0 的个数应该是:
j
−
i
+
1
−
(
s
[
j
]
−
s
[
i
−
1
]
)
j - i + 1 - (s[j] - s[i-1])
j−i+1−(s[j]−s[i−1]) 现在要求
0
0
0 和
1
1
1 的个数相等,就是需要满足如下等式:
s
[
j
]
−
s
[
i
−
1
]
=
j
−
i
+
1
−
(
s
[
j
]
−
s
[
i
−
1
]
)
s[j] - s[i-1] = j - i + 1 - (s[j] - s[i-1])
s[j]−s[i−1]=j−i+1−(s[j]−s[i−1]) 将
i
i
i 和
j
j
j 分别归到等式两边,得到:
i
−
2
×
s
[
i
−
1
]
=
j
+
1
−
2
×
s
[
j
]
i - 2 \\times s[i-1] = j + 1 - 2 \\times s[j]
i−2×s[i−1]=j+1−2×s[j] 等式左边定义为函数
f
[
i
]
=
i
−
2
×
s
[
i
−
1
]
f[i] = i - 2 \\times s[i-1]
f[i]=i−2×s[i−1]
等式右边定义为函数
g
[
j
]
=
j
+
1
−
2
×
s
[
j
]
g[j] = j + 1 - 2 \\times s[j]
g[j]=j+1−2×s[j]
这时候,我们知道
0
≤
i
≤
j
<
n
0 \\le i \\le j \\lt n
0≤i≤j<n,所以可以枚举
0
≤
j
<
n
0 \\le j \\lt n
0≤j<n,并且向哈希表插入
f
[
j
]
f[j]
f[j],然后从哈希表中查找值为
g
[
j
]
g[j]
g[j] 的,然后取最大的
j
−
i
+
1
j - i + 1
j−i+1 就是答案了。
观察到
f
[
i
]
f[i]
f[i] 和
g
[
i
]
g[i]
g[i] d 值,其实都是在
[
−
n
,
0
]
[-n, 0]
[−n,0] 之间,哈希表无须解决冲突,直接采用一个偏移了 100000 下标的数组模拟即可。
2、时间复杂度
- 哈希表的插入和查找的均摊时间复杂度为 O ( 1 ) O(1) O(1),所以总的时间复杂度为 O ( n ) O(n) O(n)。
3、代码详解
/******************** 哈希表 偏移法 ********************/
#define maxn ((1<<17)-1)
#define DataType int
#define Boolean int
#define NULLKEY (maxn+maxn) /* 空槽标记不能用-1,会导致正常值也为-1的情况*/
#define Base 100002
typedef struct {
DataType data[maxn + 1];
}HashTable;
void HashInit(HashTable *ht) {
int i;
for(i = 0; i < maxn + 1; ++i) {
ht->data[i] = NULLKEY;
}
}
int HashInsert(HashTable *ht, DataType key) {
int addr = key + Base;
ht->data[addr] = key;
return addr;
}
Boolean HashSearchKey(HashTable *ht, DataType key, int *addr) {
*addr = key + Base;
return ht->data[*addr] == key;
}
/******************** 哈希表 便宜法 ********************/
int s[maxn];
int pos[maxn+1];
int S(int idx) { // (1)
if(idx == -1)
return 0;
return s[idx];
}
int f(int x) { // (2)
return x - 2 * S(x-1);
}
int g(int x) { // (3)
return x + 1 - 2 * S(x);
}
HashTable ht;
int findMaxLength(int* nums, int numsSize){
int i, p, key, len, maxlen;
Boolean find;
HashInit( &ht ); // (4)
for(i = 0; i < numsSize; ++i) {
s[i] = nums[i];
if(i)
s[i] += s[i-1]; // (5)
}
maxlen = 0;
for(i = 0; i < numsSize; ++i) {
key = f(i);
find = HashSearchKey( &ht, key, &p );// (6)
if(!find) { // (7)
p = HashInsert( &ht, key ); // (8)
pos[p] = i; // (9)
}
key = g(i); // (10)
find = HashSearchKey( &ht, key, &p );// (11)
if(find) {
len = i - pos[p] + 1; // (12)
if(len > maxlen) {
maxlen = len;
}
}
}
return maxlen;
}
- ( 1 ) (1) (1) 由于数组下标可能为负数,所以这里 前缀和 需要抽象成函数;
- ( 2 ) (2) (2) 等式左边的部分 f [ i ] f[i] f[i];
- ( 3 ) (3) (3) 等式右边的部分 g [ i ] g[i] g[i];
- ( 4 ) (4) (4) 创建哈希表;
- ( 5 ) (5) (5) 计算前缀和;
- ( 6 ) (6) (6) 找到 f ( i ) f(i) f(i) 的一个最先出现的位置;
- ( 7 ) (7) (7) 如果没有,才执行插入操作;
- ( 8 ) − ( 9 ) (8) - (9) (8)−(9) 获取插入的位置,记录反向所以为 i i i;
- ( 10 ) (10) (10) 计算 g ( i ) g(i) g(i);
- ( 11 ) (11) (11) 去找之前有没有 f ( . . . ) f(...) f(...) 满足和 g ( i ) g(i) g(i) 相等的;
- ( 12 ) (12) (12) 一旦找到,位置一定存在 p p p 上,直接计算长度;
三、本题小知识
在进行哈希映射的时候,如果能够事先知道值域的范围在某个区间内,这个区间满足以下两个特征:
( 1 ) (1) ⭐算法入门⭐《哈希表》中等03 —— LeetCode 380. O 时间插入删除和获取随机元素⭐算法入门⭐《哈希表》中等04 —— LeetCode 347. 前 K 个高频元素
⭐算法入门⭐《哈希表》中等05 —— LeetCode 215. 数组中的第K个最大元素