饭不食,水不饮,题必须刷
C语言免费动漫教程,和我一起打卡! 《光天化日学C语言》
LeetCode 太难?先看简单题! 《C语言入门100例》
数据结构难?不存在的! 《画解数据结构》
LeetCode 太简单?算法学起来! 《夜深人静写算法》
给定一个长度不会超过 1 0 5 10^5 105 的二进制数组
nums
, 找到含有相同数量的 0 0 0 和 1 1 1 的最长连续子数组,并返回该子数组的长度。。
样例输入:nums = [0,1]
样例输出:2
int findMaxLength(int* nums, int numsSize){
}
( 1 ) (1) (1) LeetCode 525. 连续数组
( 2 ) (2) (2) 剑指 Offer II 011. 0 和 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 下标的数组模拟即可。
/******************** 哈希表 偏移法 ********************/
#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) 左区间不是从 0 开始的;
这时候比较方便且高效的做法就是直接映射到一个经过偏移的数组中。