以下内容只提供对应算法方便好用的模板,或者把某项功能做成函数直接传参调用。不提供详细的算法证明。大部分算法使用java语言编写,可自行转换成其他语言。篇幅过长的内容单独用一篇博客总结,博客内给出了对应的练习题目。持续更新中......
目录
快速排序
归并排序
堆排序
前缀和与差分(一维、二维)
并查集
整数二分、浮点二分
去除数组中重复的元素(双指针)
求最大公约数
求最小公倍数
求素数(欧拉筛)
高精度加法(C++)
高精度减法(C++)
高精度乘法(C++)
高精度除法(C++)
动态规划背包问题
模式匹配算法之KMP算法
最小生成树之prim算法(无向图)
最小生成树之Kruskal算法(无向图)
单源最短路之Dijkstra算法(无向图)
单源最短路之Bellman-ford算法(有向图)
多源最短路之floyd算法(有向图)
warshall算法判断图是否连通(邻接矩阵存放图)
快速排序java代码模板
归并排序java代码模板
堆排序及模拟堆java代码模板
前缀和与差分代码模板
并查集java代码模板
整数二分、浮点二分代码模板
public static void main(String args[]) {
int[] nums = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 7 };
int j = 0;
for (int i = 0; i < nums.length; i++) {//去除数组当中重复的元素
if (i == 0 || nums[i] != nums[i - 1])
nums[j++] = nums[i];
}
for (int i = 0; i < j; i++)
System.out.print(nums[i] + " ");//输出:1 2 3 4 5 6 7
}
朴素求法
public static int gcd(int a, int b) {// 最大公约数朴素写法,传入两个数,返回最大公约数。
while (b != 0) {
int temp = a % b;
a = b;
b = temp;
}
if (a < 0)
return -a;
return a;
}
递归写法
public static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public static int gbs(int n, int m) {//传入两个数,得到两个数的最小公倍数
int temp = Math.max(n, m);//求得这两个数的较大值
for (int i = temp; ; i++) {//从较大值开始遍历
if (i % n == 0 && i % m == 0)
return i;
}
}
public static int[] Prime(int n) {// 欧拉筛求素数,传入参数n表示范围的最大值
int[] prime = new int[100010];// 能够存放的素数个数
boolean[] bool = new boolean[100010];
int count = 0;// 记录素数个数
for (int i = 2; i <= n; i++) {
if (bool[i] == false) {
prime[count++] = i;
bool[i] = true;
}
for (int j = 0; j < count; j++) {
if (i * prime[j] > n)
break;
bool[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
}
}
System.out.printf("0~%d范围内的素数个数为:%d\n", n, count);// 输出个数
return prime;
}
java和python用不上高精度。java用BigInteger,python更方便。
vector add(string a, string b){//传入两个数的字符串形式,返回之和。
if (a.length() < b.length()) return add(b, a);
int t = 0;
vector A, B, c;
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
for (int i = 0; i < A.size(); i ++ ){
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
return C;
}
高精度减法存在正负号的判断,稍微复杂一点。
bool cmp(vector &A, vector &B)//正负号判断
{
if (A.size() != B.size()) return A.size() > B.size();
for (int i = A.size() - 1; i >= 0; i -- )
if (A[i] != B[i])
return A[i] > B[i];
return true;
}
vector sub(vector &A, vector &B)//传入数字,返回差。
{
vector C;
for (int i = 0, t = 0; i < A.size(); i ++ )
{
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
string a, b;
vector A, B;
cin >> a >> b;//输入两个字符串形式的数组,返回其差
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
vector C;
if (cmp(A, B)) C = sub(A, B);//根据正负号的不同选择不同的相减方式
else C = sub(B, A), cout << '-';
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
cout << endl;
return 0;
}
高精度乘法的两个数一般只有1个数非常大。
vector mul(string a, long b){//传入一个数的字符串形式和另一个数,返回其乘积。
vector A, c;
int t = 0;
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
for (int i = 0; i < A.size() || t; i ++ )
{
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
//for (int i = C.size() - 1; i >= 0; i -- ) printf("%d", C[i]);
return C;
}
vector div(string a, int b, int &r)//传入
{
vector A, c;
r = 0;//记录余数
for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
for (int i = A.size() - 1; i >= 0; i -- )
{
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end());
while (C.size() > 1 && C.back() == 0) C.pop_back();
printf("\n余数为:%d\n",r);
return C;
}
动态规划背包问题总结
图解KMP算法原理及其代码分析
最小生成树之prim算法java代码模板
public static int prim(int n, int[][] map) {//从下标1开始记录图的数据,n个节点,返回最小生成树的边权之和
int[] dist = new int[n + 10];// 含义:这个点到最小生成树那个集合的最短距离,点到集合的距离
boolean[] st = new boolean[n + 10];// st数组表示这个点有没有被加入最小生成树集合,如果被加入了,st[i]=true
Arrays.fill(dist, INF);// 初始化距离
int result = 0;// 记录最小生成树所有边权之和
for (int i = 0; i < n; i++) {
int t = -1;// 第一个点时情况特别判断
for (int j = 1; j <= n; j++) {// 找到集合外距离集合最近的点
if (st[j] == false && (t == -1 || dist[t] > dist[j]))
t = j;
}
if (i != 0 && dist[t] == INF)// 如果最小的节点长度都为INF,那么不用找了,不存在最小生成树
return INF;
if (i != 0)// 如果这个点不是第一个点,将这个点距离加入
result += dist[t];
st[t] = true;// 将t这个点加入集合当中
for (int j = 1; j <= n; j++)// 用这个点来更新其他点到这个集合的最短距离
dist[j] = Math.min(dist[j], map[t][j]);// 这个时候t这个点已经被加入集合当中了,所以其他点到集合的距离可以用t来更新,因为t也在集合当中。
}
return result;
}
最小生成树之Kruskal算法java代码模板
static int[] p;
public static int Find(int x) {// 并查集路径压缩优化
if (p[x] != x)
p[x] = Find(p[x]);
return p[x];
}
// kruskal算法略显繁琐,需要用到并查集
public static int kruskal(int n, int[][] way) {// n个点,way记录的是边的信息
p=new int[n+10];
Arrays.sort(way, new Comparator() {// 重写比较器,以边的权值作为比较依据
public int compare(int[] o1, int[] o2) {
return o1[2] - o2[2];
}
});
for (int i = 1; i <= n; i++)// 并查集初始化每一个点,都是以自己为根节点的集合
p[i] = i;
int result = 0, count = 0;
for (int i = 0; i < way.length; i++) {
int a = way[i][0];
int b = way[i][1];
int c = way[i][2];
a = Find(a);// 查询a的根节点
b = Find(b);// 查询b的根节点
if (a != b) {// 如果a和b不在一个集合里面
p[a] = b;// 将a的根节点的父节点指向b
result += c;// 加上这条边的距离
count++;// 合并的点数加1
}
}
if (count < n - 1)// 如果连通的点少于了点的总个数,说明不能构成最小生成树
return Integer.MIN_VALUE;
return result;
}
单源最短路之Dijkstra算法java代码模板
public static int[] Dijkstra(int[][] map) {// 传入一个临界矩阵存放的图,从下标0开始为第一个点,返回所有点到第一个节点的最短距离数组。
// 注意邻接矩阵存放的图初始化的数值是由你自己确认的!
int n = map.length;
int[] dist = new int[n];// 存放点到初始点的距离
boolean[] st = new boolean[n];// 存放是否确定到初始点的距离最短
Arrays.fill(dist, Integer.MAX_VALUE);// 初始化存放的所有节点到第一个节点的距离为无穷大
dist[0] = 0;// 初始点到自己的距离为0
for (int i = 0; i < n - 1; i++) {
int temp = -1;
for (int j = 0; j < n; j++) {// 如果矩阵从1开始存放节点信息,修改为for (int j = 1; j <= n; j++)
if (st[j] == false && (temp == -1 || dist[temp] > dist[j]))// 第一次替换找到没有标记确定的第一个点,之后循环找距离初始点最近的没有标记确定的点
temp = j;
}
// 如果矩阵从1开始存放节点信息,修改为for (int j = 1; j <= n; j++)
for (int j = 0; j < n; j++) {// 通过上一步找到的点来更新这个点能够到达的其他点的距离
// map[temp][j]表示temp到j这个点的距离,再加上dist[temp]就表示初始点以temp为中间点到j的距离
dist[j] = Math.min(dist[j], dist[temp] + map[temp][j]);
}
st[temp] = true;// 更新完成之后,将该点标记为确定
}
return dist;// 返回结果
}
单源最短路之Bellman-ford算法java代码模板
public static int[] Bellman_Ford(int n, int[][] way) {// n个点,way表示边的信息,way[i][j]=a表示从i到j距离为a,节点的下标从1开始记录,返回从第一个点到所有点最近的距离
int[] dist = new int[510];// 存放到某点的距离
int[] temp = new int[3];// 存放单条边
Arrays.fill(dist, Integer.MAX_VALUE / 20);// 初始化
dist[1] = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
temp = way[j];
dist[temp[1]] = Math.min(dist[temp[1]], dist[temp[0]] + temp[2]);
}
}
return dist;
}
多源最短路之floyd算法java代码模板
public static int[][] floyd(int n, int[][] map) {// 三重循环,n个点传入邻接矩阵存放的图map,返回多个点之间的最短路径数组
// 刚开始传入的是邻接矩阵图,返回的是距离map[i][j]=a表示从i到j最短距离为a
// 刚开始邻接矩阵的初始化自行处理。
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
map[i][j] = Math.min(map[i][j], map[i][k] + map[k][j]);
}
}
return map;
}
public static boolean Warshall(int[][] tmp, int n) {// 传入这个图的邻接矩阵和节点个数。判断图是否连通,连通返回true
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (tmp[i][j] == 1) {
for (int k = 0; k < n; k++) {
if (tmp[k][i] == 1)
tmp[k][j] = tmp[j][k] = 1;
}
}
}
}
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (tmp[i][j] != 1)
return false;
return true;
}