4.8-4.12算法刷题笔记

刷题

    • 1. 堆排序
    • 2. 模拟堆
  • 哈希表
    • 3. 模拟散列表
    • 4. 字符串哈希
  • DFS
    • 5. 排列数字
    • 6. n-皇后问题
  • 2. BFS(队列)
    • 7. 字母迷宫
    • 8. 滑动谜题
  • 3. 树与图的dfs
    • 9. 树的重心
  • 4. 树与图的bfs(最短路)
    • 10. 图中点的层次( 无权最短路 )
  • 5. 拓扑排序
    • 11. 课程表
  • 6. 朴素dijkstra算法
    • 12. Dijkstra求最短路 I(邻接矩阵)
      • 普通方法:bfs
  • 7. 堆优化版dijkstra
    • 13. Dijkstra求最短路 II(邻接表)
  • 8. Bellman-Ford算法(遍历 边)
    • 14. 有边数限制的最短路

1. 堆排序

原题链接
在这里插入图片描述

import java.util.*;class Main {static PriorityQueue<Integer> queue = new PriorityQueue<>();public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int m = scanner.nextInt();for (int i = 1; i <= n; i++) {queue.add(scanner.nextInt());}for (int i = 1; i <= m; i++) {System.out.print(queue.poll() + " ");} }
}

在这里插入图片描述

import java.util.Scanner;
public class Main{static int N = 100010;static int[] h = new int[N];static int size;//交换函数public static void swap(int x,int y){int temp = h[x];h[x] = h[y];h[y] = temp;}//堆函数核心函数,堆排序函数,传入的参数u是下标public static void down(int u){int t = u; // t用来分身变量//u的左分支下标小于size说明该数存在,然后如果这个数小于t,则让左下标赋值给t,即u的分身 if(u * 2 <= size && h[u * 2] < h[t]) t = u * 2; //u的右分支下标小于size说明该数存在,然后如果这个数小于t,因为可能上面的if语句会重置t,则判断是不是小于新或旧t,//如果小于t的话,就让右下标赋值给t ,即u的分身。if(u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;//如果u不等于t,说明左或者右下标有比较小的值赋值给t了,分身变了if(u != t){//就让u跟t这两个数交换一下位置,让小的数替代u的位置swap(u,t);//然后继续递归t下标小的位置down(t);}}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();for(int i = 1 ; i <= n ; i ++ ) h[i] = scan.nextInt(); //首先将所有数先存入数组中size = n;//长度为n//从n/2的位置开始将数组中的值插入堆中//堆结构是一个完全二叉树,所有有分支的数等于没有分支的数,即最后一排的数量等于上面所有的数//最下面一排没有分支的数不用参与插入,所以从n/2开始进行插入for(int i = n/2 ; i >= 0; --i ) down(i);//就是让他进行向下排序处理 while(m -- > 0){//输出前m小的m个元素System.out.print(h[1] + " "); //每一次输出头节点//因为是数组,删除第一个数复杂,删除最后一个元素比较简单//所以就将最后一个元素将第一个元素覆盖掉,然后删除掉最后一个元素h[1] = h[size--];//然后进行堆排序,对第一个元素进行处理down(1);}}
}

2. 模拟堆

原题链接

在这里插入图片描述

import java.util.Scanner;
public class Main{static int N = 100010,size,m;static int[] h = new int[N];static int[] hp = new int[N];//自身被映射数组static int[] ph = new int[N];//映射数组public static void swap(int[] a,int x,int y){int temp = a[x];a[x] = a[y];a[y] = temp;}public static void head_swap(int x,int y){//这里因为映射数组跟被映射数组是互相指向对方,如果有两个数更换位置,映射下标也要进行更换//ph的下标指向是按顺序插入的下标,hp所对应的值是ph的按顺序的下标,用这两个属性进行交换swap(ph,hp[x],hp[y]);//因为按照顺序插入ph到指向交换了,对应指向ph的hp也要进行交换swap(hp,x,y);//最后两个值进行交换swap(h,x,y);}public static void down(int x){int t = x;//x的分身//判断一下左下标是不是存在//判断一下左下标的值是不是比我t的值小 。那么就将左下标的值赋予t;否则不变if(x * 2 <= size && h[x * 2] < h[t]) t = x * 2;//判断一下右下标的值是不是比我t的值小。那么就将右下标的值赋予t,否则不变if(x *2 + 1 <= size && h[x * 2 + 1] < h[t]) t = x * 2 + 1;if(t != x){//如果x不等于他的分身head_swap(x,t);//那就进行交换顺序down(t);//然后一直向下进行操作}}public static void up(int x){//向上操作,判断一下根节点还不是存在//看一下根节点是不是比我左分支或者右分支的值大,大的话就进行交换while(x / 2 > 0 && h[x / 2] > h[x]){head_swap(x,x/2);x = x / 2;//相当于一直up}}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();size = 0;//size是原数组的下标m = 0;//m是映射数组的下标while(n -- > 0){String s = scan.next();if(s.equals("I")){//插入操作int x= scan.nextInt();size ++;m ++;//插入一个数两个数组的下标都加上1;ph[m] = size;hp[size] = m;//ph与hp数组是映射关系h[size] = x;//将数插入到堆中最后位置up(size);//然后up,往上面排序一遍}else if(s.equals("PM")){ //输出当前集合中的最小值System.out.println(h[1]);}else if(s.equals("DM")){//删除当前集合中的最小值//因为需要用到映射数组与被映射数组,因为需要找到k的位置在哪里,需要让映射的顺序,//因为如果用size,size是会随时改变的,不是按顺序的,因为会被up或者down顺序会被修改head_swap(1,size);//将最后一个数替换掉第一个最小值元素,然后数量减1,size--size--;down(1);//插入之后进行向下操作,因为可能不符合小根堆}else if(s.equals("D")){//删除当前集合中第k个插入得数int k = scan.nextInt();k = ph[k];//ph[k] 是一步一步插入映射的下标,不会乱序,head_swap(k,size);//然后将k与最后一个元素进行交换,然后长度减1,size--size--;up(k);//进行排序一遍,为了省代码量,up一遍down一遍。因为只会执行其中一个down(k);}else{int k = scan.nextInt();int x = scan.nextInt();k = ph[k];//ph[k] 是一步一步插入映射的下标,顺序是按照插入时候的顺序h[k] = x;//然后将第k为数修改为数xup(k);//up一遍,down一遍down(k);}}}
}

