符号表是一种以集合为基础,支持查询插入删除操作的抽象数据类型。
用数组实现的符号表,通常带一个游标指示最后一个元素在数组的储存位置,优点是结构简单,易于操作,缺点是集合的大小受数组大小限制,三个操作在最坏情况下都需要o(n)复杂度,通常集合元素并不占满整个数组,储存空间没有得到充分利用。
因此需要用哈希表(散列表)实现符号表。哈希表就是一种以键值存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。因此只要o(1)时间便可进行操作。然而处理数据间的冲突成为问题。
开散列
拉链法 当有不同元素被映射到同一个位置,将这个位置拉出一个链表,链接相同键值的元素。
三角形 题意就是给出几组三角形的边长,每个三角形有其权值。问属于同一类相似三角形的最大权值。
这题可以用开散列做。用gcd把每个三角形的最小边长相似三角形求出,三边之和作为它的键值。然后拉出链表,链表中每个节点是不同的三角形(他们的共同点是边长之和相同),把三边长从大到小排列,一一对应比较来区分。用结构体数组指针实现。
#include<iostream> #include<cstring> using namespace std; int max(int a, int b) { return a > b ? a : b; } int min(int a, int b) { return a < b ? a : b; } int gcd(int a, int b){ int t = 0; while (b){ t = a%b; a = b; b = t; } return a; } const int tablesize = 300000; struct node { int val; int flag; int a, b, c; node *next; }*table[tablesize]; int main() { int key; int i, n, m, a0, b0, c0, val, a1, b1, c1; cin >> n; for (i = 1;i < tablesize;i++) { table[i] = new node; table[i]->a = table[i]->b = table[i]->c = 0; table[i]->val = -0x3f; table[i]->next = NULL; } for (i = 0;i < n;i++) { cin >> a1 >> b1 >> c1 >> val; a0 = a1 / gcd(a1, gcd(b1, c1)); b0 = b1 / gcd(a1, gcd(b1, c1)); c0 = c1 / gcd(a1, gcd(b1, c1)); key = a0 + b0 + c0; a1 = max(a0, max(b0, c0)); c1 = min(a0, min(b0, c0)); b1 = key - a1 - c1;//将a0b0c0从大到小排序 node *p = table[key];//!!!!!!!!!重要,等于设了一个游标 while (1) { if (p->a == 0 && p->b == 0 && p->c == 0) { p->a = a1; p->b = b1; p->c = c1; p->val = val; p->next = new node; p = p->next; p->a = p->b = p->c = 0; p->val = -0x3f; p->next = NULL; break; } else if (p->a == a1 && p->b == b1 && p->c == c1) { if (val > p->val) p->val = val; break; } else p = p->next; } } cin >> m; for (i = 0;i < n;i++) { cin >> a1 >> b1 >> c1; a0 = a1 / gcd(a1, gcd(b1, c1)); b0 = b1 / gcd(a1, gcd(b1, c1)); c0 = c1 / gcd(a1, gcd(b1, c1)); key = a0 + b0 + c0; a1 = max(a0, max(b0, c0)); c1 = min(a0, min(b0, c0)); b1 = key - a1 - c1; node *p = table[key];//!!!!!!!!!重要,等于设了一个游标 int op = 1; while (1) { if (p->a == 0 && p->b == 0 && p->c == 0) { op = 0; break; } else if (p->a == a1 && p->b == b1 && p->c == c1) { cout << p->val << endl; break; } else p = p->next; } if (!op) cout << "Sorry" << endl; } return 0; }
很迷的是,这题我一直a不了 以上代码只有一个ac 并且找不出bug
闭散列
一个桶中只存放一个元素,当出现冲突时,需进行探测,找到空桶存放冲突元素。
散列函数的构造方法:
直接定址法:取关键字的某个线性函数值作为散列地址
除留余数法:取关键字的某个不大于表长的数除后所得余数作为散列地址
平方取中法:取关键字平方后中间几位作为散列地址
折叠法:将关键字分成几个部分,将这几个部分进行运算所得的值作为散列地址
数值分析法:发现关键字之间的规律构造函数
处理冲突的方法:
线性探测法:当出现冲突时,用取散列长度的模的方法来找下一个位置。hi(k)=(h(k)+di)mod m
m为散列长度 di取1,2…m-1
未完
成绩查询 输出n个学生的成绩并查询。学生的成绩由字符串构成,长度不超过4。
显然暴力求解不行,可以尝试用哈希表做。将名字字符转化为散列表。将每个字母等效为一个二十六进制数。设姓名为(ABCD)26, 散列函数为H(x)=((A*26+B)*26+C)*26+D。
#include<iostream> #include<string> #include<cstdlib> using namespace std; int a[500000]; int main() { memset(a, -1, sizeof(a)); int n, m; cin >> n >> m; string h; int i, j, len, sum, score; for (i = 0;i < n;i++) { cin >> h >> score; len = h.length(); sum = h[0] - ‘a‘ + 1; for (j = 1;j < len;j++) { sum = sum * 26 + h[j] - ‘a‘ + 1; } a[sum] = score; } for (i = 0;i < m;i++) { cin >> h; len = h.length(); sum = h[0] - ‘a‘ + 1; for (j = 1;j < len;j++) { sum = sum * 26 + h[j] - ‘a‘ + 1; } if (a[sum] >= 0) cout << a[sum] << endl; else cout << "not,exist!" << endl; } return 0; }