Hash算法
hash就是散列表,就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。
实质就是压缩映射,散列值的空间通常远小于输入的空间。
常利用hash算法,将输入的一千万个检索串转化为题目中所说大约三百万个不同的数,进行排序选出我们需要的值即可。
出现冲突时,可以用拉链法,就是用链表来解决冲突,用一个链表数组,来存储相应数据,当出现冲突时,依次添加到链表的后面进行处理。
例题1 正方形
基本思路就是,将输入的点存在数组和哈希表中,数组是为了顺序遍历,哈希表是为了查找快。
取正方形对角线上的两个顶点,根据几何关系计算另外一条对角线上。两个顶点,如果计算出的另外两个顶点都在map中,则找到一个正方形。
要注意的是,因为两条对角线各被计算了一次,所以结果要除以2。
已经知道对角线AC上的a,c坐标,求b点坐标的公式为:
bx=(ax+cx+cy-ay)/2;
by=(ay+cy+ax-cx)/2;
代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
const int H = 10007;
int ptx[N], pty[N], hashTable[H];
int cur, n;
long ans;
struct Node {
int x;
int y;
int next;
}node[N];
void initHash() {
for (int i = 0; i < H; i++) {
hashTable[i] = -1;
}
cur = ans = 0;
}
void insertHash(int x, int y) {
int h = (x * x + y * y) % H;//哈希函数
node[cur].x = x;
node[cur].y = y;
node[cur].next = hashTable[h];
hashTable[h] = cur++;
}
bool searchHash(int x, int y) {
int h = (x * x + y * y) % H;
int next = hashTable[h];
while (next != -1) {
if (x == node[next].x && y == node[next].y)
return true;
next = node[next].next;
}
return false;
}
int main() {
iostream::sync_with_stdio(false);
while (cin >> n && n != 0) {
initHash();
for (int i = 0; i < n; i++) {
cin >> ptx[i] >> pty[i];
insertHash(ptx[i], pty[i]);
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int x1 = ptx[i] - (pty[i] - pty[j]);
int y1 = pty[i] + (ptx[i] - ptx[j]);
int x2 = ptx[j] - (pty[i] - pty[j]);
int y2 = pty[j] + (ptx[i] - ptx[j]);
if (searchHash(x1, y1) && searchHash(x2, y2))
ans++;
}
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int x1 = ptx[i] - (pty[i] - pty[j]);
int y1 = pty[i] + (ptx[i] - ptx[j]);
int x2 = ptx[j] - (pty[i] - pty[j]);
int y2 = pty[j] + (ptx[i] - ptx[j]);
if (searchHash(x1, y1) && searchHash(x2, y2))
ans++;
}
}
ans >>= 2;
cout << ans << endl;
}
return 0;
}
例题2 字符串哈希
双重哈希,没用拉链法
代码实现:
#include<bits/stdc++.h>
using namespace std;
void read(int& x) {
x = 0;
int f = 0;
char ch = getchar();
while (ch < '0' || ch>'9') {
f |= (ch == '-');
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
x = f ? -x : x;
return;
}
const int maxn = 200020;
const int mod = 998244353;
const int p = 1e9 + 7;
int ans, n, cnt;
char str[maxn];
struct node {
unsigned long long res, ress;
unsigned long long hash(char* s, int len) {
unsigned long long Ans = 0;
for (int i = 1; i <= len; i++) {
Ans = Ans * mod + s[i];
}
return Ans;
}
unsigned long long rehash(char* s, int len) {
unsigned long long Ans = 0;
for (int i = 1; i <= len; i++) {
Ans = Ans * p + s[i];
}
return Ans;
}
bool operator<(const node& x) {
return res == x.res ? ress < x.ress : res < x.res;
}
}st[maxn];
int main() {
iostream::sync_with_stdio(false);
read(n);
for (int i = 1; i <= n; i++) {
cin >> str + 1;
int len = strlen(str + 1);
st[++cnt].res = st[i].hash(str + 1, len);
st[++cnt].ress = st[i].rehash(str + 1, len);
}
sort(st + 1, st + 1 + cnt);
for (int i = 1; i <= n; i++) {
if (st[i].res == st[i + 1].res)
if (st[i].ress == st[i + 1].ress)
ans++;
}
cout << n - ans << endl;
return 0;
}
插入排序
原理:将原数列中的第一个元素视为一个有序数列,视第二个元素至最后一个元素为未排序元素。从头到尾扫描整个未排序数列,然后将扫描到的每个元素插入到有序序列中的适当位置。
复杂度:最好可以达到O(n),最坏是O(n2),平均O(n2)
#include<bits/stdc++.h>
using namespace std;
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
int n;
cin >> n;
int* arr = new int[n];
for (int i = 0; i < n; i++)cin >> arr[i];
for (int i = 1; i < n; i++) {
int temp = arr[i];
int j;
for (j = i - 1; j >= 0 && temp < arr[j]; j--)
arr[j + 1] = arr[j];
arr[j + 1] = temp;
}
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
delete[]arr;
return 0;
}
希尔排序
原理:选择一个增量序列,按照增量序列元素个数进行多次排序,每次排序根据对应的增量将待排序的数列分成若干个长度为m的子序列,分别对子表进行插入排序,当增量为1的时候,整个序列作为一个表来处理,表长度就是整个序列的长度。
复杂度:最好可以达到O(n1.3),最坏是O(n2),平均O(nlog2n)~O(n2)
#include<bits/stdc++.h>
using namespace std;
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
int n;
cin >> n;
int* arr = new int[n];
for (int i = 0; i < n; i++)cin >> arr[i];
for (int d = n / 2; d >= 1; d /= 2) {
for (int i = d; i < n; i++) {
int temp = arr[i];
int j;
for (j = i - d; j >= 0 && temp < arr[j]; j = j - d)
arr[j + d] = arr[j];
arr[j + d] = temp;
}
}
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
delete[]arr;
return 0;
}
选择排序
复杂度:O(n2)
#include<bits/stdc++.h>
using namespace std;
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
int n;
cin >> n;
int* arr = new int[n];
for (int i = 0; i < n; i++)cin >> arr[i];
for (int i = 0; i < n - 1; i++) {
int flag = i;
for (int j = i + 1; j < n; j++)
if (arr[flag] > arr[j]) flag = j;//选取最小值的下标
if (flag != i) swap(arr[flag], arr[i]);
}
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
delete[]arr;
return 0;
}
冒泡排序
复杂度:最好可以达到O(n),最坏是O(n2),平均O(n2)
#include<bits/stdc++.h>
using namespace std;
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
int n;
cin >> n;
int* arr = new int[n];
for (int i = 0; i < n; i++)cin >> arr[i];
for (int i = 0; i < n; i++) {
int flag = 0;
for (int j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
flag = 1;
}
}
if (flag == 0)break;
}
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
delete[]arr;
return 0;
}
归并排序(递归实现)
复杂度:O(nlog2n)
#include<bits/stdc++.h>
using namespace std;
int datas[10];
void Merge(int first1, int last1, int last2) {
int* temp = new int[10];
int i = first1, j = last1 + 1, k = first1;
while (i <=last1 && j <= last2) {
if (datas[i] <= datas[j])temp[k++] = datas[i++];
else temp[k++] = datas[j++];
}
while (i <= last1)temp[k++] = datas[i++];
while (j <= last2)temp[k++] = datas[j++];
for (i = first1; i <= last2; i++)datas[i] = temp[i];
delete[]temp;
}
void MergeSort1(int first, int last) {
if (first == last)return;
else {
int mid = (first + last) / 2;
MergeSort1(first, mid);
MergeSort1(mid + 1, last);
Merge(first, mid, last);
}
}
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
for (int i = 0; i < 10; i++) {
cin >> datas[i];
}
MergeSort1(0,9);
for (int i = 0; i < 10; i++) {
cout << datas[i] << " ";
}
return 0;
}
归并排序(非递归实现)
#include<bits/stdc++.h>
using namespace std;
int datas[10];
int length = 10;
void Merge(int first1, int last1, int last2) {
int* temp = new int[10];
int i = first1, j = last1 + 1, k = first1;
while (i <= last1 && j <= last2) {
if (datas[i] <= datas[j])temp[k++] = datas[i++];
else temp[k++] = datas[j++];
}
while (i <= last1)temp[k++] = datas[i++];
while (j <= last2)temp[k++] = datas[j++];
for (i = first1; i <= last2; i++)datas[i] = temp[i];
delete[]temp;
}
void MergePass(int h) {
int i = 0;
while (i + 2 * h <= length) {
Merge(i, i + h - 1, i + 2 * h - 1);
i = i + 2 * h;
}
if (i + h < length) {
Merge(i, i + h - 1, length - 1);
}
}
void MergeSort2() {
int h = 1;
while (h < length) {
MergePass(h);
h = 2 * h;
}
}
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
for (int i = 0; i < 10; i++) {
cin >> datas[i];
}
MergeSort2();
for (int i = 0; i < 10; i++) {
cout << datas[i] << " ";
}
return 0;
}
快速排序
复杂度:最好可以达到O(nlog2n),最坏是O(n2),平均O(nlog2n)
#include<bits/stdc++.h>
using namespace std;
int datas[10];
int Partition(int first, int last) {
int i = first, j = last;
while (i < j) {
while (i < j && datas[i] <= datas[j])j--;//右侧扫描
if (i < j) {
swap(datas[i], datas[j]);
i++;
}
while (i < j && datas[i] <= datas[j])i++;//左侧扫描
if (i < j) {
swap(datas[i], datas[j]);
j--;
}
}
return i;
}
void QuickSort(int first, int last) {
if (first >= last)return;//区间长度为1,递归结束
else {
int pivot = Partition(first, last);
QuickSort(first, pivot-1);//对左侧子序列进行快速排序
QuickSort(pivot + 1, last);//对右侧子序列进行快速排序
}
}
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
for (int i = 0; i < 10; i++) {
cin >> datas[i];
}
QuickSort(0, 9);
for (int i = 0; i < 10; i++) {
cout << datas[i] << " ";
}
return 0;
}
基数排序
假设待排序序列记录看成是由d个子关键码复合而成,每个子关键码的取值范围为m个。
每一趟分配的时间复杂度是O(n),每一趟收集的时间复杂度为O(m),整个排序需要执行d趟。
复杂度:O(d(n+m)
#include<bits/stdc++.h>
using namespace std;
struct Node {
int data;
Node* next;
};
void RadixSort(Node* first, int d) {
Node* front[10], * rear[10], * tail;
int i, j, k, base = 1;
for (i = 1; i <= d; i++) {
for (j = 0; j < 10; j++) {
front[j] = rear[j] = NULL;
}
while (first != NULL) {
k = (first->data / base) % 10;
if (front[k] == NULL)front[k] = rear[k] = first;
else rear[k] = rear[k]->next = first;
first = first->next;
}
for (j = 0; j < 10; j++) {
if (front[j] == NULL)continue;
if (first == NULL)first = front[j];
else tail->next = front[j];
tail = rear[j];
}
tail->next = NULL;
base = base * 10;
}
}
int main() {
iostream::sync_with_stdio(false);
return 0;
}
堆排序
复杂度:O(nlog2n)
#include<bits/stdc++.h>
using namespace std;
int datas[10];
int length = 10;
void Sift(int k, int last) {
int i, j;
i = k; j = 2 * i + 1;//i是被调整结点,j是i的左孩子
while (j <= last) {
if (j < last && datas[j] < datas[j + 1])j++;//j指向左右孩子的较大者
if (datas[i] > datas[j])break;//已经是堆
else {
swap(datas[i], datas[j]);
i = j; j = 2 * i + 1;//被调整结点位于结点j的位置
}
}
}
void HeapSort() {
int i;
for (i = length / 2 - 1; i >= 0; i--) {
Sift(i, length - 1);
}
for (i = 1; i < length; i++) {
swap(datas[0], datas[length - i]);
Sift(0, length - i - 1);//重建堆
}
}
int main() {
//全都是从小到大
iostream::sync_with_stdio(false);
for (int i = 0; i < 10; i++) {
cin >> datas[i];
}
HeapSort();
for (int i = 0; i < 10; i++) {
cout << datas[i] << " ";
}
return 0;
}