哈希表

(1) 拉链法int h[N], e[N], ne[N], idx;// 向哈希表中插入一个数void insert(int x){int k = (x % N + N) % N;e[idx] = x;ne[idx] = h[k];h[k] = idx ++ ;}// 在哈希表中查询某个数是否存在bool find(int x){int k = (x % N + N) % N;for (int i = h[k]; i != -1; i = ne[i])if (e[i] == x)return true;return false;}(2) 开放寻址法int h[N];// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置int find(int x){int t = (x % N + N) % N;while (h[t] != null && h[t] != x){t ++ ;if (t == N) t = 0;}return t;}

3. 模拟散列表

原题链接

在这里插入图片描述
拉链法代码(链表)

  1. N取大于范围的第一个质数
  2. k = (x % N + N) % N; (%N 为了避免超级大的值 + N 为了避免出现负数 再%N为了正数+N超出范围)
  3. memset(h, -1, sizeof h);
import java.util.Scanner;
public class Main{static int N = 100003,idx;static int[] h = new int[N];static int[] e = new int[N];static int[] ne = new int[N];public static void add(int x){int k = (x % N + N) % N;e[idx] = x;ne[idx] = h[k];h[k] = idx ++ ;}public static boolean find(int x){int k = (x % N + N) % N;for(int i = h[k];i != -1;i = ne[i]){if(e[i] == x){return true;}    }return false;}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();idx = 0;for(int i = 0 ; i < N ; i++ ){h[i] = -1;}while(n -- > 0){String x = scan.next();if(x.equals("I")){int a = scan.nextInt();add(a);}else{int b = scan.nextInt();if(find(b)) System.out.println("Yes");else System.out.println("No");}}}
}

开放寻址法代码(有空位就存,空位用null=0x3f3f3f3f)

  1. 大于2倍的第一个质数
#include <cstring>
#include <iostream>using namespace std;//开放寻址法一般开 数据范围的 2~3倍, 这样大概率就没有冲突了
const int N = 2e5 + 3;        //大于数据范围的第一个质数
const int null = 0x3f3f3f3f;  //规定空指针为 null 0x3f3f3f3fint h[N];int find(int x) {int t = (x % N + N) % N;while (h[t] != null && h[t] != x) {t++;if (t == N) {t = 0;}}return t;  //如果这个位置是空的, 则返回的是他应该存储的位置
}int n;int main() {cin >> n;memset(h, 0x3f, sizeof h);  //规定空指针为 0x3f3f3f3fwhile (n--) {string op;int x;cin >> op >> x;if (op == "I") {h[find(x)] = x;} else {if (h[find(x)] == null) {puts("No");} else {puts("Yes");}}}return 0;
}

4. 字符串哈希

原题链接

  1. 把一段字符串转成一段数字,便可以直接比较相等
  2. 怎么转?进制大于26
  3. 样例没找全 p[r-l+1] 这个点没找到
import java.util.Scanner ;
public class Main{//开的是long类型数组,本来是需要进行前缀hash求完之后需要进行模2的64次方来防止相同的冲突,可能超过我们给的数组大小static int N = 100010,P = 131;//p是进制数,惊艳值static long[] h = new long[N];//这是存放hash前缀值得数组static long[] p = new long[N];//这是存放p的n次方的数组public static long get(int l,int r){//这里是将运用了一点前缀和公式的方式进行计算//求l-r区间的hash值,就要用h[r] - h[l-1],因为两者位数不用需要让h[l-1]向左边移到跟h[r]对齐//就比如求1234的3-4区间位,1234 - 12,12左移然后就让12*10^(4-3+1)=12*10^2=1200,然后1234-1200 = 34,这样进行计算出来//然后本题是p进制,所以需要将上面公式中的10换成p就行了//h[0] = 0//h[1] = h[i-1] * P + str[1] = 0*P+a = a//h[2] = a * P + b//h[3] = (a*P+b)*P+c = a*p[2]+b*P+c//h[4] = (a*p[2]+b*P+c)*P+d = a*p[3]+b*p[2]+c*P+d//比如abcd求3-4区间位,就是让h[d]-h[b],h[b]位数不用需要向左移对齐h[d],//h[2]*P^(4-3+1)=(a*P+b)*P^2 = a*P^3+b*P^2//然后就让h[d] - h[b]求出34区间值,(a*p[3]+b*p[2]+c*P+d) - (a*P^3+b*P^2) = c*P+dreturn h[r] - h[l-1]*p[r-l+1];}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();String s = scan.next();p[0] = 1;//这个是p的0次方的值,需要单独写出来,非常重要for(int i = 1 ; i <= n ; i++ ){p[i] = p[i-1] * P;//这里对应每一个下标对应对应P的多少次方h[i] = h[i-1] * P + s.charAt(i-1);//这里是公式,预处理前缀哈希的值,因为是P进制,所以中间乘的是P}while(m -- > 0){int l1 = scan.nextInt();int r1 = scan.nextInt();int l2 = scan.nextInt();int r2 = scan.nextInt();//判断两个区间是不是相同,用get的方法返回值一样说明区间的hash值是一样的if(get(l1,r1) == get(l2,r2)) System.out.println("Yes");else System.out.println("No");}}
}

