算法------(9)哈希表

例题:(1)Acwing 840.模拟散列表

算法------(9)哈希表_第1张图片

        开放寻址法:一般来说开所需要的数字的3倍大小,首先对该数字取模,由于有负数存在因此要用(x%N+N)%N的方式,然后找某个数字在哈希表中的位置,不断对表进行循环查找,如果找到空位就存入,因此开放寻址可能存在多个位置存储同一个数且无法记录出现次数。 

#include 
#include 
#include 
using namespace std;
const int N = 300010;
int h[N];
int null = 0x3f3f3f3f;
int find(int x){
    int t = (x%N+N)%N;
    while(h[t]!=null&&h[t]!=x){
        t++;
        if(t==N) t = 0;
    }
    return t;
}
void insert(int x){
    int t = find(x);
    h[t] = x;
}
bool query(int x){
    return h[find(x)] != null;
}
int main()
{
    memset(h,0x3f,sizeof(h));
    int n;
    scanf("%d", &n);
    for(int i = 0;i

        拉链法:存在多少个数就开多大的槽(头结点),每一个槽对应一个链表存放同一个哈希值的数。

#include 
#include 
#include 
using namespace std;
const int N = 100003;
int h[N],e[N],ne[N],idx;
int find(int x){
    return (x%N+N)%N;
}
void insert(int x){
    int t = find(x);
    e[idx] = x;
    ne[idx] = h[t];
    h[t] = idx++;
}
bool query(int x){
    int t = find(x);
    for(int i = h[t];i!=-1;i = ne[i]){
        if(e[i]==x) return true;
    }
    return false;
}
int main()
{
    memset(h, -1, sizeof(h));
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ){
        char op[2];
        scanf("%s", op);
        if(op[0] == 'I'){
            int x;
            scanf("%d", &x);
            insert(x);
        }
        else{
            int x;
            scanf("%d", &x);
            if(query(x)) printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}

(2)AcWing 841. 字符串哈希

算法------(9)哈希表_第2张图片

        字符串哈希,就是把字符串看做一个p进制数进行哈希,然后利用前缀和的思想得到每个子段的哈希值。一般来说我们利用unsigned long long进行计算,这样溢出相当于对2的64次方取模,而一般来说p为131时哈希冲突不存在。 

#include 
#include 
#include 
#include 
using namespace std;
typedef unsigned long long ULL;
const int N = 1e5+10,P = 131;
char str[N];
ULL h[N],p[N];
ULL get(int l,int r){
    return h[r] - h[l-1] * p[r-l+1];
}
int main()
{
    int n,m;
    scanf("%d%d", &n, &m);
    scanf("%s",str + 1);
    p[0] = 1;
    for(int i = 1;i<=n;i++){
        h[i] = h[i-1] * P + str[i];
        p[i] = p[i-1] * P; 
    }
    for(int i = 0;i

练习:(1) P3498 KOR-Beads

算法------(9)哈希表_第3张图片

        思路差不多。。不过中间很多细节还是参考了下题解。。

        题目就是在求每一个字符串的最多不同子串,这个“不同”要求子串正向反向都不能相同,因此可以把正向和反向各求一遍字符串hash,然后相乘。最后对每一个k的可能情况进行枚举,根据调和极数,算法复杂度必须为O(nlnn)。因此判重的复杂度需要是O(1),于是利用set。

        一开始读入出现了不小的问题,之后直接改为读入数字就ac了。

#include
using namespace std; 
typedef unsigned long long ull;
const int N = 2e5+20;
const ull P = 131;
ull p[N],h1[N],h2[N];
int ans[N],str[N];
int tot = 0,num,cnt,n;
set st;
ull get1(int l,int r){
	return h1[r] - h1[l-1] * p[r-l+1];
}
ull get2(int l,int r){
	return h2[l] - h2[r+1] * p[r-l+1];
}
int main(int argc, char** argv) {
	scanf("%d",&n);
	for(int i = 1;i<=n;i++){
		scanf("%d",&str[i]);
	}
	p[0] = 1;
	for(int i = 1;i<=n;i++){
		h1[i] = h1[i-1] * P + str[i];
		p[i] = p[i-1]*P;
	}
	for(int i = n;i>=1;i--){
		h2[i] = h2[i+1] * P + str[i]; 
	}
	for(int k = 1;k<=n;k++){
		st.clear();cnt = 0;
		for(int i = k;i<=n;i+=k){
			ull now = get1(i-k+1,i)*get2(i-k+1,i);
			if(st.count(now)) continue;
			st.insert(now);
			cnt++;
		}
		if(cnt > tot){
			tot = cnt;
			num = 1;
			ans[1] = k;
		}
		else if(cnt == tot){
			ans[++num] = k;
		}
	}
	printf("%d %d\n",tot,num);
	for(int i = 1;i<=num;i++) printf("%d ",ans[i]);
	return 0;
}

(2) Leetcode 202.快乐数

      算法------(9)哈希表_第4张图片

        一点都快乐不起来。。。居然是简单。。。

        已知一个数进行这个操作只可能有三种结果:(1)变得无限大(2)变为1(3)陷入循环,由于第一种情况总是会变成第二种/第三种情况(因为3位以上的数进行该操作都会变成3位数),因此第一种情况不会发生,我们只需要考虑第二和第三种情况。因此我们每进行一次操作就把得到的数放入哈希set当中,因为这种方式查询某元素是否存在的速度是O(1)的。如果目前的数已经存在则陷入循环,如果变为1则得到答案。查询可以利用contains函数。

class Solution {
public:
    int happy(int x){
        int sum = 0;
        while(x){
            sum += (x%10)*(x%10);
            x/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set x;
        int sum = 0;
        while(1){
            sum = happy(n);
            if(sum==1) return true;
            if(x.contains(sum)) return false;
            else{
                x.insert(sum);
                n = sum;
            }
        }
    }
};

(3) Leetcode 49.字母异位词分组

算法------(9)哈希表_第5张图片

        没想到这个方法。。。

        既然是重新排列原单词,那我对每个字符串进行排序就可以得到这个字符串最原本(按照字典序)排序的字符串,然后加入到哈希表中,最后利用迭代器遍历哈希表得到每一组结果。 

class Solution {
public:
    vector> groupAnagrams(vector& strs) {
        vector> ans;
        unordered_map> x;
        for(auto c:strs){
            string str = c;
            sort(str.begin(),str.end());
            x[str].push_back(c);
        }
        for(unordered_map>::iterator it=x.begin();it!=x.end();it++){
            ans.push_back(it->second);
        }
        return ans;
    }
};

(4)Leetcode 350.两个数组的交集II

算法------(9)哈希表_第6张图片

        做出来了。。但是方法不够好。。

        这里只讲哈希表的做法。利用哈希表存储每个元素在第一个数组里出现的次数,然后遍历第二个数组,假如该元素在第一个数组里出现则加入答案数组且出现次数-1,这样可以保证加入的次数一定是两个数组中出现次数较少的。

class Solution {
public:
    vector intersect(vector& nums1, vector& nums2) {
        vector ans;
        unordered_map x;
        int s1 = nums1.size(),s2 = nums2.size();
        for(int i = 0;i=1){
                x[nums2[i]]--;
                ans.push_back(nums2[i]);
            }
        }
        return ans;
    }
};

你可能感兴趣的:(算法基础课,算法,散列表,数据结构)