哈希表

哈希表

  1. 705.设计哈希集合(模版,开链表)

题解

这是开链表(链表形结构)。

typedef struct LinkedList
{
    struct LinkedList *next;
    int key;
    // value,key-value 对。
} ListNode;

typedef struct {
    ListNode *head;
} MyHashSet;

const int size=9973;
ListNode *init()
{
    ListNode *Node=(ListNode *) malloc(sizeof(ListNode));
    Node->key=0;
    Node->next=NULL;
    return Node;
}
void delete(ListNode *head)
{
    if(head==NULL) return;
    ListNode *delNode=head,*delNext=delNode->next;
    while(delNext!=NULL)
    {
        free(delNode);
        delNode=delNext;
        if(delNext!=NULL) delNext=delNext->next;
    }
}
// 创建散列表。
MyHashSet *myHashSetCreate()
{
    MyHashSet *obj=(MyHashSet *) malloc(sizeof(MyHashSet)*size);
    for(int i=0;i<size;i++)
        obj[i].head=NULL;
    return obj;
}

// 添加元素。
void myHashSetAdd(MyHashSet *obj, int key)
{
    int realDst=key % size;
    ListNode *storeDst;
    // 头节点为空,现行处理头节点。
    if(obj[realDst].head==NULL)
    {
        obj[realDst].head=init();
        storeDst=obj[realDst].head;
        storeDst->key=key;
        return;
    }
    //头节点非空,移到链表末尾。
    storeDst=obj[realDst].head;
    if(storeDst->key==key) return;
    while(storeDst->next != NULL)
    {
        storeDst=storeDst->next;
        if(storeDst->key==key) return;
    }
    storeDst->next=init();
    storeDst=storeDst->next;
    storeDst->key=key;
}

void myHashSetRemove(MyHashSet *obj, int key)
{
    int realDst=key % size;
    if(obj[realDst].head==NULL) return;
    ListNode *removeDst=obj[realDst].head;
    // 1. 处理是头节点的情况。
    if(removeDst->key == key)
    {
        obj[realDst].head=removeDst->next;
        free(removeDst);
        removeDst=NULL;
        return;
    }
    // 2. 处理中间节点。
    ListNode *removePre=removeDst;
    while(removeDst!=NULL)
    {
        if(removeDst->key==key)
            break;
        else
        {
            removePre=removeDst;
            removeDst=removeDst->next;
        }
    }
    if(removeDst==NULL) return; // 没有可以删除的,直接返回。
    removePre->next=removeDst->next;
    free(removeDst);
    removeDst=NULL;
    removePre=NULL;
}
// 判断key是否存在。
bool myHashSetContains(MyHashSet *obj, int key)
{
    int checkDst=key % size;
    if(obj[checkDst].head==NULL) return false;
    ListNode *check=obj[checkDst].head;
    while(check!=NULL)
    {
        if(check->key==key) return true;
        check=check->next;
    }
    return false;
}

void myHashSetFree(MyHashSet *obj)
{
    for(int i=0;i<size;i++)
    {
        if(obj[i].head==NULL) continue;
        delete(obj[i].head);
    }
}
/**
 * Your MyHashSet struct will be instantiated and called as such:
 * MyHashSet* obj = myHashSetCreate();
 * myHashSetAdd(obj, key);
 
 * myHashSetRemove(obj, key);
 
 * bool param_3 = myHashSetContains(obj, key);
 
 * myHashSetFree(obj);
*/
  1. 706.设计哈希映射(模版,开链表)

题解

typedef struct LinkedList
{
    struct LinkedList *next;
    int key;
    int value;
} ListNode;

typedef struct {
    ListNode *head;
} MyHashMap;

const int size=9973;
ListNode *init()
{
    ListNode *Node=(ListNode *) malloc(sizeof(ListNode));
    Node->key=0;
    Node->next=NULL;
    return Node;
}
void delete(ListNode *head)
{
    if(head==NULL) return;
    ListNode *delNode=head,*delNext=delNode->next;
    while(delNext!=NULL)
    {
        free(delNode);
        delNode=delNext;
        if(delNext!=NULL) delNext=delNext->next;
    }
}
// 创建散列表。
MyHashMap *myHashMapCreate()
{
    MyHashMap *obj=(MyHashMap *) malloc(sizeof(MyHashMap)*size);
    for(int i=0;i<size;i++)
        obj[i].head=NULL;
    return obj;
}

