⭐算法入门⭐《哈希表》中等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=0n1nums[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[i1]  第 i i i 个数到 第 j j j 个数,一共 j − i + 1 j - i + 1 ji+1 个数,所以其中 0 0 0 的个数应该是: j − i + 1 − ( s [ j ] − s [ i − 1 ] ) j - i + 1 - (s[j] - s[i-1]) ji+1(s[j]s[i1])  现在要求 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[i1]=ji+1(s[j]s[i1]) 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] i2×s[i1]=j+12×s[j]   等式左边定义为函数 f [ i ] = i − 2 × s [ i − 1 ] f[i] = i - 2 \times s[i-1] f[i]=i2×s[i1]
  等式右边定义为函数 g [ j ] = j + 1 − 2 × s [ j ] g[j] = j + 1 - 2 \times s[j] g[j]=j+12×s[j]
  这时候,我们知道 0 ≤ i ≤ j < n 0 \le i \le j \lt n 0ij<n,所以可以枚举 0 ≤ j < n 0 \le j \lt n 0j<n,并且向哈希表插入 f [ j ] f[j] f[j],然后从哈希表中查找值为 g [ j ] g[j] g[j] 的,然后取最大的 j − i + 1 j - i + 1 ji+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) (1) 区间本身不大;
   ( 2 ) (2) (2) 左区间不是从 0 开始的;
这时候比较方便且高效的做法就是直接映射到一个经过偏移的数组中。


  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值