雜湊表

木木ちゃん發表於2024-11-08

雜湊表

  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;
    }
};

相關文章