DFS

5. 排列数字

每次遍历dfs参数是 遍历的坑位
力扣链接
acwing链接

#include<iostream>
using namespace std;
const int N = 10;
int path[N];//保存序列
int state[N];//数字是否被用过
int n;
void dfs(int u)//u表示的是 第几个坑位
{if(u > n)//数字填完了,输出{for(int i = 1; i <= n; i++)//输出方案cout << path[i] << " ";cout << endl;}for(int i = 1; i <= n; i++)//空位上可以选择的数字为:1 ~ n{if(!state[i])//如果数字 i 没有被用过{path[u] = i;//放入空位state[i] = 1;//数字被用,修改状态dfs(u + 1);//填下一个位state[i] = 0;//回溯,取出 i}}
}int main()
{cin >> n;dfs(1);
}

6. n-皇后问题

力扣链接
acwing链接

class Solution {public List<List<String>> solveNQueens(int n) {List<List<String>> solutions = new ArrayList<List<String>>();int[] queens = new int[n];Arrays.fill(queens, -1);Set<Integer> columns = new HashSet<Integer>();Set<Integer> diagonals1 = new HashSet<Integer>();Set<Integer> diagonals2 = new HashSet<Integer>();backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);return solutions;}public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {if (row == n) {List<String> board = generateBoard(queens, n);solutions.add(board);} else {for (int i = 0; i < n; i++) {if (columns.contains(i)) {continue;}int diagonal1 = row - i;if (diagonals1.contains(diagonal1)) {continue;}int diagonal2 = row + i;if (diagonals2.contains(diagonal2)) {continue;}queens[row] = i;columns.add(i);diagonals1.add(diagonal1);diagonals2.add(diagonal2);backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);queens[row] = -1;columns.remove(i);diagonals1.remove(diagonal1);diagonals2.remove(diagonal2);}}}public List<String> generateBoard(int[] queens, int n) {List<String> board = new ArrayList<String>();for (int i = 0; i < n; i++) {char[] row = new char[n];Arrays.fill(row, '.');row[queens[i]] = 'Q';board.add(new String(row));}return board;}
}

方法 1. 按行遍历(过程中有回溯、剪枝)

思想:

  1. 每次递归中,遍历一行的元素,如果可以放皇后,就递归到下一行,下一行中不行了,就返回来,回溯,
//cpp
#include <iostream>
using namespace std;const int N = 11;char q[N][N];//存储棋盘
bool dg[N * 2], udg[N * 2], cor[N];
//点对应的两个斜线以及列上是否有皇后int n;void dfs(int r)
{if(r == n)//放满了棋盘,输出棋盘{for(int i = 0; i < n; i++){for(int j = 0; j < n; j++)cout << q[i][j];cout << endl;}cout << endl;return;}for(int i = 0; i < n; i++)//第 r 行,第 i 列 是否放皇后{if(!cor[i] && !dg[i + r] && !udg[n - i + r])//不冲突,放皇后{q[r][i] = 'Q';cor[i] = dg[i + r] = udg[n - i + r] = 1;//对应的 列, 斜线 状态改变dfs(r + 1);//处理下一行cor[i] = dg[i + r] = udg[n - i + r] = 0;//恢复现场q[r][i] = '.';}}
}int main()
{cin >> n;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ )q[i][j] = '.';dfs(0);return 0;
}

方法2. 按每个元素遍历(没有减枝)

// 不同搜索顺序 时间复杂度不同  所以搜索顺序很重要!
#include <iostream>
using namespace std;
const int N = 20;int n;
char g[N][N];
bool row[N], col[N], dg[N], udg[N]; 
// 因为是一个个搜索,所以加了row// s表示已经放上去的皇后个数
void dfs(int x, int y, int s)
{// 处理超出边界的情况if (y == n) y = 0, x ++ ;if (x == n) { // x==n说明已经枚举完n^2个位置了if (s == n) { // s==n说明成功放上去了n个皇后for (int i = 0; i < n; i ++ ) puts(g[i]);puts("");}return;}//和上面按行遍历的差别就是,这里没有循环// 分支1:放皇后if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n]) {g[x][y] = 'Q';row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;dfs(x, y + 1, s + 1);row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;g[x][y] = '.';}// 分支2:不放皇后dfs(x, y + 1, s);
}int main() {cin >> n;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ )g[i][j] = '.';dfs(0, 0, 0);return 0;
}

2. BFS(队列)

7. 字母迷宫

