算法.图论-建图/拓扑排序及其拓展

文章目录

    • 建图的三种方式
      • 邻接矩阵
      • 邻接表
      • 链式前向星
    • 拓扑排序
      • 拓扑排序基础原理介绍
      • 拓扑排序步骤解析
      • 拓扑排序模板leetcode-课程表
    • 拓扑排序拓展
      • 食物链计数
      • 喧闹与富有
      • 并行课程

建图的三种方式

我们建图的三种方式分别是邻接矩阵, 邻接矩阵, 链式前向星

邻接矩阵

假设我们的点的个数为N个, 我们就把他们的下标依次标为1, 2 ,…, 然后在一个矩阵表上进行边的添加, 比如我要在点2和点4之间添加一个权值为5的边, 那么我就在矩阵matrix[2][4] = 5即可, 唯一注意的就是如果是无向图, 就需要把matrix[4][2]位置也设置为5, 所以这个方法的弊端是十分明显的, 就是消耗的空间过大, 所以我们在大厂的笔试或者是比赛, 不会用这种方式进行建图, 下面是邻接矩阵法的代码实现

public class day23 {public static void main(String[] args) {int[][] edges = {{1,2,5},{5,3,1},{1,4,4},{2,5,2}};//测试一下使用邻接矩阵法建图GraphUseMatrix graphUseMatrix = new GraphUseMatrix();graphUseMatrix.build(5);graphUseMatrix.directGraph(edges);//graphUseMatrix.unDirectGraph(edges);graphUseMatrix.showGraph();}
}/*** 邻接矩阵建图的方法* 下面我们介绍的所用的图都是带权值的图, 不带权的更简单就不说了*/
class GraphUseMatrix{//设置一个最大的点数(根据题意)private static final int MAXN = 11;//设置一个最大的边数(根据题意, 无向图 * 2)private static final int MAXM = 21;//构建一个当前的点数curnprivate static int curN = 0;//设置一个邻接的矩阵(大小就是(点数 + 1) * (点数 + 1), 这里我们保证是够用的)private static final int[][] matrix = new int[MAXN][MAXN];//初始化矩阵的方法(传入的点的数量)public void build(int n){curN = n;//清空矩阵的脏数据for(int i = 1; i <= n; i++){for(int j = 1; j <= n; j++){//把邻接矩阵中的数据刷为最大值(因为权值可能为0)matrix[i][j] = Integer.MAX_VALUE;}}}//添加边的方法private void addEdge(int from, int to, int weight){matrix[from][to] = weight;}//在有向带权图中添加边public void directGraph(int[][] edges){for(int[] edge : edges){addEdge(edge[0], edge[1], edge[2]);}}//在无向带权图中添加边public void unDirectGraph(int[][] edges){for(int[] edge : edges){addEdge(edge[0], edge[1], edge[2]);addEdge(edge[1], edge[0], edge[2]);}}//展示图的方式public void showGraph(){System.out.println("邻接矩阵法建图展示");for(int i = 1; i <= curN; i++){for(int j = 1; j <= curN; j++){if(matrix[i][j] == Integer.MAX_VALUE){System.out.print("∞  ");}else{System.out.print(matrix[i][j] + "  ");}}System.out.println();}}
}

上述代码的测试结果见下
在这里插入图片描述
在这里插入图片描述

测试结果也是很明显的, 证明我们之前的代码是没有问题的, 无向图总体是按照正对角线呈对称分布

邻接表

邻接表是一种动态的建图的方式, 关于大厂的笔试面试题, 邻接表以及完全够用了, 如果涉及到比赛内容的话, 我们会使用链式前向星建图法, 等会我们会介绍到这种算法, 说回来邻接表, 其实就是一个
ArrayList<ArrayList<int(无权)/int>的结构, 也就是顺序表套顺序表的结构, 假如我们要建立一个从点2到点4的边,如果不带权值, 我们就让外层顺序表2下标对应的顺序表添加一个元素4, 如果同时带有一个权值的话, 我们就添加一个数组(假设权值是8)我们就让2下标对应的顺序表添加一个[4,8]数组, 下面是邻接表的代码实现

public class day23 {public static void main(String[] args) {int[][] edges = {{1, 2, 5}, {5, 3, 1}, {1, 4, 4}, {2, 5, 2}};
//        //测试一下使用邻接矩阵法建图
//        GraphUseMatrix graphUseMatrix = new GraphUseMatrix();
//        graphUseMatrix.build(5);
//        graphUseMatrix.unDirectGraph(edges);
//        graphUseMatrix.showGraph();//测试一下邻接表法建图GraphUseList graphUseList = new GraphUseList();graphUseList.build(5);graphUseList.directGraph(edges);//graphUseList.unDirectGraph(edges);graphUseList.showGraph();}
}/*** 邻接表建图的方法(是一种动态的结构)* 和邻接矩阵一样, 我们在这里介绍的都是带权的图*/
class GraphUseList {//构建一个当前的点数private static int curN = 0;//邻接表的主体private static ArrayList<ArrayList<int[]>> list = new ArrayList<>();//初始化邻接表(传入一个点的数量)public void build(int n) {//设置当前的点数curN = n;//上来直接清空邻接表list.clear();//开始构建顺序表(新添加n+1个列表)for (int i = 0; i <= n; i++) {list.add(new ArrayList<>());}}//添加边的方法private void addEdge(int from, int to, int weight) {list.get(from).add(new int[]{to, weight});}//构建一个有向的图public void directGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[0], edge[1], edge[2]);}}//构建一个无向的图public void unDirectGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[0], edge[1], edge[2]);addEdge(edge[1], edge[0], edge[2]);}}//展示邻接表图的方式public void showGraph() {System.out.println("邻接表法建图展示");for (int i = 1; i <= curN; i++) {System.out.print("点" + i + "(邻点/权值) : ");for (int[] elem : list.get(i)) {System.out.print("[" + elem[0] + "," + elem[1] + "]");}System.out.println();}}
}

执行结果如下图所示
在这里插入图片描述
在这里插入图片描述

链式前向星

前两种建图的方式都有着明显的缺点, 第一个虽然是静态空间但是空间的大小过大, 第二个虽然使用的空间不是很大但是是一种动态的结果, 就会在时间上大打折扣, 所以我们就想要一种既可以是静态空间又可以做到省空间省时间的结构, 我们就需要下面的链式前向星建图法, 这个方法有点类似与前缀树的静态空间建树法(之前有)和链表头插法的结合, 下面我们分析一下建图的过程

准备过程 :
我们准备三个数组, 把每一个加入的边都设置一个编号(从1开始逐渐增加)

数组数组解释
head数组下标表示点的编号, head[i]表示这个点的’头边’的编号
next数组下标表示边的编号, next[i]表示这个边的下一条边的编号
to数组下标表示边的编号, to[i]表示这条边到达的点的编号
weight数组下标表示边的编号, weight[i]表示这条边的权值

具体的例子
假设此时我们准备了五个点, 然后执行4次加边的操作
head数组长度准备为 6 (5 + 1) , next, to, weight 数组的长度都准备 5 (4 + 1)
下面执行加边的过程

[3,1,2], 这条边编号为1, 出发点是3, 终点是1, 权值是2, 在head中插入头边head[3] = 1(这里就是用的头插法, 这里是首条边), next[1] = 0 (头边所以是0) , to[1] = 1, weight[1] = 2

代码实现如下

public class day23 {public static void main(String[] args) {int[][] edges = {{1, 2, 5}, {5, 3, 1}, {1, 4, 4}, {2, 5, 2}};
//        //测试一下使用邻接矩阵法建图
//        GraphUseMatrix graphUseMatrix = new GraphUseMatrix();
//        graphUseMatrix.build(5);
//        graphUseMatrix.unDirectGraph(edges);
//        graphUseMatrix.showGraph();//        //测试一下邻接表法建图
//        GraphUseList graphUseList = new GraphUseList();
//        graphUseList.build(5);
//        graphUseList.unDirectGraph(edges);
//        graphUseList.showGraph();//测试一下链式前向星建图GraphUseLinkedStar graphUseLinkedStar = new GraphUseLinkedStar();graphUseLinkedStar.build(5);graphUseLinkedStar.unDirectGraph(edges);graphUseLinkedStar.showGraph();}
}/*** 链式前向星建图法(静态的建图法)*/
class GraphUseLinkedStar {//定义点最大值private static final int MAXN = 11;//定义边最大值private static final int MAXM = 22;//构建head数组(以点为准)private static final int[] head = new int[MAXN];//构建next数组(以边为准)private static final int[] next = new int[MAXM];//准备to数组(以边为主)private static final int[] to = new int[MAXM];//准备weight数组(以边为主)private static final int[] weights = new int[MAXM];//定义一下当前点的个数private static int curN = 0;//定义一个计数器用于给边编号private static int cnt = 0;//传入一个点数n用来初始化图结构public void build(int n){//更新计数器cnt = 1;//初始化当前节点个数curN = n;//清除head即可(这里不用重置to, weights, next)Arrays.fill(head, 1, n + 1, 0);}//添加边的方法(其实就是链表的头插法)private void addEdge(int from, int ton, int weight){next[cnt] = head[from];to[cnt] = ton;weights[cnt] = weight;head[from] = cnt++;}//构建一个有向带权图public void directGraph(int[][] edges){for(int[] edge : edges){addEdge(edge[0], edge[1], edge[2]);}}//构建一个无向带权图public void unDirectGraph(int[][] edges){for(int[] edge : edges){addEdge(edge[0],edge[1],edge[2]);addEdge(edge[1],edge[0],edge[2]);}}//展示图的方法(类似于链表的遍历)public void showGraph(){System.out.println("链式前向星建图展示");for(int i = 1; i <= curN; i++){System.out.print("点" + i + "(邻点/权值) : ");for(int ei = head[i]; ei != 0; ei = next[ei]){System.out.print("[" + to[ei] + "," + weights[ei] + "]");}System.out.println();}}
}

执行结果如下
在这里插入图片描述

在这里插入图片描述

拓扑排序

拓扑排序基础原理介绍

拓扑排序的存在条件是在一个有向且无环图中的排序, 拓扑排序在某种程度上反应的是一件事的执行的先后顺序, 请看下图

在这里插入图片描述
这张图中, 黑色的字符表示节点的名称, 蓝色的数字指的是该位置的入度是多少, 上面我们提到过, 拓扑排序的过程可以视为完成某一件事的先后顺序, 假设我们最终想要完成的任务是f, 那我们下面的字符的序列也就是完成最终事件的顺序
拓扑排序不是唯一的, 比如下面的图
在这里插入图片描述
在这张图中, 先完成a还是先完成b都是可以的, 所以产生的拓扑排序的情况就有两种, 这就说明拓扑排序的结果可能有多种, 只要满足每一个节点的前面所需要的节点都完成了就可以
拓扑排序的应用举例 :
比如在计算机编译程序的过程中, 编译一个程序需要另外的程序结果才能编译完成, 所以很自然的就会出现程序编译的先后顺序问题, 见下图, 左侧的表格是编译一个程序所需要的已经完成的编译结果, 右面是完成a的编译过程的图, 右下角的两串字符串都是完成的顺序, 这同样说明拓扑排序不是唯一的
在这里插入图片描述
拓扑排序要求一个图有向, 这个很好理解, 做事情的步骤肯定是有先后的顺序的, 而且要无环, 这个我们就拿上面的编译过程理解, 假如a的编译过程依赖b, b的编译过程依赖a, 那这不就是矛盾了吗, 两者互相依赖谁也完成不了

拓扑排序步骤解析

实现拓扑排序用的是零入度删除法, 需要用到队列(特殊状况下用小根堆), 核心就是删除0入度的节点和因为该节点所造成的入度影响
这就解释了上面的图为什么我们要进行入度的标记, 首先图解一下0入度删除法的步骤
在这里插入图片描述
最终组合出来的删除节点的步骤就是最终的拓扑排序的答案

拓扑排序模板leetcode-课程表

leetcode210-课程表题目链接
在这里插入图片描述

class Solution {//我们采用邻接表建图就已经够用了private static ArrayList<ArrayList<Integer>> list = new ArrayList<>();//课的最大数目private static final int MAXN = 2001;//同时定义一个入读表private static final int[] indegree = new int[MAXN];//当前的数据个数private static int curN = 0;public int[] findOrder(int numCourses, int[][] prerequisites) {build(numCourses);directGraph(prerequisites);//定义一个计数器看一看队列弹出了多少次(建议使用数组形式队列)int cntNum = 0;Queue<Integer> queue = new ArrayDeque<>();//首先遍历入度数组加入入度为0的节点for (int i = 0; i < numCourses; i++) {if (indegree[i] == 0) {queue.offer(i);}}//从队列中逐渐弹出元素进行判断int[] res = new int[numCourses];while (!queue.isEmpty()) {int index = queue.poll();//在返回数组中添加上该元素res[cntNum++] = index;//遍历其所属的列表删除这个元素造成的入度for(int elem : list.get(index)){if(--indegree[elem] == 0){queue.offer(elem);}}}return cntNum == res.length ? res : new int[0];}//初始化图结构private static void build(int n) {//更新当前的数据个数curN = n;//更新顺序表list.clear();for (int i = 0; i < n; i++) {list.add(new ArrayList<>());}//更新入度表Arrays.fill(indegree, 0, n, 0);}//建图的主函数(本质是有向无权图), 建图的过程中同时完成入度的统计private static void directGraph(int[][] edges) {for (int[] edge : edges) {addEdge(edge[1], edge[0]);indegree[edge[0]]++;}}//插入边的函数private static void addEdge(int from, int to) {list.get(from).add(to);}
}

拓扑排序拓展

关于拓扑排序的最基础的用法我们在上一节就已经探讨过了, 这节课我们讨论的是拓扑排序的最重要的技巧, 就是根据拓扑排序逐段的向下推送消息, 其实有点类似于树形dp

食物链计数

在这里插入图片描述
上面我们仅仅介绍了一部分流程而已, 但是整体上的逻辑就是这样的, 建图之后跑拓扑排序, 然后计数即可
洛谷-食物链测试链接
代码实现如下, 使用的是链式前向星的建图方式, 然后注意一下比赛平台的IO方式即可

import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.IOException;
//计算食物链的条数, 用的是一种向下推送的技巧(建图使用链式前向星建图法)
public class Main{private static final int MAXN = 5001;private static final int MAXM = 500001;private static final int[] head = new int[MAXN];private static final int[] next = new int[MAXM];private static final int[] to = new int[MAXM];private static final int[] indegree = new int[MAXN];private static final int[] ans = new int[MAXN];private static int resCnt = 0;private static int cnt = 0;private static final int MOD = 80112002;//拓扑排序时需要的队列我们就使用动态的结构模拟看看过不过private static final Queue<Integer> queue = new ArrayDeque<>();//初始化图的函数private static void build(int n){cnt = 1;resCnt = 0;Arrays.fill(head, 0, n + 1, 0);Arrays.fill(indegree, 0, n + 1, 0);Arrays.fill(ans, 0, n + 1, 0);queue.clear();}//添加边的函数(链式前向星的加边方式, 同时修改入度)private static void addEdge(int fro, int t){next[cnt] = head[fro];to[cnt] = t;head[fro] = cnt++;indegree[t]++;}//拓扑排序的过程private static void topoSort(int n){//首先添加0入度的点入队列(同时在ans中修改等待推送)for(int i = 1; i <= n; i++){if(indegree[i] == 0){queue.offer(i);ans[i] = 1;}}//开始进行排序的主逻辑while(!queue.isEmpty()){int po = queue.poll();if(head[po] == 0){//此时说明该点是末尾的节点, 直接计数resCnt = (ans[po] + resCnt) % MOD;}else{//此时继续链式前向星的遍历过程for(int ei = head[po]; ei > 0; ei = next[ei]){ans[to[ei]] = (ans[to[ei]] + ans[po]) % MOD;if(--indegree[to[ei]] == 0){queue.offer(to[ei]);}}}}}public static void main(String[] args) throws IOException{BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StreamTokenizer in = new StreamTokenizer(br);PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));//下面的while循环其实就是建图的过程while(in.nextToken() != StreamTokenizer.TT_EOF){int n = (int)in.nval;build(n);in.nextToken();int opSize = (int)in.nval;for(int i = 0; i < opSize; i++){in.nextToken();int fro = (int)in.nval;in.nextToken();int t = (int)in.nval;addEdge(fro, t);}//下面就去跑拓扑排序topoSort(n);out.println(resCnt);}out.flush();out.close();br.close();}
}

喧闹与富有

在这里插入图片描述
leetcode851.喧闹与富有题目链接
总体上说这个题没什么分析的难度, 就是简单的建出来图之后然后跑拓扑排序就可以了, 传递的信息就是最安静的人的下标, 注意初始化ans数组的时候就把ans数组中每个位置的值初始化为本身的下标(因为自己的安静值肯定是一个限制值)
代码实现如下

//使用链式前向星的建图方式
class LoudAndRich {private static final int MAXN = 501;private static final int MAXM = MAXN * (MAXN - 1) / 2;private static final int[] head = new int[MAXN];private static final int[] next = new int[MAXM];private static final int[] to = new int[MAXM];private static final int[] indegree = new int[MAXN];private static int cnt = 0;private static int curN = 0;private static final Queue<Integer> queue = new ArrayDeque<>();//初始化图的函数private static void build(int n){cnt = 1;curN = n;Arrays.fill(head, 0, n + 1, 0);Arrays.fill(indegree, 0, n + 1, 0);queue.clear();}//添加边的函数(同时统计入度)private static void addEdge(int fro, int t){next[cnt] = head[fro];to[cnt] = t;head[fro] = cnt++;indegree[t]++;}public int[] loudAndRich(int[][] richer, int[] quiet) {//建图int len = quiet.length;build(len);for(int[] edge : richer){addEdge(edge[0], edge[1]);}//初始化一个答案数组int[] ans = new int[len];for(int i = 0; i < len; i++){ans[i] = i;}//跑拓扑排序topoSort(ans, quiet);return ans;}private static void topoSort(int[] ans, int[] quiet){//首先找到0入度的点加入队列for(int i = 0; i < curN; i++){if(indegree[i] == 0){queue.offer(i);}}//遍历图进行ans数组的修改while(!queue.isEmpty()){int curPointer = queue.poll();for(int ei = head[curPointer]; ei > 0; ei = next[ei]){if(--indegree[to[ei]] == 0){queue.offer(to[ei]);}ans[to[ei]] = quiet[ans[curPointer]] < quiet[ans[to[ei]]] ? ans[curPointer] : ans[to[ei]];}}}
}

并行课程

其实就是一个简单的拓扑排序, 建图的时候我们使用了一个新的辅助空间
代码实现如下


class Cource {private static final int MAXN = 50001;private static final int MAXM = 50001;private static final int[] head = new int[MAXN];private static final int[] next = new int[MAXM];private static final int[] to = new int[MAXM];private static final int[] indegree = new int[MAXN];private static int curN = 0;private static int cnt = 0;private static int res = Integer.MIN_VALUE;private static final Queue<Integer> queue = new ArrayDeque<>();//专属于本题的辅助数组(用于结算新加的time)private static final int[] need = new int[MAXN];private static final int[] ans = new int[MAXN];//初始化图的函数private static void build(int n, int[] time){cnt = 1;curN = n;res = Integer.MIN_VALUE;Arrays.fill(head, 0, n + 1, 0);Arrays.fill(need, 0, n + 1, 0);Arrays.fill(indegree, 0, n + 1, 0);queue.clear();//初始化ans数组for(int i = 1; i <= time.length; i++){res = Math.max(res, time[i - 1]);ans[i] = time[i - 1];}}//添加边的函数(顺带统计入度)private static void addEdge(int fro, int t){next[cnt] = head[fro];to[cnt] = t;head[fro] = cnt++;indegree[t]++;}public int minimumTime(int n, int[][] relations, int[] time) {//初始化并建图build(n, time);for(int[] edge : relations){addEdge(edge[0], edge[1]);}//下面就是拓扑排序的主流程topoSort();return res;}private static void topoSort(){//首先添加入度为0的点入队列for(int i = 1; i <= curN; i++){if(indegree[i] == 0){queue.offer(i);}}//开始进行拓扑排序的主流程while(!queue.isEmpty()){int courceNum = queue.poll();if(indegree[courceNum] == 0){//说明这个时候可以结算了ans[courceNum] += need[courceNum];res = Math.max(res, ans[courceNum]);}else{for(int ei = head[courceNum]; ei > 0; ei = next[ei]){need[to[ei]] = Math.max(need[to[ei]], ans[courceNum]);if(--indegree[to[ei]] == 0){queue.offer(to[ei]);}}}}}
}

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

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

相关文章

IMS 在线计费 IMS 离线计费

目录 1. IMS 在线计费 1.1 主要内容 1.2 IMS 在线计费架构 ​编辑1.3 IMS 在线计费方案 1.4 IMS 在线计费的关键步骤 1.5 在线计费的基本流程 1.6 IMS Information AVP 2. IMS 离线计费 2.1 IMS 离线计费架构 2.2 IMS 离线计费概述 2.3 什么时候 AS 给 CG 发送 ACR?…

深度学习:基础知识

深度学习是机器学习的一个领域 神经网络构造 一个神经元有n个输入&#xff0c;每一个输入对应一个权值w&#xff0c;神经元内会对输入与权重做乘法后求和。 感知器 由两层神经元组成的神经网络--“感知器”&#xff08;Perceptron&#xff09;,感知器只能线性划分数据。 公式…

生成式人工智能在无人机群中的应用、挑战和机遇

人工智能咨询培训老师叶梓 转载标明出处 无人机群在执行人类难以或危险任务方面有巨大潜力&#xff0c;但在复杂动态环境中学习和协调大量无人机的移动和行动&#xff0c;对传统AI方法来说是重大挑战。生成式人工智能&#xff08;Generative AI, GAI&#xff09;&#xff0c;凭…

实例讲解电动汽车钥匙Start上下电控制策略及Simulink建模方法

在电动汽车VCU开发中&#xff0c;上下电控制是其中一个核心控制内容&#xff0c;也是其他控制功能的基础&#xff0c;在钥匙ON挡上电后&#xff0c;整车电池主回路高压供电接通&#xff0c;但此时车辆电驱动回路尚未接通高压&#xff0c;如果要达到车辆具备行车准备就绪状态&am…

Qt_按钮类控件

目录 1、QAbstractButton 2、设置带图标的按钮 3、设置带有快捷键的按钮 4、QRadioButtion&#xff08;单选按钮&#xff09; 4.1 QButtonGroup 5、QCheckBox 结语 前言&#xff1a; 按钮类控件是Qt中最重要的控件类型之一&#xff0c;该类型的控件可以通过鼠标的点击…

pdf文件怎么直接翻译?使用这些工具让翻译变得简单

在全球化日益加深的职场环境中&#xff0c;处理外语PDF文件成为了许多职场人士面临的共同挑战。 面对这些“加密”的信息宝库&#xff0c;如何高效、准确地将英文pdf翻译成对应语言&#xff0c;成为了提升工作效率的关键。 以下是几款在PDF翻译领域表现出色的软件&#xff0c…

python基础知识(六)--字典遍历、公共运算符、公共方法、函数、变量分类、参数分类、拆包、引用

字典遍历方法 函数名含义keys()以列表的形式&#xff0c;返回一个字典所有的键。values()以列表的形式&#xff0c;返回一个字典所有的值。items()返回由键值组成的序列&#xff0c;主要应用于遍历字典。 公共运算符 运算符描述支持的容器类型合并字符串、列表、元组*复制字符…

沟通更高效:微信群转移至企业微信操作攻略!

微信群转移到企业微信并不难&#xff0c;具体操作如下&#xff1a; 打开移动端企业微信主页&#xff0c;找到微信聊天栏中的【接收微信中的工作消息】&#xff1b; 点击【前往微信选择群聊】&#xff0c; 跳转到微信&#xff1b; 选择微信上的工作群聊&#xff0c;只能选择作…

K8S容器实例Pod安装curl-vim-telnet工具

在没有域名的情况下&#xff0c;有时候需要调试接口等需要此工具 安装curl、telnet、vim等 直接使用 apk add curlapk add vimapk add tennet

性能优化一:oracle 锁的原则

文章目录 锁的原则查看具体会话阻塞过程 锁的原则 1、只有被修改时,行才会被锁定。 2、当条语句修改了一条记录,只有这条记录上被锁定,在Oracle数据库中不存在锁升 3、当某行被修改时 &#xff0c;它将阻塞别人对它的修改。 4、当一个事务修改一行时.将在这个行上加上行锁(TX…

大佬,简单解释下“嵌入式软件开发”和“嵌入式硬件开发”的区别

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;首先&#xff0c;嵌入式硬…

zabbix之钉钉告警

钉钉告警设置 我们可以将同一个运維组的人员加入到同一个钉钉工作群中&#xff0c;当有异常出现后&#xff0c;Zabbix 将告警信息发送到钉钉的群里面&#xff0c;此时&#xff0c;群内所有的运维人员都能在第一时间看到这则告警详细。 Zabbix 监控系统默认没有开箱即用…

React学习day08-useReducer、useMemo、memo、useCallback、forwardRef、useInperativeHandle

15、useReducer 1&#xff09;作用&#xff1a;用来管理相对复杂的状态数据&#xff0c;类似于useState 2&#xff09;使用步骤&#xff08;传递一般的参数&#xff09;&#xff08;在APP.js中&#xff09;&#xff1a; ①定义一个reducer函数&#xff0c;在函数中通过switc…

Linux——k8s认识

计算资源隔离 - 更方便进行高并发架构的维护和升级 - 架构管理的灵活性更高&#xff0c;不再以单个节点的物理资源作为基础 技术&#xff1a; - 硬件辅助虚拟化 - 容器技术 在企业部署方案中&#xff0c;很少以单节点实现虚拟化和容器技术&#xff0c;一般以集群状态来运…

68 - I. 二叉搜索树的最近公共祖先

comments: true difficulty: 简单 edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9868%20-%20I.%20%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88/README.md 面试题…

MySQL高阶1873-计算特殊奖金

目录 题目 准备数据 分析数据 总结 题目 编写解决方案&#xff0c;计算每个雇员的奖金。如果一个雇员的 id 是 奇数 并且他的名字不是以 M 开头&#xff0c;那么他的奖金是他工资的 100% &#xff0c;否则奖金为 0 。 返回的结果按照 employee_id 排序。 准备数据 Crea…

【Python语言初识(一)】

一、python简史 1.1、python的历史 1989年圣诞节&#xff1a;Guido von Rossum开始写Python语言的编译器。1991年2月&#xff1a;第一个Python编译器&#xff08;同时也是解释器&#xff09;诞生&#xff0c;它是用C语言实现的&#xff08;后面&#xff09;&#xff0c;可以调…

Python编码系列—Python代理模式:为对象赋予超能力的魔法

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

数据结构(Day14)

一、学习内容 结构体 概念 引入&#xff1a;定义整数赋值为10 int a10; 定义小数赋值为3.14 float b3.14; 定义5个整数并赋值 int arr[5] {1 , 2 , 3 , 4 ,5}; 定义一个学生并赋值学号姓名成绩 定义一个雪糕并赋值名称产地单价 问题&#xff1a;没有学生、雪糕 数据类型 解决&…

Python语言学习-pandas库学习

一、什么是Pandas库 Pandas是python的第三方库&#xff0c;他用于灵活的数据操作&#xff0c;数据可视化&#xff0c;数据清洗&#xff0c;数据的聚合和转换&#xff0c;数据的可视化 二、安装pandas库 在终端中运行 pip install pandas 导入Pandas库并重命名为pd import …