// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {
Queue<Node> q; // 核心数据结构
Set<Node> visited; // 避免走回头路
q.offer(start); // 将起点加入队列
visited.add(start);
int step = 0; // 记录扩散的步数
while (q not empty) {
int sz = q.size();
/* 将当前队列中的所有节点向四周扩散 */
for (int i = 0; i < sz; i++) {
Node cur = q.poll();
/* 划重点:这里判断是否到达终点 */
if (cur is target)
return step;
/* 将 cur 的相邻节点加入队列 */
for (Node x : cur.adj())
if (x not in visited) {
q.offer(x);
visited.add(x);
}
}
/* 划重点:更新步数在这里 */
step++;
}
}
二、二分查找
int left_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
// 搜索区间为 [left, right]
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] == target) {
// 收缩右侧边界
right = mid - 1;
}
}
// 检查出界情况
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
int right_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 这里改成收缩左侧边界即可
left = mid + 1;
}
}
// 这里改为检查 right 越界的情况,见下图
if (right < 0 || nums[right] != target)
return -1;
return right;
}
int binary_search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if(nums[mid] == target) {
// 直接返回
return mid;
}
}
// 直接返回
return -1;
}
三、滑动窗口
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
四、线段树
static int n = 10; // n是元素个数
static int[] array = {0, 1, 5, 1, 3, 4, 2, 0, 9, 0, 9};
// array是原序列(第一个0是占array[0]位的)
static Node[] tree = new Node[4*n]; // tree是线段树
public static void main(String[] args) {
initTree();
build(1, 10, 1); // 利用build函数建树
System.out.println("操作1:[2,5]的区间和是:" + query(2, 5, 1));
// 利用query函数搜索区间和
modify(5, 9, 1); // 利用modify函数实现单点修改(元素5从4改为9)
System.out.println("操作2:元素5从4改为9,此时[2,5]的区间和是:" + query(2, 5, 1));
modifySegment(3, 4, 3, 1);
// 利用modifySegment函数将[3,4]每个元素增加3
System.out.println("操作3:区间[3,4]每个元素+3,此时[2,5]的区间和是:" + query(2, 5, 1));
}
static void initTree (){
for(int i = 0; i < tree.length; i++){
tree[i] = new Node(0, 0, 0, 0);
}
}
static void updateNode (int num) { // num是当前节点序号
tree[num].sum = tree[num * 2].sum + tree[num * 2 + 1].sum;
}
static void build (int l, int r, int num) { // 建树
tree[num].l = l;
tree[num].r = r;
if (l == r) { // l = r说明到达叶子节点
tree[num].sum = array[l];
return;
}
int mid = (l + r) / 2;
build(l, mid, num * 2); // 递归左儿子
build(mid + 1, r, num * 2 + 1); // 递归右儿子
updateNode(num);
}
static void modify (int i, int value, int num) { // 把元素i修改为值value
if (tree[num].l == tree[num].r) { // 到达叶子节点
tree[num].sum = value;
return;
}
int mid = (tree[num].l + tree[num].r) / 2;
if (i <= mid) {
modify(i, value, num * 2); // 递归左儿子
}
else {
modify(i, value, num * 2 + 1); // 递归右儿子
}
updateNode(num);
}
static void modifySegment(int l, int r, int value, int num) { // [l,r]每一项都增加value
if (tree[num].l == l && tree[num].r == r) { // 找到当前区间
tree[num].sum += ( r - l + 1 ) * value; // r-l+1是区间元素个数
tree[num].lazy += value;
return;
}
int mid = (tree[num].l + tree[num].r) / 2;
if (r <= mid) { // 在左区间
modifySegment(l, r, value, num * 2);
}
else if (l > mid) { // 在右区间
modifySegment(l, r, value, num * 2 + 1);
}
else { // 分成2块
modifySegment(l, mid, value, num * 2);
modifySegment(mid + 1, r, value, num * 2 + 1);
}
updateNode(num);
}
static void pushDown(int num) {
if(tree[num].l == tree[num].r) { // 叶节点不用下传标记
tree[num].lazy = 0; // 清空当前标记
return;
}
tree[num * 2].lazy += tree[num].lazy; // 下传左儿子的懒惰标记
tree[num * 2 + 1].lazy += tree[num].lazy; // 下传右儿子的懒惰标记
tree[num * 2].sum += (tree[num * 2].r - tree[num * 2].l + 1) * tree[num].lazy; // 更新左儿子的值
tree[num * 2 + 1].sum += (tree[num * 2 + 1].r - tree[num * 2 + 1].l + 1) * tree[num].lazy; // 更新右儿子的值
tree[num].lazy=0; // 清空当前节点的懒惰标记
}
static int query (int l, int r, int num) {
if (tree[num].lazy != 0) { // 下传懒惰标记
pushDown(num);
}
if (tree[num].l == l && tree[num].r == r) { // 找到当前区间
return tree[num].sum;
}
int mid = (tree[num].l + tree[num].r) / 2;
if (r <= mid) { // 在左区间
return query(l, r, num * 2);
}
if (l > mid) { // 在右区间
return query(l, r, num * 2 + 1);
}
return query(l, mid, num * 2) + query(mid + 1, r, num * 2 + 1); // 分成2块
}
static class Node {
int l; // l是区间左边界
int r; // r是区间右边界
int sum; // sum是区间元素和
int lazy; // lazy是懒惰标记
public Node (int l, int r, int sum, int lazy){
this.l = l;
this.r = r;
this.sum = sum;
this.lazy = lazy;
}
}
五、并查集
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int unionfind(int root)
{
if(a[root] == root)
return root;
return a[root] = unionfind(a[root]); //路径压缩
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n;i++)
{
a[i] = i;
}
while (m--)
{
int x, y;
cin >> x >> y;
int root1 = unionfind(x);
int root2 = unionfind(y);
if(root1 != root2)
{
a[root2] = root1;
}
else
{
cout << root1 <<endl;
}
return 0;
}
六、Dijkstra算法
#include <bits/stdc++.h>
using namespace std;
int dis[10005];
int visit[10005];
int edge[5000][5000];
int n, m, s;
void dijkstra()
{
int minv;
int u;
visit[s] == 1;
for (int i = 1; i <= n; i++) //附上初始值
{
if (i == s)
dis[i] == 0;
else
dis[i] = edge[s][i] == 0 ? INT_MAX : edge[s][i];
}
for (int j = 1; j <= n; j++)
{
minv = INT_MAX;
for (int i = 1; i <= n; i++) //找出距离出发点最近,且没有使用过的点
{
if (dis[i] < minv && visit[i] == 0)
{
minv = dis[i];
u = i;
}
}
visit[u] = 1;
for (int i = 1; i <= n; i++) //松弛
{
if (visit[i] == 0 && edge[u][i] != 0)
dis[i] = min(dis[i], dis[u] + edge[u][i]);
}
}
for (int i = 1; i <= n; i++)
{
cout << dis[i];
if (i != n)
cout << " ";
}
cout << endl;
}
int main()
{
int u, v, w;
cin >> n >> m >> s;
while (m--)
{
cin >> u >> v >> w;
edge[u][v] = w;
}
dijkstra();
return 0;
}
七、快速幂
#include <bits/stdc++.h>
using namespace std;
int solve(int basic, int p, int k)
{
int res = 1;
int a = basic;
while (p)
{
if (p & 1)
{
res = res * a;
}
a = a * a;
p = p >> 1;
}
return res;
}
int main()
{
int b, p;
cin >> b >> p;
int res = solve(b, p);
cout << res << endl;
return 0;
}
八、堆排序(最小堆)
//最小堆
#include <bits/stdc++.h>
using namespace std;
int a[100] = {1, 3, 6, 4, 9, 2, 5, 4};
int n = 8;
void fixdown(int i) //向下调整
{
int temp = a[i];
int j = 2 * i + 1;
while (j <= n)
{
if (j + 1 < n && a[j] > a[j + 1])
{
j++;
}
if (a[j] >= temp)
{
break;
}
a[i] = a[j];
i = j;
j = 2 * i + 1;
}
a[i] = temp;
}
void fixup(int i) //向上调整
{
int temp = a[i];
int j = (i - 1) / 2;
while (j >= 0 && i != 0)
{
if (a[j] <= temp)
break;
a[i] = a[j];
i = j;
j = (i - 1) / 2;
}
a[i] = temp;
}
void buildheap() //建堆
{
for (int i = n / 2; i >= 0; i--)
{
fixdown(i);
}
}
void insertNode(int i) //插入一个数
{
a[n] = i;
n++;
fixup(n - 1);
}
void deleteNode() //删除顶点
{
swap(a[0], a[n - 1]);
n--;
fixdown(0);
}
void printarray() //打印数组
{
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
int main()
{
buildheap(); //建堆
printarray(); //打印
insertNode(0); //插入一个0
printarray(); //打印
deleteNode(); //删除顶点
printarray(); //打印
return 0;
}
九、线性筛素数
#include<cstdio>
#include<cstring>
using namespace std;
bool isPrime[100000010];
int prime[1000010];
void getPrime(int n)
{
int cnt = 1;
memset(isPrime, 1, sizeof(isPrime));
isPrime[1] = 0;
for (int i = 2; i <= n; i++)
{
if(isPrime[i])
prime[cnt++] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++)
{
isPrime[i * prime[j]] = 0;
if(i % prime[j] == 0)
break;
}
}
}
int main()
{
int n, q, m;
scanf("%d %d", &n, &q);
getPrime(n);
while(q--)
{
scanf("%d", &m);
printf("%d\n", prime[m]);
}
return 0;
}