// 添加元素。
void myHashMapPut(MyHashMap *obj, int key, int value)
{
    int realDst=key % size;
    ListNode *storeDst;
    // 头节点为空,现行处理头节点。
    if(obj[realDst].head==NULL)
    {
        obj[realDst].head=init();
        storeDst=obj[realDst].head;
        storeDst->key=key;
        storeDst->value=value;
        return;
    }
    //头节点非空,移到链表末尾。
    storeDst=obj[realDst].head;
    if(storeDst->key==key)
    {
        storeDst->value=value;
        return;
    }
    while(storeDst->next != NULL)
    {
        storeDst=storeDst->next;
        if(storeDst->key==key)
        {
            storeDst->value=value;
            return;
        }
    }
    storeDst->next=init();
    storeDst=storeDst->next;
    storeDst->key=key;
    storeDst->value=value;
}

void myHashMapRemove(MyHashMap *obj, int key)
{
    int realDst=key % size;
    if(obj[realDst].head==NULL) return;
    ListNode *removeDst=obj[realDst].head;
    // 1. 处理是头节点的情况。
    if(removeDst->key == key)
    {
        obj[realDst].head=removeDst->next;
        free(removeDst);
        removeDst=NULL;
        return;
    }
    // 2. 处理中间节点。
    ListNode *removePre=removeDst;
    while(removeDst!=NULL)
    {
        if(removeDst->key==key)
            break;
        else
        {
            removePre=removeDst;
            removeDst=removeDst->next;
        }
    }
    if(removeDst==NULL) return; // 没有可以删除的,直接返回。
    removePre->next=removeDst->next;
    free(removeDst);
    removeDst=NULL;
    removePre=NULL;
}
// 判断key是否存在。
int myHashMapGet(MyHashMap *obj, int key)
{
    int checkDst=key % size;
    if(obj[checkDst].head==NULL) return -1;
    ListNode *check=obj[checkDst].head;
    while(check!=NULL)
    {
        if(check->key==key) return check->value;
        check=check->next;
    }
    return -1;
}

void myHashMapFree(MyHashMap *obj)
{
    for(int i=0;i<size;i++)
    {
        if(obj[i].head==NULL) continue;
        delete(obj[i].head);
    }
}
  1. 217.存在重复元素

题解

用自己的模版:

const int size=99991; // 哈希表的动态分配大小,最好是质数。
typedef struct LinkedList
{
    struct LinkedList *next;
    int key;
    // value,key-value 对。
} ListNode;

// 定义散列表。散列表存储的是相同映射的头节点。
typedef struct HashSet
{
    ListNode *head;
}MyHashSet;

ListNode *init()
{
    ListNode *Node=(ListNode *) malloc(sizeof(ListNode));
    Node->key=0;
    Node->next=NULL;
    return Node;
}
void delete(ListNode *head)
{
    if(head==NULL) return;
    ListNode *delNode=head,*delNext=delNode->next;
    while(delNext!=NULL)
    {
        free(delNode);
        delNode=delNext;
        if(delNext) delNext=delNext->next;
    }
}


// 创建散列表。
MyHashSet *myHashSetCreate()
{
    MyHashSet *obj=(MyHashSet *) malloc(sizeof(MyHashSet)*size);
    for(int i=0;i<size;i++)
        obj[i].head=NULL;
    return obj;
}

// 添加元素。
void myHashSetAdd(MyHashSet *obj, int key)
{
    int realDst=abs(key % size);
    ListNode *storeDst;
    // 头节点为空,现行处理头节点。
    if(obj[realDst].head==NULL)
    {
        obj[realDst].head=init();
        storeDst=obj[realDst].head;
        storeDst->key=key;
        return;
    }
    //头节点非空,移到链表末尾。
    storeDst=obj[realDst].head;
    if(storeDst->key==key) return;
    while(storeDst->next != NULL)
    {
        if(storeDst->key==key) return;
        storeDst=storeDst->next;
    }
    storeDst->next=init();
    storeDst=storeDst->next;
    storeDst->key=key;
}

