【图】概念、存储结构、广度优先遍历遍历、深度优先遍历 - 详解

news2025/1/11 23:46:54

目录

前言

一、图

1.1、基本概念

二、图的存储结构

2.1、存储结构

2.1、邻接矩阵(考察重点)

2.1.1、代码实现

2.2、邻接表

2.3.1、无向邻接表存储

2.3.2、有向图邻接表存储

3.1、图的广度优先遍历(层序遍历)

3.2、图的深度优先遍历


前言


本章主要讲的是图的基本概念以及应用,面试的时候基本不考图~

一、图

1.1、基本概念

图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E),具体的:

  • 顶点集合V = {x|x属于某个数据对象集}是有穷非空集合;
  • E = {(x,y)|x,y属于V}或者E = {|x,y属于V && Path(x, y)}是顶点间关系的有穷集合,也叫做边的集 合。

(x, y)表示 x 到 y 的一条双向路径,即(x, y)是无方向的;Path<x, y>表示从x到y的一条单向通路,即Path是有方向的。例如下图:

顶点和边:图中结点称为顶点,第i个顶点记作vi。两个顶点vi和vj相关联称作顶点vi和顶点vj之间有一条边, 图中的第k条边记作ek,ek = (vi,vj)或,<vi, vj>。例如下图:

有向图和无向图:

  • 在有向图中,顶点对是有序的,顶点对,y>称为顶点x到顶点y的一条边(弧),和是两条不同的边;
  • 在无向图中,顶点对(x, y)是无序的,顶点对(x,y)称为顶点x和顶点y相关联的一条边,这条边没有特定方向,(x,  y)和(y,x)是同一条边;

例如下图:

完全图:

  • 在有n个顶点的无向图中,若有n * (n - 1) / 2条边,即任意两个顶点之间有且仅有一条边,则称此图为 无向完全图;
  • 在n个顶点的有向图中,若有n * (n-1)条边,即任意两个顶点之间有且仅有方向 相反的边,则称此图为有向完全图;

例如下图:

邻接顶点:

  • 在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u,v)依附于顶点u和v;
  • 在有向图G中,若是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接自顶点u,并称边与顶点u和顶点v相关联。

顶点的度:

顶点v的度是指与它相关联的边的条数(类似树的度),记作deg(v)。在有向图中,顶点的度等于该顶点的入度与 出度之和,其中顶点v的入度是以v为终点的有向边的条数,记作indev(v);顶点v的出度是以v为起始点的有向 边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注意:对于无向图,顶点的度等于该顶 点的入度和出度,即dev(v) = indev(v) = outdev(v)。

路径:在图G = (V, E)中,若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶点序列为从 顶点vi到顶点vj的路径。

路径长度:

  • 对于不带权的图,一条路径的路径长度是指该路径上的边的条数;
  • 对于带权的图,一条路径的路 径长度是指该路径上各个边权值的总和。

例如下图:

简单路径与回路:若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路径。回路或环:路径上第一个顶点v1和最后一个顶点vm重合。

例如下图:

 子图:设图G = {V, E}和图G1 = {V1,E1},若V1属于V且E1属于E,则称G1是G的子图

例如下图:

连通图:无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。

例如下图:

强连通图:有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到 vi的路 径,则称此图是强连通图。

例如下图:

生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条 边。

二、图的存储结构


2.1、存储结构

在图的存储中,只需要保存:节点和边的关系即可~

2.1、邻接矩阵(考察重点)

邻接矩阵就是用矩阵(二维数组)来表示图的节点和边的关系,其中用 1 表示连通,0表示不连通.

如下无权值:

如下有权值:

2.1.1、代码实现

import java.util.Arrays;

public class GraphByMatrix {

    private char[] arrayV;//顶点数组
    private int[][] matrix;//邻接矩阵
    private boolean isDirect;//是否是有向图

