⭐算法入门⭐《哈希表》中等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=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) ⭐算法入门⭐《哈希表》中等03 —— LeetCode 380. O 时间插入删除和获取随机元素

⭐算法入门⭐《哈希表》中等04 —— LeetCode 347. 前 K 个高频元素

解题报告力扣 第 280 场周赛

⭐算法入门⭐《哈希表》中等05 —— LeetCode 215. 数组中的第K个最大元素

⭐算法入门⭐《二叉树》中等01 —— LeetCode 114. 二叉树展开为链表

⭐算法入门⭐《哈希表》困难01 —— LeetCode 76. 最小覆盖子串