void myHashSetRemove(MyHashSet *obj, int key)
{
    int realDst=abs(key % size);
    if(obj[realDst].head==NULL) return;
    ListNode *removeDst=obj[realDst].head;
    // 1. 处理是头节点的情况。
    if(removeDst->key == key)
    {
        obj[realDst].head=removeDst->next;
        free(removeDst);
        removeDst=NULL;
        return;
    }
    // 2. 处理中间节点。
    ListNode *removePre=removeDst;
    while(removeDst!=NULL)
    {
        if(removeDst->key==key)
            break;
        else
        {
            removePre=removeDst;
            removeDst=removeDst->next;
        }
    }
    if(removeDst==NULL) return; // 没有可以删除的,直接返回。
    removePre->next=removeDst->next;
    free(removeDst);
    removeDst=NULL;
    removePre=NULL;
}
// 判断key是否存在。
bool myHashSetContains(MyHashSet *obj, int key)
{
    int checkDst=abs(key % size);
    if(obj[checkDst].head==NULL) return false;
    ListNode *check=obj[checkDst].head;
    while(check!=NULL)
    {
        if(check->key==key) return true;
        check=check->next;
    }
    return false;
}

void myHashSetFree(MyHashSet *obj)
{
    for(int i=0;i<size;i++)
    {
        if(obj[i].head==NULL) continue;
        delete(obj[i].head);
    }
}


bool containsDuplicate(int* nums, int numsSize) {
    MyHashSet *obj=myHashSetCreate();
    bool flag=false;
    for(int i=0;i<numsSize;i++)
    {
        flag=myHashSetContains(obj,nums[i]);
        if(flag==true) return flag;
        myHashSetAdd(obj,nums[i]);
    }
    return flag;
}

尝试学习C++的STL。

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> s;
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        for(auto x:nums)
        {
            if(s.find(x)!=s.end())
                return true;
            s.insert(x);
        }
        return false;
    }
};
  1. 349.两个数组的交集

题解

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1,set2;
        for(auto x:nums1)
            set1.insert(x);
        for(auto x:nums2)
            set2.insert(x);
        
        vector<int> ans;
        if(set1.size()>set2.size())
        {
            for(auto &x:set2)
            {
                if(set1.contains(x))
                    ans.push_back(x);
            }
        }
        
        else
        {
            for(auto &x:set1)
            {
                if(set2.contains(x))
                    ans.push_back(x);
            }
        }
        return ans;
    }
};

题解二

使用merge函数。
a.merge(b)
\(a=a\cup b\),\(b=a\cap b\).

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1,set2;
        for(auto x:nums1)
            set1.insert(x);
        for(auto x:nums2)
            set2.insert(x);
        
        vector<int> ans;
        set1.merge(set2);

        for(auto &x:set2)
            ans.push_back(x);
        return ans;
    }
};

注释

unordered_set<template> 是哈希无序集合。一般用:
1. find(T)来返回元素所处于的迭代器地址。
2. count(T)来返回含有的指定元素的个数。
3. contains(T)返回集合中是否具有该元素。

  1. 128.最长连续序列

采用有序的set。

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        set<int> s;
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        for(auto i:nums)
        {
            s.insert(i);
        }
        int count=0;
        int ans=0;
        int pre,current;
        for (auto &x:s)
        {
            if(count==0)
            {
                pre=x;
                count++;
                continue;
            }
            current=x;
            if(current==pre+1)
            {
                count++;
                pre=current;
                continue;
            }
            else
            {
                ans=count>ans?count:ans;
                pre=current;
                count=1;
            }
        }
        ans=count>ans?count:ans;
        return ans;
    }
};
  1. 290.单词规律

思路

注意采用两个无序映射。需要分别检查pattern与substring的关系,也要检查substring与pattern的关系,检查他们的双射关系。

分隔字符串采用substr(pos,num)函数。寻找下标。分隔为\([pos,pos+num)\).

最后要注意匹配时要将pattern全部使用完成。

题解