    /**
     * @param size 代表当前顶点的个数
     * @param isDirect
     */
    public GraphByMatrix(int size, boolean isDirect) {
        this.arrayV = new char[size];
        matrix = new int[size][size];
        //这里约定:统一初始化为最大值
        for(int i = 0; i < size; i++) {
            Arrays.fill(matrix[i], Constant.MAX);
        }
        this.isDirect = isDirect;
    }

    /**
     * 初始化顶点
     * @param array
     */
    public void initArrayV(char[] array) {
        for(int i = 0; i < array.length; i++) {
            arrayV[i] = array[i];
        }
    }

    /**
     * 增加边
     * @param srcV 起点
     * @param destV 终点
     * @param weight 权值
     */
    public void addEdge(char srcV, char destV, int weight) {
        int srcIndex = getIndexOfV(srcV);
        int destIndex = getIndexOfV(destV);

        matrix[srcIndex][destIndex] = weight;
        //如果是无向图,那么相反的位置,也同样需要置为空
        if(!isDirect) {
            matrix[destIndex][srcIndex] = weight;
        }
    }

    /**
     * 获取顶点的下标
     * @param v
     * @return
     */
    private int getIndexOfV(char v) {
        for(int i = 0; i < arrayV.length; i++) {
            if(arrayV[i] == v)
            return i;
        }
        return -1;
    }

    /**
     * 获取顶点的度,有向图 = 入度 + 出度
     * @return
     */
    public int getDevofV(char v) {
        int count = 0;
        int srcIndex = getIndexOfV(v);
        for(int i = 0; i < arrayV.length; i++) {
            if(matrix[srcIndex][i] != Constant.MAX) {
                count++;
            }
        }

        //计算有向图的入度
        if(isDirect) {
            for(int i = 0; i < arrayV.length; i++) {
                if(matrix[i][srcIndex] != Constant.MAX) {
                    count++;
                }
            }
        }
        return count;
    }

    //打印数组
    public void printGraph() {
        for(int i = 0; i < matrix.length; i++) {
            for(int j = 0; j < matrix[i].length; j++) {
                if(matrix[i][j] == Constant.MAX) {
                    System.out.print("∞ ");
                } else {
                    System.out.print(matrix[i][j] + " ");
                }
            }
            System.out.println();
        }
    }

    //测试
    public static void main(String[] args) {
        GraphByMatrix graph = new GraphByMatrix(4, true);
        char[] array = {'A', 'B', 'C', 'D'};
        graph.initArrayV(array);

        graph.addEdge('A', 'B', 1);
        graph.addEdge('A', 'D', 1);
        graph.addEdge('B', 'A', 1);
        graph.addEdge('B', 'C', 1);
        graph.addEdge('C', 'B', 1);
        graph.addEdge('C', 'D', 1);
        graph.addEdge('D', 'A', 1);
        graph.addEdge('D', 'C', 1);

        graph.printGraph();
        //获取 A 的度
        System.out.println(graph.getDevofV('A'));
    }

}

 

2.2、邻接表

邻接表:使用数组表示顶点的集合,使用链表表示边的关系 。

2.3.1、无向邻接表存储

Ps:无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度,只需要知道顶点vi边链表集 合中结点的数目即可。 

2.3.2、有向图邻接表存储

Ps:有向图中每条边在邻接表中只出现一次,与顶点vi对应的邻接表所含结点的个数,就是该顶点的 出度,也称出度表,要得到vi顶点的入度,必须检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i。

import java.util.ArrayList;

public class GraphByNode {

    static class Node {
        public int src;//起始位置
        public int dest;//目标位置
        public int weight;//权重
        public Node next;

        public Node(int src, int dest, int weight) {
            this.src = src;
            this.dest = dest;
            this.weight = weight;
        }
    }

    public char[] arrayV;
    public ArrayList<Node> edgList;//存储边
    public boolean isDirect;

    public GraphByNode(int size, boolean isDirect) {
        this.arrayV = new char[size];
        edgList = new ArrayList<>(size);
        for(int i = 0; i < size; i++) {
            edgList.add(null);
        }
        this.isDirect = isDirect;
    }

