概要
在本博客中,将盘点几个主要的基础算法,包括排序、二分、高精度、前缀和与差分、双指针算法、位运算、离散化、区间合并。本文将对各个算法详细介绍,并且给出实例,使用C++代码实现。
1. 快速排序
快速排序是一种高效的排序算法,它的基本思想是分治法(Divide and Conquer),通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序的平均时间复杂度为 O(n log n),但最坏情况下的时间复杂度为 O(n^2)。
1.1 题目实例
1.2 代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N];
void quick_sort(int a[], int l, int r)
{
// 判断边界
if(l >= r) return;
// 初始化变量
int i = l - 1, j = r + 1, x = a[(i + j) / 2];
// 数组两边有序
while(i < j)
{
do i++; while(a[i] < x);
do j--; while(a[j] > x);
if(i < j) swap(a[i], a[j]);
}
// 递归排序
quick_sort(a, l, j);
quick_sort(a, j+1, r);
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
// 排序
quick_sort(a, 0, n-1);
// 输出排序后结果
for(int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
return 0;
}
2. 归并排序
归并排序将数组分成两半,对每一半进行排序,然后将排序好的两半合并在一起。归并排序的性能非常稳定,时间复杂度总是 O(n log n),无论是最好、最坏还是平均情况。归并排序不是原地排序,因为它需要额外的空间来合并排序的子数组。
归并排序的步骤如下:
- 分解:将当前序列从中间分成两个子序列。
- 递归:递归地对两个子序列进行归并排序。
- 合并:将两个排序好的子序列合并成一个最终的排序序列。
2.1 题目实例
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N], temp[N];
void merge_sort(int a[], int l, int r)
{
// 判断边界
if(l >= r) return;
int mid = (l + r) / 2;
int i = l, j = mid + 1, k = 0;
// 两边排序,递归
merge_sort(a, l, mid);
merge_sort(a, mid+1, r);
// 合并操作,选择两边中小的数放到temp暂存
while(i <= mid && j <= r)
{
if(a[i] < a[j]) temp[k++] = a[i++];
else temp[k++] = a[j++];
}
// 剩余合并
while(i <= mid) temp[k++] = a[i++];
while(j <= r) temp[k++] = a[j++];
// 将此区间合并好序的返回a数组
for(int i = 0, j = l; i < k; i++)
{
a[j++] = temp[i];
}
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
// 排序
merge_sort(a, 0, n-1);
// 输出排序后结果
for(int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
return 0;
}
3. 二分
二分算法(Binary Search Algorithm),也称为二分查找算法或折半查找算法,是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半,通过比较数组中间的元素与目标值来决定下一步搜索的区间。
二分查找算法的步骤如下:
- 初始化:设置两个指针,一个指向数组的最低位置(low),另一个指向最高位置(high)。
- 比较:在每次迭代中,比较数组中间位置的元素与目标值。
- 调整指针:
如果中间元素等于目标值,搜索成功,返回元素的位置。
如果目标值小于中间元素,将 high 指针移动到中间位置的左侧。
如果目标值大于中间元素,将 low 指针移动到中间位置的右侧。
重复:重复步骤2和步骤3,直到 low 大于 high,表示搜索失败,目标值不在数组中。
3.1 题目实例
3.2 代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++)
cin >> a[i];
while(m--)
{
// 初始化位置信息
int x;
cin >> x;
int l = 0, r = n - 1, mid;
while(l < r) // 查询左端点
{
mid = (l + r) / 2;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
if(a[l] != x)
{
cout << "-1 -1" << endl;
}
else{ // 右边界
cout << l << " ";
// 初始化变量
int l = 0, r = n - 1, mid;
while(l < r)
{
mid = (l + r + 1) / 2;
if(a[mid] > x) r = mid - 1;
else l = mid;
}
cout << l << endl;
}
}
return 0;
}
4 高精度算法
高精度算法通常指的是能够处理大量数字或非常精确计算的算法。
4.1 高精度加法
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
vector<int> add(vector<int> A, vector<int> B)
{
vector<int> C;
int t = 0; // 进位
for(int i = 0, j = 0; i < A.size() || j < B.size(); i++, j++)
{
if(i < A.size()) t += A[i];
if(j < B.size()) t += B[i];
C.push_back(t % 10);
t = t / 10;
}
if(t) C.push_back(t);
return C;
}
int main()
{
string s1, s2;1
cin >> s1 >> s2;
vector<int> A, B;
for(int i = s1.size() - 1 ; i >= 0; i--) A.push_back(s1[i] - '0');
for(int i = s2.size() - 1; i >= 0; i--) B.push_back(s2[i] - '0');
auto C = add(A, B);
for(int i = C.size() - 1; i >= 0; i--) cout << C[i];
return 0;
}
5 区间合并
区间合并算法是一种处理区间数据的算法,它将一组有重叠的区间合并成不重叠的区间集合。
算法的基本步骤如下:
- 排序:首先按照每个区间的起始位置进行排序。
- 初始化:使用一个新列表来存储最终合并后的区间。
- 合并区间:遍历排序后的区间列表,如果当前区间与列表中最后一个区间不重叠,就直接添加到列表中;如果有重叠,则将当前区间与 列表中的最后一个区间合并。
5.1 题目实例 链接
5.2 题解
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int, int> PI;
vector<PI> a, res;
int main()
{
int n;
cin >> n;
while(n--)
{
int l, r;
cin >> l >> r;
a.push_back({l, r});
}
sort(a.begin(), a.end()); // 排序
int st = -2e9, ed = -2e9;
for(auto temp : a)
{
if(ed < temp.first) //不能合并
{
if(ed == -2e9) // 特判第一个
{
ed = temp.second;
st = temp.first;
}
res.push_back({st, ed});
ed = temp.second;
st = temp.first;
}
else if(ed <= temp.second){ // 合并
ed = temp.second;
}
}
cout << res.size();
return 0;
}
6 差分
差分矩阵在算法中通常指的是一种数据结构,用于高效地处理数组或矩阵中的区间更新和查询操作。它利用了前缀和的概念,通过构建一个差分数组,使得对原数组或矩阵的某个区间进行增加或减少操作的时间复杂度降低
6.1 实例 二维差分数组
6.2 题解
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int n, m, q;
const int N = 1004;
int a[N][N], s[N][N];
int main()
{
cin >> n >> m >> q;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
scanf("%d", &a[i][j]);
}
}
// 构建差分矩阵
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
s[i][j] = s[i-1][j] + s[i][j-1] + a[i][j] - s[i-1][j-1];
}
}
// 输出结果
while(q--)
{
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]<< endl;
}
return 0;
}
7 双指针算法
双指针算法使用两个或多个指针(或索引)来遍历数据结构,特别是数组或链表,以解决特定的问题。双指针技术可以用于执行各种操作,如搜索、排序、查找特定元素或模式等。值得注意的是,在执行双指针算法时,必须注意指针的位置关系,注意指针的范围。
7.1 实例 最长连续不重复子序列
7.2 实现
// 双指针算法
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010;
int n, a[N], c[N]; // a存储输入,c存储输入的数在当前扫描区间中数的个数
int main()
{
cin >> n;
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
// 开始双指针
int res = 0;
for(int i = 0, j = 0; i < n; i++)
{
c[a[i]] ++;
while(c[a[i]] > 1)
{
c[a[j]] --;
j++;
}
res = max(res, i - j + 1);
}
cout<< res;
return 0;
}
7.3 数组元素的目标和
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N], b[N];
int n, m, x;
int main()
{
cin >> n >> m >> x;
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
for(int j = 0; j < m; j++) scanf("%d", &b[j]);
for(int i = 0, j = m - 1; i < n, j >= 0; i++)
{
while(a[i] + b[j] > x && j >= 0)
{
j--;
}
if(a[i] + b[j] == x)
{
cout << i << " " << j << endl;
break;
}
}
return 0;
}
小结
提示:这里可以添加总结
例如:
提供先进的推理,复杂的指令,更多的创造力。