class Solution {
public:
    bool wordPattern(string pattern, string s) {
        unordered_map<char,string> p;
    unordered_map<string,char> q;
    
    int i=0,cnt=0,j=0;
    string substring;
    while(i<s.size())
    {
        while(s[i+cnt]!=' ')
        {
            cnt++;
            if(i+cnt>=s.size()) break;
        }
        substring=s.substr(i,cnt);
        i=i+cnt+1;
        cnt=0;

        if(p.find(pattern[j])==p.end())
            p.emplace(make_pair(pattern[j],substring));
        if(p[pattern[j]]!=substring) return false;
        if(q.find(substring)==q.end())
            q.emplace(make_pair(substring,pattern[j]));
        if(q[substring]!=pattern[j]) return false;
        j++;
    }
    if(j!=pattern.size()) return false;
    return true;
    }
};
  1. 532.数组中的k-diff数对

思路

采用有序的哈希表进行计数。并且寻找当前数+k是否存在即可。
注意区分讨论k=0.

题解

class Solution {
public:
    int findPairs(vector<int>& nums, int k) {
        map<int,int> p;
        for(auto i:nums)
        {
            if(p.find(i)==p.end())
                p.emplace(make_pair(i,1));
            else
                p[i]++;
        }
        int ans=0;
        if(k==0)
        {
            for(auto &[i,cnt]:p)
            {
                if(cnt>1)
                    ans++;
            }
            return ans;
        }
        else
        {
            for(auto &[i,cnt]:p)
            {
                if(p.contains(i+k))
                    ans++;
            }
            return ans;
        }
    }
};
  1. 138.随机链表的复制

思路1

创建两个哈希表,一个将原有node映射到下标,一个将下标映射到现有的node.

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        if(!head) return nullptr;

        // build 2 hash map.
        // hashmap 1: original node -> index;
        // hashmap 2: index -> new node;

        Node *original_chain=head;
        unordered_map<Node*,int> Node2index;
        unordered_map<int,Node*> index2Node;
        int index=0;
        while(original_chain)
        {
            Node2index.emplace(make_pair(original_chain,index++));
            original_chain=original_chain->next;
        }
        Node2index.emplace(make_pair(nullptr,index));

        original_chain=head;
        index=0;
        Node *new_chain=new Node(original_chain->val);
        Node *read_new=new_chain;
        index2Node.emplace(make_pair(index++,read_new));
        while(original_chain->next)
        {
            read_new->next=new Node(original_chain->next->val);
            index2Node.emplace(make_pair(index++,read_new->next));
            original_chain=original_chain->next;
            read_new=read_new->next;
        }
        index2Node.emplace(make_pair(index++,nullptr));

        original_chain=head;
        read_new=new_chain;
        while(original_chain)
        {
            int ind=Node2index[original_chain->random];
            Node *rand=index2Node[ind];
            read_new->random=rand;
            read_new=read_new->next;
            original_chain=original_chain->next;
        }
        return new_chain;
    }
};

思路2(更好)

采用递归+回溯的方法,可以更好地只采用一个哈希映射进行。

class Solution {
public:
    unordered_map<Node*, Node*> cachedNode;

    Node* copyRandomList(Node* head) {
        if (head == nullptr) {
            return nullptr;
        }
        if (!cachedNode.count(head)) {
            Node* headNew = new Node(head->val);
            cachedNode[head] = headNew;
            headNew->next = copyRandomList(head->next);
            headNew->random = copyRandomList(head->random);
        }
        return cachedNode[head];
    }
};
  1. 554.砖墙

思路

遍历整个数组。并且将数组中的数字累加得到新的边界标记。利用哈希表存储边界标记并得到最大标记即可。

class Solution {
public:
    int leastBricks(vector<vector<int>>& wall) {
        int range=0;
        for(int i=0;i<wall[0].size();i++)
            range+=wall[0][i];
        
        map<int,int> bucket;
        int maximum=0;
        for(auto &row:wall)
        {
            int mark=0;
            for(auto i:row)
            {
                mark+=i;
                if(mark<range && mark>0)
                {
                    bucket[mark]++;
                    maximum=max(bucket[mark],maximum);
                }
            }
        }
        return wall.size()-maximum;
    }
};
  1. 609.在系统中查找重复文件