    /**
     * 初始化顶点
     * @param array
     */
    public void initArrayV(char[] array) {
        for(int i = 0; i < array.length; i++) {
            arrayV[i] = array[i];
        }
    }

    /**
     * 增加边
     * @param srcV 起点
     * @param destV 终点
     * @param weight 权值
     */
    public void addEdge(char srcV, char destV, int weight) {
        int srcIndex = getIndexOfV(srcV);
        int destIndex = getIndexOfV(destV);
        addEdgeChild(srcIndex, destIndex, weight);

        //无向图需要添加两条边
        if(!isDirect) {
            addEdgeChild(destIndex, srcIndex, weight);
        }
    }

    private void addEdgeChild(int srcIndex, int destIndex, int weight) {
        //这里拿到的是头结点
        Node cur = edgList.get(srcIndex);
        while(cur != null) {
            if(cur.dest == destIndex) {
                return;
            }
            cur = cur.next;
        }
        //之前没有存储过这条边
        Node node = new Node(srcIndex, destIndex, weight);
        node.next = edgList.get(srcIndex);
        edgList.set(srcIndex, node);
    }

    /**
     * 获取顶点的下标
     * @param v
     * @return
     */
    private int getIndexOfV(char v) {
        for(int i = 0; i < arrayV.length; i++) {
            if(arrayV[i] == v)
                return i;
        }
        return -1;
    }

    //获取顶点的度
    public int getDevOfv(char v) {
        int count = 0;
        int srcIndex = getIndexOfV(v);
        Node cur = edgList.get(srcIndex);
        while(cur != null) {
            count++;
            cur = cur.next;
        }

        //有向图
        if(isDirect) {
            int destIndex = srcIndex;
            for(int i = 0; i < arrayV.length; i++) {
                if(i == destIndex) {
                    continue;
                } else {
                    Node pCur = edgList.get(i);
                    while(pCur != null) {
                        if(pCur.dest == destIndex) {
                            count++;
                        }
                        pCur = pCur.next;
                    }
                }
            }
        }
        return count;
    }


    public void printGraph() {
        for(int i = 0; i < arrayV.length; i++) {
            System.out.print(arrayV[i] + "->");
            Node cur = edgList.get(i);
            while(cur != null) {
                System.out.print(arrayV[cur.dest] + " ->");
                cur = cur.next;
            }
            System.out.println();
        }
    }

    //测试
    public static void main(String[] args) {
        GraphByNode graph = new GraphByNode(4, true);
        char[] array = {'A', 'B', 'C', 'D'};
        graph.initArrayV(array);

        graph.addEdge('A', 'B', 1);
        graph.addEdge('A', 'D', 1);
        graph.addEdge('B', 'A', 1);
        graph.addEdge('B', 'C', 1);
        graph.addEdge('C', 'B', 1);
        graph.addEdge('C', 'D', 1);
        graph.addEdge('D', 'A', 1);
        graph.addEdge('D', 'C', 1);
        System.out.println("getDevOfV:" + graph.getDevOfv('A'));
        graph.printGraph();
    }

}

3.1、图的广度优先遍历(层序遍历)

 类似于二叉树的层序遍历,也需要使用队列,首先将第一个元素(假设如上图中的B)放入队列,同时使用一个 boolean 类型数组标记这个元素已被放入(这样做是为了防止一个元素被多次放入),接着出队(将 B 弹出),将出队的元素的连通的结点(A,C结点)经检验不重复后(boolean 类型数组检验是否之前被放入过),再次放入队列,依次往复即可~


