Acwing算法基础课学习笔记(六)--数据结构之Hash表&&STL简介

来到数据结构章节的最后一节课,主要内容是哈希表和STL。

模拟散列表

首先要理解什么是哈希。
其实之前的课讲过的离散化就是一种特殊的哈希方式,这里介绍的是一般的哈希方式。算法题中对哈希的操作一般只有添加查找删除一般不会涉及,非要进行删除操作的话,也不是真的删除,而是用一个bool变量进行标记即可。

//拉链法
#include 
#include 

using namespace std;

const int N = 100003;
int h[N], e[N], ne[N], idx;
//h[]是哈希函数的一维数组//e[]是链表中存的值//ne[]是指针存的指向的地址//idx是当前指针
void insert(int x)
{
	int k = (x % N + N) % N;//为了让负数在整数有映射,负数的取模还是负数,加上maxn后为正,再%即可
	/*如果不同单链表的idx都是从0开始单独计数,那么不同链表之间可能会产生冲突。
		这里的模型是这样的:e[]和ne[]相当于一个大池子,里面是单链表中的节点,会被所有单点表共用,idx相当于挨个分配池子中的节点的指针。
		比如如果第0个节点被分配给了第一个单链表,那么所有单链表就只能从下一个节点开始分配,所以所有单链表需要共用一个idx。*/
	e[idx] = x;
	ne[idx] = h[k];
	h[k] = idx;
	idx++;
}

bool find(int x)
{
	int k = (x % N + N) % N;
	for (int i = h[k]; i != -1; i = ne[i])//所有槽都清空,对应的是单链表的头(head)[注:head存的是地址,详见单链表的课]指针为-1
	{
		if (e[i] == x)
			return true;
	}
	return false;
}

int main()
{
	int n;
	scanf("%d", &n);

	memset(h, -1, sizeof h);
	while (n--)
	{
		char op[2];
		int x;
		scanf("%s%d", op, &x);
		if (*op == 'I') insert(x);
		else
		{
			if (find(x)) puts("Yes");
			else puts("No");
		}
	}
	return 0;
}
//开放寻址法
#include 
#include 

using namespace std;

const int N = 200003, null = 0x3f3f3f3f;

int h[N];

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

int main()
{
    memset(h, 0x3f, sizeof h);

    int n;
    scanf("%d", &n);

    while (n--)
    {
        char op[2];
        int x;
        scanf("%s%d", op, &x);
        if (*op == 'I') h[find(x)] = x;
        else
        {
            if (h[find(x)] == null) puts("No");
            else puts("Yes");
        }
    }
    return 0;
}

字符串哈希

题解
字符哈希串的意思 其实就是将字符串的前缀转换为数来存值
由于每位的权值是不一样的 所以每个前缀值都对应着唯一的一种字符串
所以相减后的值也应该是唯一的 从而利用相减后的值可以判断字符串的区间段是否相等
当然具体还有很多细节需要注意

#include
using namespace std;

typedef unsigned long long ULL;     //由于前缀值的值会很大 所以应该将数组中的数据定义为ULL型

const int N = 1e5 + 10;
const int  P = 131;       //P为权重
						//131为经验值 即P=131或13331时 哈希冲突的可能性最小

int n, m;

char str[N];
ULL h[N];              //h[]存放字符串的前缀值
ULL p[N];             //p[]存放各个位数的相应权值

ULL get(int l, int r)
{
	return h[r] - h[l - 1] * p[r - l + 1];        //这步其实是将h[l-1]左移
}                                       //其目的事实上是为了将h[l-1]的高位与h[r]相对齐从而才可以未完成计算
int main()
{
	scanf("%d%d%s", &n, &m, str + 1);

	p[0] = 1;                         //注意这步千万不要忘了 最开始的权值必须赋值为1 否则接下来就会出错
	for (int i = 1; i <= n; i++)
	{
		p[i] = p[i - 1] * P;              //计算每个位上的相应权值
		h[i] = h[i - 1] * P + str[i];       //计算字符串前缀值
									//最新加入的数的权值为p的0次 所以直接加上str[i]即可
	}
	while (m--)
	{
		int l1, r1, l2, r2;
		scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
		if (get(l1, r1) == get(l2, r2)) puts("Yes");
		else puts("No");
	}

	return 0;
}

STL

vector, 变长数组,倍增的思想
	size()  返回元素个数
	empty()  返回是否为空
	clear()  清空
	front()/back()
	push_back()/pop_back()
	begin()/end()
	[]
	支持比较运算,按字典序
pair<int, int>
	first, 第一个元素
	second, 第二个元素
	支持比较运算,以first为第一关键字,以second为第二关键字(字典序)
string,字符串
	szie()/length()  返回字符串长度
	empty()
	clear()
	substr(起始下标,(子串长度))  返回子串
	c_str()  返回字符串所在字符数组的起始地址
queue, 队列
	size()
	empty()
	push()  向队尾插入一个元素
	front()  返回队头元素
	back()  返回队尾元素
	pop()  弹出队头元素
priority_queue, 优先队列,默认是大根堆
	push()  插入一个元素
	top()  返回堆顶元素
	pop()  弹出堆顶元素
	定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;
stack,size()
	empty()
	push()  向栈顶插入一个元素
	top()  返回栈顶元素
	pop()  弹出栈顶元素
deque, 双端队列
	size()
	empty()
	clear()
	front()/back()
	push_back()/pop_back()
	push_front()/pop_front()
	begin()/end()
	[]
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
		size()
		empty()
		clear()
		begin()/end()
		++, -- 返回前驱和后继,时间复杂度 O(logn)
		
		set/multiset
			insert()  插入一个数
			find()  查找一个数
			count()  返回某一个数的个数
			erase()
				(1) 输入是一个数x,删除所有x   O(k + logn)
				(2) 输入一个迭代器,删除这个迭代器
			lower_bound()/upper_bound()
				lower_bound(x)  返回大于等于x的最小的数的迭代器
				upper_bound(x)  返回大于x的最小的数的迭代器
		map/multimap
			insert()  插入的数是一个pair
			erase()  输入的参数是pair或者迭代器
			find()
			[]   时间复杂度是 O(logn)
			lower_bound()/upper_bound()

	unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
		和上面类似,增删改查的时间复杂度是 O(1)
		不支持 lower_bound()/upper_bound(), 迭代器的++--
bitset, 圧位
	bitset<10000> s;
	~, &, |, ^
	>>, <<
	==, !=
	[]
	
	count()  返回有多少个1
	
	any()  判断是否至少有一个1
	none()  判断是否全为0
	
	set()  把所有位置成1
	set(k, v)  将第k位变成v
	reset()  把所有位变成0
	flip()  等价于~
	flip(k) 把第k位取反

你可能感兴趣的:(学习笔记,刷题练习,Acwing)