1. 双指针算法
双指针算法都是O(n)的,核心思想就是将暴力O(n^2)算法优化到O(n)。
常见问题分类:
- 对于一个序列,用两个指针维护一段区间
- 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
例题
- 最长连续不重复子序列
双指针,i
从头到尾遍历一遍,(如果左边是头右边是尾,i
在j
的右边)j
表示i
往左能到的最远不重复数的位置,且j
也是只往尾走不倒退。
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N], s[N]; //存输入的数的数组 和 记录j到i之间元素出现次数的数组
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int res = 0;
//双指针,i从头到尾遍历一遍,j表示i往左能到的最远不重复数的位置,且j也是只往尾走不倒退
for (int i = 0, j = 0; i < n; i++)
{
s[a[i]] ++; //次数++
while (s[a[i]] > 1) //如果j到i中出现了重复的数,那这个数肯定是新加进来的i的数
{
s[a[j]] --; //j指针要往后走,把当前j所指的数的次数--
j++; //j指针往后走
}
res = max(res, i - j + 1);
}
printf("%d", res);
return 0;
}
- 双指针 O(n)
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m, x;
int a[N], b[N];
int main()
{
scanf("%d%d%d", &n, &m, &x);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < m; i++) scanf("%d", &b[i]);
for (int i = 0, j = m - 1; i < n; i++)
{
while (j >= 0 && a[i] + b[j] > x) j--;
if (a[i] + b[j] == x)
{
printf("%d %d\n", i, j);
break;
}
}
return 0;
}
- 二分 O(nlogn)
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m, x;
int a[N], b[N];
int main()
{
scanf("%d%d%d", &n, &m, &x);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < m; i++) scanf("%d", &b[i]);
for (int i = 0; i < n; i++)
{
int y = x - a[i];
int l = 0, r = m - 1;
while (l < r)
{
int mid = l + r >> 1;
if (b[mid] >= y) r = mid;
else l = mid + 1;
}
if (b[l] == y)
{
printf("%d %d", i, l);
break;
}
}
return 0;
}
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int a[N], b[N];
vector<int> s;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < m; i++) scanf("%d", &b[i]);
int i = 0, j = 0;
while (j < m)
{
if (i < n && a[i] == b[j]) i++;
j ++;
}
if (i == n) puts("Yes");
else puts("No");
return 0;
}
2. 位运算
-
n的二进制表示中地k位是几(第k位是从右即各位往左数,从0开始)。
- 先把第k位移到最后一位
n>>k
- 看移动后的各位是几
x&1
两步结合后就是
n>>k&1
- 先把第k位移到最后一位
-
lowbit
操作:返回x的最后一位1是多少
如x=1010
即10,lowbit(x)=10
即2。
实现方式:x&-x
例题
#include <iostream>
using namespace std;
int lowbit(int x)
{
return x & -x;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int x;
cin >> x;
int res = 0;
while (x)
{
x -= lowbit(x); //每次减去x的最后一位1
res ++; //每减一次就说明有一个1
}
cout << res << ' ';
}
return 0;
}