import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class GraphByMatrix {

    private char[] arrayV;//顶点数组
    private int[][] matrix;//邻接矩阵
    private boolean isDirect;//是否是有向图

    /**
     * @param size 代表当前顶点的个数
     * @param isDirect
     */
    public GraphByMatrix(int size, boolean isDirect) {
        this.arrayV = new char[size];
        matrix = new int[size][size];
        //这里约定:统一初始化为最大值
        for(int i = 0; i < size; i++) {
            Arrays.fill(matrix[i], Constant.MAX);
        }
        this.isDirect = isDirect;
    }

    /**
     * 初始化顶点
     * @param array
     */
    public void initArrayV(char[] array) {
        for(int i = 0; i < array.length; i++) {
            arrayV[i] = array[i];
        }
    }

    /**
     * 增加边
     * @param srcV 起点
     * @param destV 终点
     * @param weight 权值
     */
    public void addEdge(char srcV, char destV, int weight) {
        int srcIndex = getIndexOfV(srcV);
        int destIndex = getIndexOfV(destV);

        matrix[srcIndex][destIndex] = weight;
        //如果是无向图,那么相反的位置,也同样需要置为空
        if(!isDirect) {
            matrix[destIndex][srcIndex] = weight;
        }
    }

    /**
     * 获取顶点的下标
     * @param v
     * @return
     */
    private int getIndexOfV(char v) {
        for(int i = 0; i < arrayV.length; i++) {
            if(arrayV[i] == v)
            return i;
        }
        return -1;
    }

    /**
     * 获取顶点的度,有向图 = 入度 + 出度
     * @return
     */
    public int getDevofV(char v) {
        int count = 0;
        int srcIndex = getIndexOfV(v);
        for(int i = 0; i < arrayV.length; i++) {
            if(matrix[srcIndex][i] != Constant.MAX) {
                count++;
            }
        }

        //计算有向图的入度
        if(isDirect) {
            for(int i = 0; i < arrayV.length; i++) {
                if(matrix[i][srcIndex] != Constant.MAX) {
                    count++;
                }
            }
        }
        return count;
    }

    //打印数组
    public void printGraph() {
        for(int i = 0; i < matrix.length; i++) {
            for(int j = 0; j < matrix[i].length; j++) {
                if(matrix[i][j] == Constant.MAX) {
                    System.out.print("∞ ");
                } else {
                    System.out.print(matrix[i][j] + " ");
                }
            }
            System.out.println();
        }
    }


    //广度优先遍历
    public void bfs(char v) {
        //数组用来标记顶点是否被使用过
        boolean[] visited = new boolean[arrayV.length];

        //定义一个队列,来辅助完成 bfs
        Queue<Integer> queue = new LinkedList<>();
        int srcIndex = getIndexOfV(v);
        queue.offer(srcIndex);
        while(!queue.isEmpty()) {
            int top = queue.poll();
            System.out.print(arrayV[top] + "->");
            visited[top] = true;//每次弹出一个元素就置为 true
            for(int i = 0; i < arrayV.length; i++) {
                if(matrix[top][i] != Constant.MAX && !visited[i]) {
                    queue.offer(i);
                    visited[i] = true;//每次入队就置为true
                }
            }
        }
    }

    //测试
    public static void main(String[] args) {
        GraphByMatrix graph = new GraphByMatrix(4, false);
        char[] array = {'A', 'B', 'C', 'D'};
        graph.initArrayV(array);

        graph.addEdge('A', 'B', 1);
        graph.addEdge('A', 'D', 1);
        graph.addEdge('B', 'A', 1);
        graph.addEdge('B', 'C', 1);
        graph.addEdge('C', 'B', 1);
        graph.addEdge('C', 'D', 1);
        graph.addEdge('D', 'A', 1);
        graph.addEdge('D', 'C', 1);

        graph.bfs('B');
    }

}

3.2、图的深度优先遍历

 