class Solution {public boolean wordPuzzle(char[][] grid, String word) {int h = grid.length, w = grid[0].length;boolean[][] visited = new boolean[h][w];for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {boolean flag = exist(grid, visited, i, j, word, 0);if (flag) {return true;}}}return false;}public boolean exist(char[][] grid, boolean[][] visited, int i, int j, String s, int k) {if (grid[i][j] != s.charAt(k)) {return false;} else if (k == s.length() - 1) {return true;}visited[i][j] = true;int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};boolean result = false;for (int[] dir : directions) {int newi = i + dir[0], newj = j + dir[1];if (newi >= 0 && newi < grid.length && newj >= 0 && newj < grid[0].length) {if (!visited[newi][newj]) {boolean flag = exist(grid, visited, newi, newj, s, k + 1);if (flag) {result = true;break;}}}}visited[i][j] = false;return result;}
}

原题链接
原题链接

class Solution {public boolean wordPuzzle(char[][] grid, String word) {int h = grid.length, w = grid[0].length;boolean[][] visited = new boolean[h][w];for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {boolean flag = exist(grid, visited, i, j, word, 0);if (flag) {return true;}}}return false;}public boolean exist(char[][] grid, boolean[][] visited, int i, int j, String s, int k) {if (grid[i][j] != s.charAt(k)) {return false;} else if (k == s.length() - 1) {return true;}visited[i][j] = true;int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};boolean result = false;for (int[] dir : directions) {int newi = i + dir[0], newj = j + dir[1];if (newi >= 0 && newi < grid.length && newj >= 0 && newj < grid[0].length) {if (!visited[newi][newj]) {boolean flag = exist(grid, visited, newi, newj, s, k + 1);if (flag) {result = true;break;}}}}visited[i][j] = false;return result;}
}
#include<iostream>
#include<cstring>using namespace std;int n,m;
int p[110][110];
int d[110][110];
int dx[4] = {-1,0,1,0};
int dy[4] = {0,-1,0,1};pair<int,int> q[110*110];
int hh,tt=-1;void bfs()
{q[++tt] = {1,1};p[1][1] = 1;d[1][1] = 0;while(hh<=tt){pair<int,int> item = q[hh++];for(int i = 0; i < 4; i++){int x = item.first;int y = item.second;//cout << x << ' ' << y << endl;if(p[x+dx[i]][y+dy[i]]!=1 && x+dx[i]<=n && x+dx[i] > 0 && y+dy[i] <=m && y+dy[i] >0){d[x+dx[i]][y+dy[i]] = d[x][y] + 1;q[++tt] = {x+dx[i],y+dy[i]};p[x+dx[i]][y+dy[i]] = 1;}}}
}int main()
{cin >> n >> m;for(int i = 1; i<=n; i++){for(int j = 1; j<=m; j++)cin >> p[i][j];}bfs();cout << d[n][m];return 0;
}

8. 滑动谜题

力扣链接
acwing链接


class Solution {public int slidingPuzzle(int[][] board) {int m = 2, n = 3;StringBuilder sb = new StringBuilder();String target = "123450";// 将 2x3 的数组转化成字符串作为 BFS 的起点for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {sb.append(board[i][j]);}}String start = sb.toString();// 记录一维字符串的相邻索引int[][] neighbor = new int[][]{{1, 3},{0, 4, 2},{1, 5},{0, 4},{3, 1, 5},{4, 2}};/******* BFS 算法框架开始 *******/Queue<String> q = new LinkedList<>();HashSet<String> visited = new HashSet<>();// 从起点开始 BFS 搜索q.offer(start);visited.add(start);int step = 0;while (!q.isEmpty()) {int sz = q.size();for (int i = 0; i < sz; i++) {String cur = q.poll();// 判断是否达到目标局面if (target.equals(cur)) {return step;}// 找到数字 0 的索引int idx = 0;for (; cur.charAt(idx) != '0'; idx++) ;// 将数字 0 和相邻的数字交换位置for (int adj : neighbor[idx]) {String new_board = swap(cur.toCharArray(), adj, idx);// 防止走回头路if (!visited.contains(new_board)) {q.offer(new_board);visited.add(new_board);}}}step++;}/******* BFS 算法框架结束 *******/return -1;}private String swap(char[] chars, int i, int j) {char temp = chars[i];chars[i] = chars[j];chars[j] = temp;return new String(chars);}
}
#include<iostream>
#include<unordered_map>
#include<cstring>
#include<queue>using namespace std;int bfs(string s)
{queue<string> q;q.push(s);int dx[4] = { 1,0,-1,0 },dy[4] = { 0,-1,0,1 };unordered_map<string, int> d;d[s] = 0;while (!q.empty()){string f = q.front();q.pop();if (f == "12345678x")return d[f];int df = d[f];int k = f.find("x");int x = k / 3;int y = k % 3;for (int i = 0; i < 4; i++){if (x + dx[i] <= 2 && x + dx[i] >= 0 && y + dy[i] <= 2 && y + dy[i] >= 0){swap(f[k], f[(x + dx[i]) * 3 + y + dy[i]]);if (!d.count(f)){d[f] = df + 1;q.push(f);}swap(f[k], f[(x + dx[i]) * 3 + y + dy[i]]);}}}return -1;
}int main()
{char ch;string s;for (int i = 0; i < 9; i++){cin >> ch;s += ch;}cout << bfs(s);return 0;
}

3. 树与图的dfs

9. 树的重心

在这里插入图片描述

原题链接

void add(int a,int b)
{e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
  1. 遍历过的点标记一下,不再遍历,因为无向图可能往回遍历
import java.util.*;
public class Main{static int N = 100010,M = N * 2,idx,n;static int[] h = new int[N];static int[] e = new int[M];//存的是双倍,所以是Mstatic int[] ne = new int[M];//存的是双倍,所以是Mstatic boolean[] st = new boolean[N];static int ans = N; //一开始将最大值赋值成N,最大了/**** 邻接表,存储方法* 邻接表不用管执行顺序,只需要知道每个节点能够执行到每个多少个节点就行* 比如案例中4 3 , 4 6 ,头结点4插入两个节点3和6,所以执行到4就能够执行3和6,* 固定的,邻接表就是这样子的***/public static void add(int a,int b){e[idx] = b;ne[idx] = h[a];h[a] = idx++;}//返回的是当前子树的数量,比如1下面的所有数量包括自己就是9public static int dfs(int u){int res = 0;//连通块中的最大值这个其实就是ans,到时候跟ans比较大小,小的话就赋值给ans的st[u] = true;//将这个删除的点标记,下次不在遍历int sum = 1;//将删除的点也算上是初始值就是1;到时候有利于求n-sum;//单链表遍历for(int i = h[u];i != -1 ; i = ne[i]){int j = e[i];//然后将每一个的指向的点用变量表示出来if(!st[j]){ //然后如果是没用过,没被标记过的,就可以执行int s = dfs(j);//然后递归他的邻接表上面所有能够抵达的点//然后返回的数量是他所删除的点下面的连通块的大小res = Math.max(res,s); //然后和res比较一下大小,谁大谁就是最大连通块sum += s; //这里是将每递归一个点,就增加一个返回的s,就可以将这个值作为返回值成为最大连通块}}/**** 因为邻接表表中只是往下面执行,删除的点的上面的连通块可能是最大的连通块,* 所以需要用总数减去我们下面所统计出来的最大的连通块* 然后将最大的连通块的值赋值给res* **/res = Math.max(res,n-sum); //然后将每个次的最大值进行比较,留下最小的最大值ans = Math.min(res,ans);return sum;}    public static void main(String[] ags){Scanner scan = new Scanner(System.in);n = scan.nextInt();//这里是将每一个头节点都赋值成-1for(int i = 1 ; i < N ; i++ ){h[i] = -1;}//案例输入输出for(int i = 0 ; i < n - 1 ; i ++){int a = scan.nextInt();int b = scan.nextInt();//因为是无向边,所以就两个数同时指向对方add(a,b);add(b,a);}dfs(1);//从1开始//最后输出的是最小的最大值System.out.println(ans);}
}

4. 树与图的bfs(最短路)

在这里插入图片描述

10. 图中点的层次( 无权最短路 )

原题链接
原题链接

import java.util.Scanner;
public class Main{static int N = 100010,n,m,idx,hh,tt;static int[] h = new int[N],e = new int[N],ne = new int[N];static int[] d = new int[N],q = new int[N];//这个是单链表的模板public static void add(int a,int b){e[idx] = b;ne[idx] = h[a];h[a] = idx++;}public static int bfs(){hh = 0 ; tt = -1;d[1] = 0; // 第一个点是距离为0q[++tt] = 1; //然后将第一个点加进去//如果队列不是空的while(hh <= tt){int t = q[hh++]; //取出队头//然后遍历一下单链表for(int i = h[t] ; i != -1 ; i = ne[i]){int s = e[i]; //然后这个点用一个变量表示if(d[s] == -1){ //如果这个点是没走过的d[s] = d[t] + 1; //然后将这个点距离加上1q[++tt] = s;//然后将第二步的点加入到队尾中}}}return d[n]; //最后返回1到n的最短距离d}public static void main(String[] args){Scanner scan = new Scanner(System.in);n = scan.nextInt();m = scan.nextInt();//这里需要将距离跟头结点都赋值成-1for(int i = 1 ; i < N ; i++){h[i] = -1;d[i] = -1; }for(int i = 0 ; i < m ; i ++ ){int a = scan.nextInt();int b = scan.nextInt();add(a,b);}System.out.println(bfs());}
}

5. 拓扑排序

11. 课程表

力扣链接
acwing链接

class Solution {private static final int N = 5001;private static int[] e;private static int[] ne;private static int[] h;private static int[] cnt;private static int idx;private static void add(int a,int b) {e[idx] = b;ne[idx] = h[a];cnt[b]++;h[a] = idx++;}public boolean canFinish(int numCourses, int[][] prerequisites) {e = new int[N];ne = new int[N];h = new int[N];cnt = new int[N];idx = 0;Arrays.fill(h,-1);for (int[] temp : prerequisites) {add(temp[1],temp[0]);}Queue<Integer> queue = new LinkedList<>();for (int i = 0; i < numCourses; i++) {if (cnt[i] == 0) {queue.add(i);}}int sum = 0;while (!queue.isEmpty()) {int r = queue.remove();sum++;for (int i = h[r]; i != -1; i = ne[i]) {int j = e[i];cnt[j]--;if (cnt[j] == 0) {queue.add(j);}}}if (sum == numCourses) {return true;} else {return false;}}
}
import java.util.*;
public class Main{static int N =  100010,n,m,hh,tt,idx;static int[] e = new int[N],ne = new int[N],h = new int[N];static int[] q = new int[N];static int[] d = new int[N];public static void add(int a,int b){e[idx] = b;ne[idx] = h[a];h[a] = idx++;}public static boolean bfs(){hh = 0 ; tt = -1;for(int i = 1 ; i <= n ; i ++ ){ if(d[i] == 0){     //首先将所有入度为0的点全部插入q队列中q[++tt] = i;}}while(hh <= tt){int t = q[hh++]; //拿出队头for(int i = h[t] ; i != -1; i = ne[i]){ //遍历一下队列中所有的边int s = e[i]; //然后将这个边拿出来d[s] -- ; //将这个边的入度数减1if(d[s] == 0){q[++tt] = s; //如果减完之后s的入度数为0;就将他插入队列中}       }}return tt == n - 1; //最后返回,如果队列中的tt等于n-1个数,说明就是正确的的}public static void main(String[] args){Scanner scan = new Scanner(System.in);n = scan.nextInt();m = scan.nextInt();for(int i = 0 ; i < N ; i ++ ){h[i] = -1; }while(m -- > 0){int a = scan.nextInt();int b = scan.nextInt();add(a,b);d[b] ++;}if(bfs()){//队列刚好队头删除的点就是我们的拓扑序列,因为我们只是将hh往后面移动,但是它具体前面的值还在,直接输出就行for(int i = 0 ; i < n; i ++ ){ System.out.print(q[i] + " ");}}else{System.out.println("-1");}}
}
#include<iostream>
#include<cstring>
#include<queue>using namespace std;const int N = 1e5+10;bool st[N];
int e[N],ne[N],idx,h[N];
int b[N];//每个节点的入读int n,k;  queue<int> q,ans;void bfs()
{while(q.size()){int f = q.front();q.pop();for(int i = h[f]; i != -1; i = ne[i]){b[e[i]]--;if(b[e[i]]==0){ans.push(e[i]);q.push(e[i]);}}}}void add(int a,int b)
{e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}int main()
{ memset(h,-1,sizeof h);cin >> n >> k;for(int i = 1; i <= k; i++){int a,bb;cin >> a >> bb;add(a,bb);b[bb]++;}for(int i = 1; i <= n; i++){if(b[i]==0){//cout << i << endl;q.push(i);ans.push(i);}}bfs();if(ans.size()!=n){cout << -1;return 0;}//cout << ans.size() << endl;while(ans.size()){cout << ans.front() << ' ';ans.pop();}return 0;
}

6. 朴素dijkstra算法

12. Dijkstra求最短路 I(邻接矩阵)

在这里插入图片描述

原题链接
刷题总结

  1. 稀疏矩阵 和 疏密矩阵(疏密矩阵 可以用 邻接矩阵存储方式)
  2. 邻接矩阵直接就可以存储 边距离了
  3. d存储到1节点的距离

二刷总结

  1. dijk就是两步 :1. 在dist里选出最小值 2.遍历dist更新
    如何选(如下)
int t = -1;
for (int j = 1; j <= n; j ++ )if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;

import java.util.*;
public class Main{static int N = 510,n,m, max = 0x3f3f3f3f;static int[][] g = new int[N][N];//存每个点之间的距离static int[] dist = new int[N];//存每个点到起点之间的距离static boolean[] st = new boolean[N];//存已经确定了最短距离的点public static int dijkstra(){Arrays.fill(dist,max);//将dist数组一开始赋值成较大的数dist[1] = 0; //首先第一个点是零//从0开始,遍历n次,一次可以确定一个最小值for(int i = 0 ; i < n ; i ++ ){ int t = -1; //t这个变量,准备来说就是转折用的for(int j = 1 ; j <= n ; j ++ ){/**** 因为数字是大于1的,所以从1开始遍历寻找每个数* 如果s集合中没有这个数* 并且t == -1,表示刚开始 或者 后面的数比我心找的数距离起点的距离短* 然后将j 的值赋值给 t***/if(!st[j] && (t == -1 || dist[j] < dist[t])){t = j; }}st[t] = true;//表示这个数是已经找到了确定了最短距离的点//用已经确认的最短距离的点来更新后面的点//就是用1到t的距离加上t到j的距离来更新从1到j的长度for(int j = 1 ; j <= n ; j ++ ){//dist[j] = Math.min(dist[j],dist[t] + g[t][j]);}}//如果最后n的长度没有改变,输出-1,没有找到;否则输出最短路nif(dist[n] == max) return -1;else return dist[n];}public static void main(String[] args){Scanner scan = new Scanner(System.in);n = scan.nextInt();m = scan.nextInt();//将他们每个点一开始赋值成一个较大的值for(int i = 1 ; i <= n ; i ++ ){Arrays.fill(g[i],max);}while(m -- > 0){int a = scan.nextInt();int b = scan.nextInt();int c = scan.nextInt();g[a][b] = Math.min(g[a][b],c);//这个因为可能存在重边,所以泽出最短的}int res = dijkstra();System.out.println(res);}
}

普通方法:bfs

import java.util.*;class Main {static int N = 510;static int[][] p = new int[N][N];public static void main(String[] args) {Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();for (int i = 0; i < p.length; i++) {Arrays.fill(p[i], 50000000);}for (int i = 0; i < m; i++) {int a = scan.nextInt();int b = scan.nextInt();int c = scan.nextInt();p[a][b] = Math.min(p[a][b],c);}int[] d = new int[N];Arrays.fill(d,(int)5e7);Queue<Integer> queue = new LinkedList<>();d[1] = 0;queue.add(1);while (queue.size() > 0) {int t = queue.poll();for (int i = 1; i <= n; i++) {if (p[t][i] != 50000000 && d[i] > d[t] + p[t][i]) {d[i] = d[t] + p[t][i];queue.add(i);}}}if (d[n] == 50000000) {System.out.print(-1);} else {System.out.print(d[n]);}}
}

7. 堆优化版dijkstra

在这里插入图片描述

13. Dijkstra求最短路 II(邻接表)

原题链接

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;public class Main{static int N = 100010;static int n;static int[] h = new int[N];static int[] e = new int[N];static int[] ne = new int[N];static int[] w = new int[N];static int idx = 0;static int[] dist = new int[N];// 存储1号点到每个点的最短距离static boolean[] st = new boolean[N];static int INF = 0x3f3f3f3f;//设置无穷大public static void add(int a,int b,int c){e[idx] = b;w[idx] = c;ne[idx] = h[a];h[a] = idx ++;}// 求1号点到n号点的最短路,如果不存在则返回-1public static int dijkstra(){//维护当前未在st中标记过且离源点最近的点PriorityQueue<PIIs> queue = new PriorityQueue<PIIs>();Arrays.fill(dist, INF);dist[1] = 0;queue.add(new PIIs(0,1));while(!queue.isEmpty()){//1、找到当前未在s中出现过且离源点最近的点PIIs p = queue.poll();int t = p.getSecond();int distance = p.getFirst();if(st[t]) continue;//2、将该点进行标记st[t] = true;//3、用t更新其他点的距离for(int i = h[t];i != -1;i = ne[i]){int j = e[i];if(dist[j] > distance + w[i]){dist[j] = distance + w[i];queue.add(new PIIs(dist[j],j));}}}if(dist[n] == INF) return -1;return dist[n];}public static void main(String[] args) throws IOException{BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));String[] str1 = reader.readLine().split(" ");n = Integer.parseInt(str1[0]);int m = Integer.parseInt(str1[1]);Arrays.fill(h, -1);while(m -- > 0){String[] str2 = reader.readLine().split(" ");int a = Integer.parseInt(str2[0]);int b = Integer.parseInt(str2[1]);int c = Integer.parseInt(str2[2]);add(a,b,c);}System.out.println(dijkstra());}
}class PIIs implements Comparable<PIIs>{private int first;//距离值private int second;//点编号public int getFirst(){return this.first;}public int getSecond(){return this.second;}public PIIs(int first,int second){this.first = first;this.second = second;}@Overridepublic int compareTo(PIIs o) {// TODO 自动生成的方法存根return Integer.compare(first, o.first);}
}

8. Bellman-Ford算法(遍历 边)

14. 有边数限制的最短路

原题链接

  1. b到1的距离 = min(a到1的距离 + a 到 b的距离)
import java.util.*;class Main {public static void main(String[] args) {Scanner scann = new Scanner(System.in);int n = scann.nextInt();int m = scann.nextInt();int k = scann.nextInt();int[] d = new int[n+2];Arrays.fill(d,0x3f3f3f3f);List<Edge> list = new ArrayList<>();for (int i = 0; i < m; i++) {int a = scann.nextInt();int b = scann.nextInt();int dist = scann.nextInt();Edge e = new Edge();e.a = a; e.b = b; e.d = dist;list.add(e);}d[1] = 0;for (int i = 0; i < k; i++) {int[] temp = Arrays.copyOf(d,d.length);for (Edge e : list) {int a = e.a;int b = e.b;int dist = e.d;if (temp[b] > d[a] + dist) {temp[b] = d[a] + dist;}}d = temp;}if (d[n] >= (int)0x3f3f3f3f/2) {System.out.print("impossible");} else {System.out.print(d[n]);}}static class Edge {int a;int b;int d;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/823054.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker (CentOS,ubuntu)安装及常用命令

Docker和虚拟机一样&#xff0c;都拥有环境隔离的能力&#xff0c;但它比虚拟机更加轻量级&#xff0c;可以使资源更大化地得到应用 Client&#xff08;Docker客户端&#xff09;&#xff1a;是Docker的用户界面&#xff0c;可以接受用户命令&#xff08;docker build&#xff…

记录Python的pandas库详解

如何生成一个pd import pandas as pd df pd.DataFrame([[1,2,3],[4,5,6]],index[A,B],columns[C1,C2,C3])df ---------------------------------------------------------------------------C1 C2 C3 A 1 2 3 B 4 5 6df.T -------------------------------------------------…

爬虫 新闻网站 以湖南法治报为例(含详细注释) V4.0 升级 自定义可任意个关键词查询、时间段、粗略判断新闻是否和优化营商环境相关,避免自己再一个个判断

目标网站&#xff1a;湖南法治报 爬取目的&#xff1a;为了获取某一地区更全面的在湖南法治报的已发布的和优化营商环境相关的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 环境&#xff1a;Pycharm2021&#xff0c;Python3.10&#xff0c; 安装的包&#xff1a;requests&a…

element-ui container 组件源码分享

今日简单分享 container 组件的源码实现&#xff0c;从以下两个方面来讲解&#xff1a; 1、container 组件的页面结构 2、container 组件的属性 一、container 组件的页面结构 二、container 组件的属性 1、container 部分的 direction 属性&#xff0c;子元素的排列方向&am…

Nacos2.3.0安装部署

一&#xff0c;准备安装包 github下载点 二&#xff0c;在/usr/local/目录下创建一个文件夹用于上传和解压Nacos cd /usr/local/ #上传Nacos文件 #解压之前cd进安装包根目录 cd /usr/local/ #这边选择的Nacos版本为2.3.0 tar -zxxvf nacos-server-2.3.0.tar.gz #把该文件移动…

基于SpringBoot的“商务安全邮箱”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“商务安全邮箱”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构 收件箱效果图 草稿箱效果图 已发送…

【数据结构】习题之消失的数字和轮转数组

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;前路漫漫亦灿灿 前言 消失的数字这道题目我会和大家分享三种思路。 还有一道题目是轮转数组&#xff0c;&#xff0c;也会分享三种思路&#xff0c;大…

常见的垃圾回收器(下)

文章目录 G1ShenandoahZGC 常见垃圾回收期&#xff08;上&#xff09; G1 参数1&#xff1a; -XX:UseG1GC 打开G1的开关&#xff0c;JDK9之后默认不需要打开 参数2&#xff1a;-XX:MaxGCPauseMillis毫秒值 最大暂停的时间 回收年代和算法 ● 年轻代老年代 ● 复制算法 优点…

Methoxy PEG Propionic acid具有良好的亲水性和分子量可控性

【试剂详情】 英文名称 mPEG-PA&#xff0c;mPEG-Propionic acid&#xff0c; Methoxy PEG PA&#xff0c; Methoxy PEG Propionic acid 中文名称 聚乙二醇单甲醚丙酸&#xff0c; 甲氧基-聚乙二醇-丙酸 外观性状 由分子量决定&#xff0c;固体或者液体 分子量 400&…

如何提高直线模组的技术水平?

在工业制造业中&#xff0c;不管我们使用任何机械产品&#xff0c;都有一个共同的出发点&#xff0c;就是能用先进的技术突破其产品的性能及使用性。那么直线模组究竟是用什么技术突破其产品的使用性的呢&#xff1f; 1、优化机械设计&#xff1a;设计过程中应充分考虑模组的结…

JetBrains2024来袭

JetBrains2024来袭&#xff0c;激活包含在内的编程IDE&#xff0c;其中AppCode已下架&#xff0c;Aqua&#xff0c;RustRover不支持本地激活需要关联帐号。 Tap&#xff1a;激活稳定可靠&#xff0c;支持Windows&#xff0c;macOS&#xff0c;Linux客户端。

C语言【指针】

1. 基本语法 1.1 指针变量的定义和使用(重点) 指针是一种数据类型&#xff0c;指针变量指向谁 就把谁的地址赋值给指针变量 1.2 通过指针间接修改变量的值 指针变量指向谁 就把谁的地址赋值给指针变量 可以通过 *指针变量 间接修改变量的值 1.3 const修饰的指针变量 语法…

全视通院内导航助力“医”路畅通,让您就医不迷路

“这个科室怎么走&#xff1f;”“CT检查在哪里做&#xff1f;”“请问抽血在哪里&#xff1f;”…… 这是患者在赴院就诊时常会发出的疑问&#xff0c;医院导诊台及其他区域的工作人员对此应接不暇&#xff0c;繁忙时段更容易顾此失彼&#xff0c;不仅自身工作负担大&#xf…

stable diffusion基本原理

diffusion model latent diffusion &#xff1a;先对图片降维&#xff0c;然后在降维空间做diffusion&#xff1b;stable diffusion即基于此方法实现的&#xff0c;因此计算量很小&#xff1b; 共用降噪网络U-Net&#xff1a;输入noisy imagestep&#xff0c;告诉网络当前的噪声…

scipy.signal.cwt, pywt.cwt, ssq_cwt 使用记录

scipy.signal.cwt 该代码中widths以及freq计算公式来源于scipy.signal.morlet2函数官方案例 from scipy.signal import morlet, morlet2 from scipy import signal import matplotlib.pyplot as pltsignal_length 2000 fs 1000# 生成信号数据 time np.arange(0, signal_leng…

全新付费进群系统源码 带定位完整版 附教程

搭建教程 Nginx1.2 PHP5.6-7.2均可 最好是7.2 第一步上传文件程序到网站根目录解压 第二步导入数据库&#xff08;dkewl.sql&#xff09; 第三步修改/config/database.php里面的数据库地址 第四步修改/config/extra/ip.php里面的域名 第四步设置伪静态thinkphp 总后台账…

MySQL死锁与死锁检测

一、什么是MySQL死锁 MySQL中死锁是指两个或多个事务在互相等待对方释放资源&#xff0c;导致无法继续执行的情况。 MySQL系统中当两个或多个事务在并发执行时&#xff0c;就可能会遇到每项事务都持有某些资源同时又请求其他事务持有的资源&#xff0c;从而形成事务之间循环等…

Nginx第2篇-HTTPS配置教程

背景 我最近做个项目要上线&#xff0c;接口部署到服务器&#xff0c;总不能给别人个ip地址加端口吧&#xff0c;而且小程序上线要有接口不能是ip和http协议&#xff0c;必须是https协议。这里记录下使用Nginx配置HTTPS的过程&#xff0c;主要包含以下三部分。 申请域名SSL证…

切换plesk面板语言

近期购入了Hostease的Windows虚拟主机产品&#xff0c;由于进入他们主机Plesk面板后查看全都是英文的&#xff0c;对于英文也不是很懂&#xff0c;尤其是像这种专业 词汇的更不明白。因此这边咨询了Hostease的技术支持&#xff0c;寻求帮助了解到可以Plesk面板可以切换语言的&a…

EDI是什么:EDI系统功能介绍

EDI全称Electronic Data Interchange&#xff0c;中文名称是电子数据交换&#xff0c;也被称为“无纸化贸易”。EDI实现企业间&#xff08;B2B&#xff09;自动化通信&#xff0c;帮助贸易伙伴和组织完成更多的工作、加快物流时间并消除人为错误。 目前国内企业实现EDI通信大多…