class Solution {
public:
    void cut(string &str,multimap<string,string> &m)
    {
        string path="";
        int i;
        for(i=0;i<str.size();i++)
        {
            if(str[i]==' ') break;
            path+=str[i];
        }
        path+='/';
        i++;
        string doc="";
        string contents="";
        for(;i<str.size();i++)
        {
            if(str[i]=='(')
            {
                i++;
                while(str[i]!=')')
                    contents+=str[i++];
            }
            if(str[i]==')')
            {
                m.emplace(make_pair(contents,path+doc));

                continue;
            }
            if(str[i]==' ')
            {
                doc="";
                contents="";
                continue;
            }
            doc+=str[i];
        }
    }
    vector<vector<string>> findDuplicate(vector<string>& paths) {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        multimap<string,string> pathWithContents;
        for(auto &i:paths)
        {
           cut(i,pathWithContents);
        }
        vector<vector<string> > ans;
        vector<string> ans_temp;
        string content="";
        for(auto &[con,doc]:pathWithContents)
        {
            if(content=="")
            {
                content=con;
                ans_temp.push_back(doc);
            }
            else if(content==con)
            {
                ans_temp.push_back(doc);
            }
            else
            {
                if(ans_temp.size()>1)
                {
                    ans.push_back(ans_temp);
                    ans_temp={};
                    ans_temp.push_back(doc);
                    content=con;
                }
                else
                {
                    ans_temp={};
                    ans_temp.push_back(doc);
                    content=con;
                }
            }
        }
        if(ans_temp.size()>1)
            ans.push_back(ans_temp);
        return ans;
    }
};
  1. 454.四数相加II

思路

分组哈希。

题解

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> p;
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        for (auto i:nums1)
        {
            for(auto j:nums2)
                p[i+j]++;
        }
        int ans=0;
        for(auto k:nums3)
        {
            for(auto l:nums4)
                ans+=p[-k-l];
        }
        return ans;
    }
};
  1. 550.和为K的子数组

前缀和+哈希表

我们遍历数组,利用哈希表记录下前缀和的值和相同前缀和值出现的次数。
我们的目的是为了\(pre[j:i]=pre[i]-pre[j-1]=k\).则我们只需要记录下符合\(pre[j-1]=pre[i]-k\)的j即可。
这样我们就有

题解

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int,int> p;
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        p[0]++;
        int temp=0;
        int ans=0;
        for(auto i:nums)
        {
            temp+=i;
            if(p.find(temp-k)!=p.end())
                ans+=p[temp-k];
            p[temp]++;
        }
        return ans;
    }
};
  1. 523.连续的子数组和

前缀和+哈希。这里要注意采用的存取方式是前缀和mod k的余数。

为什么?

有:

\[\begin{aligned} pre[i]&=(pre[i-1]+nums[i])\mod k \\ &\text{if}~ nums[i]\mod k\equiv 0\\ pre[i]&=pre[i-1] \end{aligned} \]

因此当出现间隔超过1的余数相同的前缀时,就存在一个区间使得其中的和是k的倍数。

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        // prefix sum + hash table.
        unordered_map<int,int> p;
        int temp=0;
        int index=0;
        ios::sync_with_stdio(false);
        cin.tie(0),cout.tie(0);
        p.emplace(make_pair(0,-1));
        for(index=0;index<nums.size();index++)
        {   
            temp=(temp+nums[index])%k;

            if(p.find(temp)!=p.end() && index-p[temp]>1)
                return true;
            else
                p.emplace(make_pair(temp,index));
        }
        return false;
    }
};
  1. 525.连续数组

题解

注意unordered_map(map)和unordered_set(set)的单一性。一个key只能对应一个value。我们将\(nums[i]=0\to nums[i]=-1\),这样是为了计算前缀中0和1的差值。

只需要保留首次出现sum时的数组索引下标即可。剩下的全部只进行长度的计算。

   0  0  1  0  0  0  1  1  1  1	//nums
0 -1 -2 -1 -2 -3 -4 -3 -2 -1  0	//prefix
   0  0  2  2  2  2  2  4  6  8	//length
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        // prefix sum and hash.
        // prefix sum and hash.
        unordered_map<int,int> p;
        p[0]=-1;
        int index=0;
        int sum=0;
        int ans=0;
        for(auto i:nums)
        {
            // find 1 and -1.
            sum+=(i==0)?-1:1;
            if(p.find(sum)!=p.end())
            {
                ans=ans>index-p[sum]?ans:index-p[sum];
                index++;
            }
            else
                p.emplace(make_pair(sum,index++));
        }
        return ans;
    }
};
posted @ 2024-11-08 23:54  木木ちゃん  阅读(27)  评论(0)    收藏  举报