类似于二叉树的前序遍历,通过邻接矩阵进行优先遍历,例如起始位置为上图中的 B,那么上图中右边的邻接矩阵从竖列 B 元素开始向右,找到第一个不为 0 的,也就是通向 A 元素,那么接着再从竖列 A 元素开始向右,找到第一个不为 0 的,这里是 B 元素,但由于之前通向过 B 元素所以继续向右找不为 0 的,于此类推,最后得出 dfs 结果,如下代码

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class GraphByMatrix {

    private char[] arrayV;//顶点数组
    private int[][] matrix;//邻接矩阵
    private boolean isDirect;//是否是有向图

    /**
     * @param size 代表当前顶点的个数
     * @param isDirect
     */
    public GraphByMatrix(int size, boolean isDirect) {
        this.arrayV = new char[size];
        matrix = new int[size][size];
        //这里约定:统一初始化为最大值
        for(int i = 0; i < size; i++) {
            Arrays.fill(matrix[i], Constant.MAX);
        }
        this.isDirect = isDirect;
    }

    /**
     * 初始化顶点
     * @param array
     */
    public void initArrayV(char[] array) {
        for(int i = 0; i < array.length; i++) {
            arrayV[i] = array[i];
        }
    }

    /**
     * 增加边
     * @param srcV 起点
     * @param destV 终点
     * @param weight 权值
     */
    public void addEdge(char srcV, char destV, int weight) {
        int srcIndex = getIndexOfV(srcV);
        int destIndex = getIndexOfV(destV);

        matrix[srcIndex][destIndex] = weight;
        //如果是无向图,那么相反的位置,也同样需要置为空
        if(!isDirect) {
            matrix[destIndex][srcIndex] = weight;
        }
    }

    /**
     * 获取顶点的下标
     * @param v
     * @return
     */
    private int getIndexOfV(char v) {
        for(int i = 0; i < arrayV.length; i++) {
            if(arrayV[i] == v)
            return i;
        }
        return -1;
    }

    /**
     * 获取顶点的度,有向图 = 入度 + 出度
     * @return
     */
    public int getDevofV(char v) {
        int count = 0;
        int srcIndex = getIndexOfV(v);
        for(int i = 0; i < arrayV.length; i++) {
            if(matrix[srcIndex][i] != Constant.MAX) {
                count++;
            }
        }

        //计算有向图的入度
        if(isDirect) {
            for(int i = 0; i < arrayV.length; i++) {
                if(matrix[i][srcIndex] != Constant.MAX) {
                    count++;
                }
            }
        }
        return count;
    }

    //打印数组
    public void printGraph() {
        for(int i = 0; i < matrix.length; i++) {
            for(int j = 0; j < matrix[i].length; j++) {
                if(matrix[i][j] == Constant.MAX) {
                    System.out.print("∞ ");
                } else {
                    System.out.print(matrix[i][j] + " ");
                }
            }
            System.out.println();
        }
    }


    //广度优先遍历
    public void bfs(char v) {
        //数组用来标记顶点是否被使用过
        boolean[] visited = new boolean[arrayV.length];

        //定义一个队列,来辅助完成 bfs
        Queue<Integer> queue = new LinkedList<>();
        int srcIndex = getIndexOfV(v);
        queue.offer(srcIndex);
        while(!queue.isEmpty()) {
            int top = queue.poll();
            System.out.print(arrayV[top] + "->");
            visited[top] = true;//每次弹出一个元素就置为 true
            for(int i = 0; i < arrayV.length; i++) {
                if(matrix[top][i] != Constant.MAX && !visited[i]) {
                    queue.offer(i);
                    visited[i] = true;//每次入队就置为true
                }
            }
        }
    }

    // 深度优先遍历
    public void dfs(char v) {
        //数组用来标记顶点是否被使用过
        boolean[] visited = new boolean[arrayV.length];
        int srcIndex = getIndexOfV(v);
        dfsChild(srcIndex, visited);
    }

    private void dfsChild(int srcIndex, boolean[] visited) {
        System.out.print(arrayV[srcIndex] + "->");
        visited[srcIndex] = true;
        for(int i = 0; i < arrayV.length; i++) {
            if(matrix[srcIndex][i] != Constant.MAX && !visited[i]) {
                dfsChild(i, visited);
            }
        }
    }

    //测试
    public static void main(String[] args) {
        GraphByMatrix graph = new GraphByMatrix(4, false);
        char[] array = {'A', 'B', 'C', 'D'};
        graph.initArrayV(array);

        graph.addEdge('A', 'B', 1);
        graph.addEdge('A', 'D', 1);
        graph.addEdge('B', 'A', 1);
        graph.addEdge('B', 'C', 1);
        graph.addEdge('C', 'B', 1);
        graph.addEdge('C', 'D', 1);
        graph.addEdge('D', 'A', 1);
        graph.addEdge('D', 'C', 1);

//        graph.bfs('B');
        graph.dfs('B');
    }
}

 

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

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

相关文章

Wails + Go 实现图形化桌面应用

效果展示 编写一个热点查看程序&#xff0c;包含百度热搜、微博热搜、头条、知乎等&#xff0c;废话不说上效果图&#xff1a; 效果图1&#xff1a; 效果图2 打包大小 涉及技术点 Golang 使用golang 1.9 编写代码 Wails vue3 使用Wails技术实现GUI渲染&#xff0c;页…

网络——网络协议总结

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。 个人主页&#xff1a;小李会科技的…

笔记本安装CentOS

目标: 1.利用闲置笔记本 2.省电/提高利用率/不安装图形桌面/最小化安装/附加选项:开发工具 step1&#xff1a;镜像下载 CentOS-7.9 163镜像 阿里云镜像 清华大学镜像 随便选一个 step2: 下载U盘系统盘制作工具Rufus U盘写入镜像/安装 step3: 安装完毕进入系统 …

2023年上半年软考学习总结(超详细)

目录 前言 一、背景1.1上次考试感受&#xff1a;1.2这次考试感受&#xff1a;1.3方法&#xff1a; 二、 过程2.1计算机网络概论计算机组成数据表示相关知识校验码相关知识计算机体系结构网络体系结构OSI/RM和TCP/IP计算机安全性可靠性性能评价 2.2 程序设计语言基础知识编译和解…

如何在华为OD机试中获得满分?Java实现【求最小公倍数】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

【Unity3D】广告牌特效

1 前言 广告牌特效是指&#xff1a;空间中的一个 2D 对象始终&#xff08;或尽可能&#xff09;面向相机&#xff0c;使得用户能够尽可能看清楚该 2D 物体。广告牌特效一共有以下 3 种&#xff1a; 正视广告牌&#xff1a;广告牌始终以正视图姿态面向相机&#xff0c;即广告牌…

异常检测进阶梳理1:Tabular AD视角

接触异常检测领域也有一年多的时间了&#xff0c;过程中遇到不少坑&#xff0c;知识体系也在不断更新完善&#xff0c;这里以专题的形式进行知识体系的梳理~ 异常检测&#xff08;Anomaly Detection, AD&#xff09;领域内的划分体系较多&#xff0c;这里基于异常检测最常用到的…

【P39】JMeter 随机顺序控制器(Random Order Controller)

文章目录 一、随机顺序控制器&#xff08;Random Order Controller&#xff09;参数说明二、测试计划设计2.1、测试计划一2.2、测试计划二 一、随机顺序控制器&#xff08;Random Order Controller&#xff09;参数说明 可以让控制器内部的组件按随机顺序执行&#xff08;内部…

Alibaba Arthas学习与使用

Alibaba Arthas学习与使用 目录 下载安装卸载退出快捷键重点部分: 命令 dashboardthreadjvmsyspropsysenvvmoptiongetstaticognlscsmjadmcredefinedumpclassloadermonitorwatchtracestackttoptionsprofiler 下载安装 # 下载 curl -O https://alibaba.github.io/arthas/art…

Apache Kafka - 如何实现可靠的数据传递

文章目录 可靠的数据传递导图 可靠的数据传递 Kafka 通过以下几个方面实现可靠的数据传递: 分区副本 - Kafka 的分区有多个副本,如果某个副本失效,其他副本可以继续服务。生产者重试 - 生产者在发送消息失败时会自动重试,一直到成功发送或者达到最大重试次数。批量确认 - 生产…

云服务器和专用服务器之间的区别

在当今数字化时代&#xff0c;服务器是构建和支持各种应用和服务的基础设施之一。随着技术的发展和需求的增加&#xff0c;出现了不同类型的服务器&#xff0c;其中最常见的是云服务器和专用服务器。本文将详细介绍云服务器和专用服务器之间的区别&#xff0c;以帮助您更好地了…

SpringSecurity从入门到实战

SpringSecurity从入门到实战 0. 简介 ​ Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 ​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有…

【LAMP架构】

目录 一、LAMP架构1、组件作用 二、编译安装Apache httpd服务2、安装环境依赖包3、配置软件模块4、编译及安装5、优化配置文件路径&#xff0c;并把httpd服务的可执行程序文件放入路径环境变量的目录中便于系统识别6.添加httpd系统服务7.修改httpd 服务配置文件8.浏览器访问验证…

【云原生|探索 Kubernetes 系列 5】简化 Kubernetes 的部署,深入解析其工作流程

前言 大家好&#xff0c;我是秋意零。 在前面 4 个章节中&#xff0c;我们充分了解了容器技术和 Kubernes 原生时代引擎的架构和设计思想&#xff0c;今天分享的主要内容是&#xff0c;探索 Kubernetes 部署&#xff0c;深入解析其工作流程 &#x1f47f; 简介 &#x1f3e0…

[元带你学: eMMC协议详解 11] Data transfer mode 数据传输模式

依JEDEC eMMC 5.1及经验辛苦整理&#xff0c;付费内容&#xff0c;禁止转载。 所在专栏 《元带你学: eMMC协议详解》 全文2300 字&#xff0c; 主要介绍数据传输模式&#xff0c;本节数据传输模式图非常重要。数据传输模式图可以说是我查对过最频繁的图之一了。eMMC 限定了这么…

数据库基础——6.排序与分页

这篇文章来讲一下数据库的排序与分页 目录 1.排序数据 1.1排序规则 1.2 单列排序 1.3 多列排序 2.分页 2.1 背景 2.2 实现规则 2.3 拓展 1.排序数据 1.1排序规则 使用 ORDER BY 子句排序 ASC&#xff08;ascend&#xff09;&#xff1a;升序 &#xff1b; DESC&a…

怎么一键保存浏览器中打开的所有标签页?

2023年5月28日&#xff0c;周日晚上&#xff1a; 被这个问题困扰很久了&#xff0c;之前一直不知道怎么全部保存浏览器中打开的所有标签页&#xff0c;浪费了不少的时间&#xff0c;今天下午偶然发现了解决办法。 很简单&#xff0c;直接在浏览器里使用快捷键“CtrlShiftD” …

本周大新闻|传Meta与Magic Leap谈专利授权;PS VR2前6周出货60万台

本周XR大新闻&#xff0c;AR方面&#xff0c;苹果XR项目核心高管曝光&#xff1b;传Meta与Magic Leap洽谈专利授权合作&#xff1b;歌尔光学公布新一代AR显示模组&#xff1b;Lumus公布二代波导Z-Lens最新细节&#xff1b;JBD X-cube发布全彩Micro LED光机Hummingbird&#xff…

我用GPT写了一个关于GPT的文章,大家看看写的如何

声明&#xff1a;以下内容来自GPT-3.5大模型&#xff08;图片除外&#xff09; 目录 I. 引言 1.1 研究背景和意义 1.2 现有研究综述 II. ChatGPT技术介绍 2.1 ChatGPT技术原理 2.2 ChatGPT技术优势 III. ChatGPT技术在智能客服中的应用和挑战 3.1 ChatGPT技术在智能客…

【TOP生物信息】使用Seurat包自带的方法进行单细胞类型注释

扫码关注下方公粽号&#xff0c;回复推文合集&#xff0c;获取400页单细胞学习资源&#xff01; 本文共计1318字&#xff0c;阅读大约需要4分钟&#xff0c;目录如下&#xff1a; 方法简介演示数据来源代码演示小结代码参考往期单细胞系统教程 单细胞自动注释细胞类型的软件和…