【数据结构与算法】JavaScript实现图结构

文章目录

      • 一、图论
        • 1.1.图的简介
        • 1.2.图的表示
          • 邻接矩阵
          • 邻接表
      • 二、封装图结构
        • 2.1.添加字典类和队列类
        • 2.2.创建图类
        • 2.3.添加顶点与边
        • 2.4.转换为字符串输出
        • 2.5.图的遍历
          • 广度优先搜索
          • 深度优先搜索
        • 2.6.完整实现

一、图论

1.1.图的简介

什么是图?

  • 图结构是一种与树结构有些相似的数据结构;
  • 图论是数学的一个分支,并且,在数学中,树是图的一种;
  • 图论以图为研究对象,研究顶点组成的图形的数学理论和方法;
  • 主要的研究目的为:事物之间的联系顶点代表事物代表两个事物间的关系

图的特点:

  • 一组顶点:通常用 V (Vertex)表示顶点的集合;
  • 一组边:通常用 E(Edge)表示边的集合;
    • 边是顶点和顶点之间的连线;
    • 边可以是有向的,也可以是无向的。比如A----B表示无向,A —> B 表示有向;

图的常用术语:

  • 顶点:表示图中的一个节点
  • 边:表示顶点和顶点给之间的连线
  • 相邻顶点:由一条边连接在一起的顶点称为相邻顶点
  • 度:一个顶点的相邻顶点的数量
  • 路径:
    • 简单路径: 简单路径要求不包含重复的顶点;
    • 回路:第一个顶点和最后一个顶点相同的路径称为回路;
  • 无向图:图中的所有边都是没有方向的;
  • 有向图:图中的所有边都是方向的;
  • 无权图: 无权图中的边没有任何权重意义;
  • 带权图: 带权图中的边有一定的权重含义;
1.2.图的表示
邻接矩阵

表示图的常用方式为:邻接矩阵

  • 可以使用二维数组来表示邻接矩阵;
  • 邻接矩阵让每个节点和一个整数相关联,该整数作为数组的下标值
  • 使用一个二维数组来表示顶点之间的连接

在这里插入图片描述

如上图所示:

  • 二维数组中的0表示没有连线,1表示有连线;
  • 如:A[ 0 ] [ 3 ] = 1,表示 A 和 C 之间有连接;
  • 邻接矩阵的对角线上的值都为0,表示A - A ,B - B,等自回路都没有连接(自己与自己之间没有连接);
  • 若为无向图,则邻接矩阵应为对角线上元素全为0的对称矩阵;

邻接矩阵的问题:

  • 如果图是一个稀疏图,那么邻接矩阵中将存在大量的 0,造成存储空间的浪费;
邻接表

另外一种表示图的常用方式为:邻接表

  • 邻接表由图中每个顶点以及和顶点相邻的顶点列表组成;
  • 这个列表可用多种方式存储,比如:**数组/链表/字典(哈希表)**等都可以;

在这里插入图片描述

如上图所示:

  • 图中可清楚看到A与B、C、D相邻,假如要表示这些与A顶点相邻的顶点(边),可以通过将它们作为A的值(value)存入到对应的数组/链表/字典中。
  • 之后,通过键(key)A可以十分方便地取出对应的数据;

邻接表的问题:

  • 邻接表可以简单地得出出度,即某一顶点指向其他顶点的个数;
  • 但是,邻接表计算入度(指向某一顶点的其他顶点的个数称为该顶点的入度)十分困难。此时需要构造逆邻接表才能有效计算入度;

二、封装图结构

在实现过程中采用邻接表的方式来表示边,使用字典类来存储邻接表。

2.1.添加字典类和队列类

首先需要引入之前实现的,之后会用到的字典类和队列类:

    //封装字典类
function Dictionary(){//字典属性this.items = {}//字典操作方法//一.在字典中添加键值对Dictionary.prototype.set = function(key, value){this.items[key] = value}//二.判断字典中是否有某个keyDictionary.prototype.has = function(key){return this.items.hasOwnProperty(key)}//三.从字典中移除元素Dictionary.prototype.remove = function(key){//1.判断字典中是否有这个keyif(!this.has(key)) return false//2.从字典中删除keydelete this.items[key]return true}//四.根据key获取valueDictionary.prototype.get = function(key){return this.has(key) ? this.items[key] : undefined}//五.获取所有keysDictionary.prototype.keys = function(){return Object.keys(this.items)}//六.size方法Dictionary.prototype.keys = function(){return this.keys().length}//七.clear方法Dictionary.prototype.clear = function(){this.items = {}}
}// 基于数组封装队列类function Queue() {// 属性this.items = []// 方法// 1.将元素加入到队列中Queue.prototype.enqueue = element => {this.items.push(element)}// 2.从队列中删除前端元素Queue.prototype.dequeue = () => {return this.items.shift()}// 3.查看前端的元素Queue.prototype.front = () => {return this.items[0]}// 4.查看队列是否为空Queue.prototype.isEmpty = () => {return this.items.length == 0;}// 5.查看队列中元素的个数Queue.prototype.size = () => {return this.items.length}// 6.toString方法Queue.prototype.toString = () => {let resultString = ''for (let i of this.items){resultString += i + ' '}return resultString}}
2.2.创建图类

先创建图类Graph,并添加基本属性,再实现图类的常用方法:

    //封装图类function Graph (){//属性:顶点(数组)/边(字典)this.vertexes = []  //顶点this.edges = new Dictionary() //边}
2.3.添加顶点与边

如图所示:

在这里插入图片描述

创建一个数组对象vertexes存储图的顶点;创建一个字典对象edges存储图的边,其中key为顶点,value为存储key顶点相邻顶点的数组。

代码实现:

      //添加方法//一.添加顶点Graph.prototype.addVertex = function(v){this.vertexes.push(v)this.edges.set(v, []) //将边添加到字典中,新增的顶点作为键,对应的值为一个存储边的空数组}//二.添加边Graph.prototype.addEdge = function(v1, v2){//传入两个顶点为它们添加边this.edges.get(v1).push(v2)//取出字典对象edges中存储边的数组,并添加关联顶点this.edges.get(v2).push(v1)//表示的是无向表,故要添加互相指向的两条边}
2.4.转换为字符串输出

为图类Graph添加toString方法,实现以邻接表的形式输出图中各顶点。

代码实现:

      //三.实现toString方法:转换为邻接表形式Graph.prototype.toString = function (){//1.定义字符串,保存最终结果let resultString = ""//2.遍历所有的顶点以及顶点对应的边for (let i = 0; i < this.vertexes.length; i++) {//遍历所有顶点resultString += this.vertexes[i] + '-->'let vEdges = this.edges.get(this.vertexes[i])for (let j = 0; j < vEdges.length; j++) {//遍历字典中每个顶点对应的数组resultString += vEdges[j] + '  ';}resultString += '\n'}return resultString}

测试代码:

   //测试代码//1.创建图结构let graph = new Graph()//2.添加顶点let myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']for (let i = 0; i < myVertexes.length; i++) {graph.addVertex(myVertexes[i])}//3.添加边graph.addEdge('A', 'B')graph.addEdge('A', 'C')graph.addEdge('A', 'D')graph.addEdge('C', 'D')graph.addEdge('C', 'G')graph.addEdge('D', 'G')graph.addEdge('D', 'H')graph.addEdge('B', 'E')graph.addEdge('B', 'F')graph.addEdge('E', 'I')//4.输出结果console.log(graph.toString());

测试结果:

在这里插入图片描述

2.5.图的遍历

图的遍历思想:

  • 图的遍历思想与树的遍历思想一样,意味着需要将图中所有的顶点都访问一遍,并且不能有重复的访问(上面的toString方法会重复访问);

遍历图的两种算法:

  • 广度优先搜索(Breadth - First Search,简称BFS);
  • 深度优先搜索(Depth - First Search,简称DFS);
  • 两种遍历算法都需要指定第一个被访问的顶点

为了记录顶点是否被访问过,使用三种颜色来表示它们的状态

  • 白色:表示该顶点还没有被访问过;
  • 灰色:表示该顶点被访问过,但其相邻顶点并未完全被访问过;
  • 黑色:表示该顶点被访问过,且其所有相邻顶点都被访问过;

首先封装initializeColor方法将图中的所有顶点初始化为白色,代码实现如下:

      //四.初始化状态颜色Graph.prototype.initializeColor = function(){let colors = []for (let i = 0; i < this.vertexes.length; i++) {colors[this.vertexes[i]] = 'white';}return colors}
广度优先搜索

广度优先搜索算法的思路:

  • 广度优先搜索算法会从指定的第一个顶点开始遍历图,先访问其所有的相邻顶点,就像一次访问图的一层;
  • 也可以说是先宽后深地遍历图中的各个顶点;

在这里插入图片描述

实现思路:

基于队列可以简单地实现广度优先搜索算法:

  • 首先创建一个队列Q(尾部进,首部出);
  • 调用封装的initializeColor方法将所有顶点初始化为白色;
  • 指定第一个顶点A,将A标注为灰色(被访问过的节点),并将A放入队列Q中;
  • 循环遍历队列中的元素,只要队列Q非空,就执行以下操作:
    • 先将灰色的A从Q的首部取出;
    • 取出A后,将A的所有未被访问过(白色)的相邻顶点依次从队列Q的尾部加入队列,并变为灰色。以此保证,灰色的相邻顶点不重复加入队列;
    • A的全部相邻节点加入Q后,A变为黑色,在下一次循环中被移除Q外;

代码实现:

      //五.实现广度搜索(BFS)//传入指定的第一个顶点和处理结果的函数Graph.prototype.bfs = function(initV, handler){//1.初始化颜色let colors = this.initializeColor()//2.创建队列let que = new Queue()//3.将顶点加入到队列中que.enqueue(initV)//4.循环从队列中取出元素,队列为空才停止while(!que.isEmpty()){//4.1.从队列首部取出一个顶点let v = que.dequeue()//4.2.从字典对象edges中获取和该顶点相邻的其他顶点组成的数组let vNeighbours = this.edges.get(v)//4.3.将v的颜色变为灰色colors[v] = 'gray'//4.4.遍历v所有相邻的顶点vNeighbours,并且加入队列中for (let i = 0; i < vNeighbours.length; i++) {const a = vNeighbours[i];//判断相邻顶点是否被探测过,被探测过则不加入队列中;并且加入队列后变为灰色,表示被探测过if (colors[a] == 'white') {colors[a] = 'gray'que.enqueue(a)}}//4.5.处理顶点vhandler(v)//4.6.顶点v所有白色的相邻顶点都加入队列后,将顶点v设置为黑色。此时黑色顶点v位于队列最前面,进入下一次while循环时会被取出colors[v] = 'black'}}

过程详解:

下为指定的第一个顶点为A时的遍历过程:

  • 如 a 图所示,将在字典edges中取出的与A相邻的且未被访问过的白色顶点B、C、D放入队列que中并变为灰色,随后将A变为黑色并移出队列;
  • 接着,如图 b 所示,将在字典edges中取出的与B相邻的且未被访问过的白色顶点E、F放入队列que中并变为灰色,随后将B变为黑色并移出队列;

在这里插入图片描述

  • 如 c 图所示,将在字典edges中取出的与C相邻的且未被访问过的白色顶点G(A,D也相邻不过已变为灰色,所以不加入队列)放入队列que中并变为灰色,随后将C变为黑色并移出队列;
  • 接着,如图 d 所示,将在字典edges中取出的与D相邻的且未被访问过的白色顶点H放入队列que中并变为灰色,随后将D变为黑色并移出队列。

在这里插入图片描述

如此循环直到队列中元素为0,即所有顶点都变黑并移出队列后才停止,此时图中顶点已被全部遍历。

测试代码:

    //测试代码//1.创建图结构let graph = new Graph()//2.添加顶点let myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']for (let i = 0; i < myVertexes.length; i++) {graph.addVertex(myVertexes[i])}//3.添加边graph.addEdge('A', 'B')graph.addEdge('A', 'C')graph.addEdge('A', 'D')graph.addEdge('C', 'D')graph.addEdge('C', 'G')graph.addEdge('D', 'G')graph.addEdge('D', 'H')graph.addEdge('B', 'E')graph.addEdge('B', 'F')graph.addEdge('E', 'I')//4.测试bfs遍历方法let result = ""graph.bfs(graph.vertexes[0], function(v){result += v + "-"})console.log(result);

测试结果:

在这里插入图片描述

可见,安装了广度优先搜索的顺序不重复地遍历了所有顶点。

深度优先搜索

广度优先算法的思路:

  • 深度优先搜索算法将会从指定的第一个顶点开始遍历图,沿着一条路径遍历直到该路径的最后一个顶点都被访问过为止;
  • 接着沿原来路径回退并探索下一条路径,即先深后宽地遍历图中的各个顶点;

在这里插入图片描述

实现思路:

  • 可以使用结构来实现深度优先搜索算法;
  • 深度优先搜索算法的遍历顺序与二叉搜索树中的先序遍历较为相似,同样可以使用递归来实现(递归的本质就是函数栈的调用)。

基于递归实现深度优先搜索算法:定义dfs方法用于调用递归方法dfsVisit,定义dfsVisit方法用于递归访问图中的各个顶点。

在dfs方法中:

  • 首先,调用initializeColor方法将所有顶点初始化为白色;
  • 然后,调用dfsVisit方法遍历图的顶点;

在dfsVisit方法中:

  • 首先,将传入的指定节点v标注为灰色
  • 接着,处理顶点V;
  • 然后,访问V的相邻顶点;
  • 最后,将顶点v标注为黑色;

代码实现:

      //六.实现深度搜索(DFS)Graph.prototype.dfs = function(initV, handler){//1.初始化顶点颜色let colors = this.initializeColor()//2.从某个顶点开始依次递归访问this.dfsVisit(initV, colors, handler)}//为了方便递归调用,封装访问顶点的函数,传入三个参数分别表示:指定的第一个顶点、颜色、处理函数Graph.prototype.dfsVisit = function(v, colors, handler){//1.将颜色设置为灰色colors[v] = 'gray'//2.处理v顶点handler(v)//3.访问V的相邻顶点let vNeighbours = this.edges.get(v)for (let i = 0; i < vNeighbours.length; i++) {let a = vNeighbours[i];//判断相邻顶点是否为白色,若为白色,递归调用函数继续访问if (colors[a] == 'white') {this.dfsVisit(a, colors, handler)}}//4.将v设置为黑色colors[v] = 'black'}

过程详解:

这里主要解释一下代码中的第3步操作:访问指定顶点的相邻顶点。

  • 以指定顶点A为例,先从储存顶点及其对应相邻顶点的字典对象edges中取出由顶点A的相邻顶点组成的数组:

在这里插入图片描述

  • 第一步:A顶点变为灰色,随后进入第一个for循环,遍历A白色的相邻顶点:B、C、D;在该for循环的第1次循环中(执行B),B顶点满足:colors == “white”,触发递归,重新调用该方法;
  • 第二步:B顶点变为灰色,随后进入第二个for循环,遍历B白色的相邻顶点:E、F;在该for循环的第1次循环中(执行E),E顶点满足:colors == “white”,触发递归,重新调用该方法;
  • 第三步:E顶点变为灰色,随后进入第三个for循环,遍历E白色的相邻顶点:I;在该for循环的第1次循环中(执行I),I顶点满足:colors == “white”,触发递归,重新调用该方法;
  • 第四步:I顶点变为灰色,随后进入第四个for循环,由于顶点I的相邻顶点E不满足:colors == “white”,停止递归调用。过程如下图所示:

在这里插入图片描述

  • 第五步:递归结束后一路向上返回,首先回到第三个for循环中继续执行其中的第2、3…次循环,每次循环的执行过程与上面的同理,直到递归再次结束后,再返回到第二个for循环中继续执行其中的第2、3…次循环…以此类推直到将图的所有顶点访问完为止。

下图为遍历图中各顶点的完整过程:

  • 发现表示访问了该顶点,状态变为灰色
  • 探索表示既访问了该顶点,也访问了该顶点的全部相邻顶点,状态变为黑色
  • 由于在顶点变为灰色后就调用了处理函数handler,所以handler方法的输出顺序为发现顶点的顺序即:A、B、E、I、F、C、D、G、H 。

在这里插入图片描述

测试代码:

    //测试代码//1.创建图结构let graph = new Graph()//2.添加顶点let myVertexes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']for (let i = 0; i < myVertexes.length; i++) {graph.addVertex(myVertexes[i])}//3.添加边graph.addEdge('A', 'B')graph.addEdge('A', 'C')graph.addEdge('A', 'D')graph.addEdge('C', 'D')graph.addEdge('C', 'G')graph.addEdge('D', 'G')graph.addEdge('D', 'H')graph.addEdge('B', 'E')graph.addEdge('B', 'F')graph.addEdge('E', 'I')//4.测试dfs遍历顶点let result = ""graph.dfs(graph.vertexes[0], function(v){result += v + "-"})console.log(result);

测试结果:

在这里插入图片描述

2.6.完整实现
    //封装图结构function Graph (){//属性:顶点(数组)/边(字典)this.vertexes = []  //顶点this.edges = new Dictionary() //边//方法//添加方法//一.添加顶点Graph.prototype.addVertex = function(v){this.vertexes.push(v)this.edges.set(v, []) //将边添加到字典中,新增的顶点作为键,对应的值为一个存储边的空数组}//二.添加边Graph.prototype.addEdge = function(v1, v2){//传入两个顶点为它们添加边this.edges.get(v1).push(v2)//取出字典对象edges中存储边的数组,并添加关联顶点this.edges.get(v2).push(v1)//表示的是无向表,故要添加互相指向的两条边}//三.实现toString方法:转换为邻接表形式Graph.prototype.toString = function (){//1.定义字符串,保存最终结果let resultString = ""//2.遍历所有的顶点以及顶点对应的边for (let i = 0; i < this.vertexes.length; i++) {//遍历所有顶点resultString += this.vertexes[i] + '-->'let vEdges = this.edges.get(this.vertexes[i])for (let j = 0; j < vEdges.length; j++) {//遍历字典中每个顶点对应的数组resultString += vEdges[j] + '  ';}resultString += '\n'}return resultString}//四.初始化状态颜色Graph.prototype.initializeColor = function(){let colors = []for (let i = 0; i < this.vertexes.length; i++) {colors[this.vertexes[i]] = 'white';}return colors}//五.实现广度搜索(BFS)//传入指定的第一个顶点和处理结果的函数Graph.prototype.bfs = function(initV, handler){//1.初始化颜色let colors = this.initializeColor()//2.创建队列let que = new Queue()//3.将顶点加入到队列中que.enqueue(initV)//4.循环从队列中取出元素while(!que.isEmpty()){//4.1.从队列中取出一个顶点let v = que.dequeue()//4.2.获取和顶点相相邻的其他顶点let vNeighbours = this.edges.get(v)//4.3.将v的颜色变为灰色colors[v] = 'gray'//4.4.遍历v所有相邻的顶点vNeighbours,并且加入队列中for (let i = 0; i < vNeighbours.length; i++) {const a = vNeighbours[i];//判断相邻顶点是否被探测过,被探测过则不加入队列中;并且加入队列后变为灰色,表示被探测过if (colors[a] == 'white') {colors[a] = 'gray'que.enqueue(a)}}//4.5.处理顶点vhandler(v)//4.6.顶点v所有白色的相邻顶点都加入队列后,将顶点v设置为黑色。此时黑色顶点v位于队列最前面,进入下一次while循环时会被取出colors[v] = 'black'}}//六.实现深度搜索(DFS)Graph.prototype.dfs = function(initV, handler){//1.初始化顶点颜色let colors = this.initializeColor()//2.从某个顶点开始依次递归访问this.dfsVisit(initV, colors, handler)}//为了方便递归调用,封装访问顶点的函数,传入三个参数分别表示:指定的第一个顶点、颜色、处理函数Graph.prototype.dfsVisit = function(v, colors, handler){//1.将颜色设置为灰色colors[v] = 'gray'//2.处理v顶点handler(v)//3.访问v相连的其他顶点let vNeighbours = this.edges.get(v)for (let i = 0; i < vNeighbours.length; i++) {let a = vNeighbours[i];//判断相邻顶点是否为白色,若为白色,递归调用函数继续访问if (colors[a] == 'white') {this.dfsVisit(a, colors, handler)}}//4.将v设置为黑色colors[v] = 'black'}}

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

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

相关文章

正则表达式的规则

文章目录 一、&#x1f94f;正则表达式的简述1、&#x1f9ff;什么是正则表达式2、&#x1f9eb;正则表达式特点 二、&#x1f30a;正则表达式的规则三、&#x1f3af;常用的正则表达式⚛️正则表达式语法 一、&#x1f94f;正则表达式的简述 1、&#x1f9ff;什么是正则表达式…

Java中的反射

反射 Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c; Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. 反射相关的概念 2. 反射的…

Thread-Per-Message设计模式

Thread-Per-Message是为每一个消息的处理开辟一个线程&#xff0c;以并发方式处理&#xff0c;提高系统整体的吞吐量。这种模式再日常开发中非常常见&#xff0c;为了避免线程的频繁创建和销毁&#xff0c;可以使用线程池来代替。 示例代码如下&#xff1a; public class Requ…

HarmonyOS4.0从零开始的开发教程12给您的应用添加弹窗

HarmonyOS&#xff08;十&#xff09;给您的应用添加弹窗 概述 在我们日常使用应用的时候&#xff0c;可能会进行一些敏感的操作&#xff0c;比如删除联系人&#xff0c;这时候我们给应用添加弹窗来提示用户是否需要执行该操作&#xff0c;如下图所示&#xff1a; 弹窗是一种…

工作招聘

https://jobs.bilibili.com/social/positions/16978 Java开发工程师&#xff08;交易业务&#xff09; 上海全职技术类2023-11-15 发布 职位描述 工作职责: 1、B站电商平台架构整体设计&#xff0c;B站电商业务高速发展背景下&#xff0c;电商业务中台建设&#xff0c;如商品…

AI全栈大模型工程师(二十八)如何做好算法备案

互联网信息服务算法 什么情况下要备案&#xff1f; 对于B2B业务&#xff0c;不需要备案。 但在B2C领域&#xff0c;一切要视具体情况而定。 如果我们自主训练大型模型&#xff0c;这是必要的。 但如果是基于第三方模型提供的服务&#xff0c;建议选择那些已获得备案并且具有较大…

前端小记--4.前端终止jquery的ajax请求

在项目中&#xff0c;有时需要主动终止数据请求&#xff0c;比如&#xff0c;某个数据请求响应时间比较慢&#xff0c;用户在等待请求的过程中取消请求&#xff0c;但后台服务器仍在执行计算&#xff0c;此时就需要前端主动终止请求&#xff0c;避免资源浪费。 这里以终止jque…

前端打包工具之Webpack5

前端打包工具之Webpack5 什么是打包工具打包工具的作用常见的打包工具一、Webpack1、什么是[webpack](https://webpack.docschina.org/concepts/)2、webpack本身的功能是有限的3、webpack基本使用&#xff1a;基于本身功能只解析JS资源4、webpack配置4.1、entry&#xff08;入口…

Python脚本打包

一.Windows操作系统 Python脚本打包 1.cmd窗口执行如下指令&#xff1a;pip install pyinstaller C:\Users\ZhuQing>pip install pyinstaller2.执行以下指令验证pyinstaller是否安装成功&#xff08;pyinstaller&#xff09; C:\Users\ZhuQing>pyinstaller3.被打包程序…

frp配置内网穿透步骤

frp配置内网穿透步骤 1.环境准备1.1 云服务器1.2 frp包 2. frp安装2.1 server服务端设置2.2 客户端配置 实现目标通过云服务器ip:8080访问内网电脑启动的web项目localhost:8080 1.环境准备 1.1 云服务器 服务器安装centos7.9, 安全组入口方向开通 7500 7000 8080 8060端口 …

DPDK是什么?DPDK网卡更有优势吗?

近年来&#xff0c;随着数字化的推进&#xff0c;上云成为企业数字化建设的重要指标&#xff0c;用云程度持续深入。可以说&#xff0c;云时代已经来临。 应云而生的DPDK 云时代的一个典型特征&#xff0c;是数据的高速增长。据华为GIV数据&#xff0c;预计2025年全球数据量将…

Spark读写Hive

Spark读写Hive 文章目录 Spark读写Hive&#xff08;一&#xff09;配置本地域名映射&#xff08;二&#xff09;创建Hive表&#xff08;三&#xff09;IDEA中编写Spark代码读取Hive数据&#xff08;四&#xff09;IDEA中编写Spark代码写入数据到Hive &#xff08;一&#xff09…

前端优化 ----防抖 节流

防抖 如果一个事件在短时间内连续触发&#xff0c;则只去执行最后一次。 控制频率 实现方式&#xff1a;每次触发事件时设置一个延迟调用方法&#xff0c;并且取消之前的延时调用方法缺点&#xff1a;如果事件在规定的时间间隔内被不断的触发&#xff0c;则调用方法会被不断的…

【力扣】2.两数相加

2.两数相加 这是第二题&#xff0c;还行豁~。 题解&#xff1a; 首先就是对题目的理解。这里你要知道两链表中数字的排列都是逆序的&#xff0c;也就是说示例一中2-4-3他原本的数字应该是342。同理可得下面链表的意思&#xff0c;二者相加所得到的结果也是逆序的&#xff0c;…

企业微信群裂变:重塑新时代营销格局

随着数字化时代的来临&#xff0c;社交媒体已经逐渐成为人们获取和交流信息的主要平台。微信&#xff0c;作为中国最大的社交平台&#xff0c;正在上演着一场名为“微信群裂变”的营销革命。本文将深入剖析企业微信群裂变的内涵&#xff0c;揭示其背后的驱动力&#xff0c;以及…

【Java】MySQL存储 MD5 值应该用 VARCHAR 还是CHAR?

CHAR 非常适合存储 MD5 值。因为MD5 值是一个定长的值&#xff0c;对于固定长度的非常短的列&#xff0c;CHAR比VARCHAR的效率更高。 CHAR和VARCHAR的区别: 1)存储长度不同&#xff0c;CHAR 的长度是固定的&#xff0c;VARCHAR 的长度是可变的 假设&#xff0c;当我们定义 CHA…

基于Java8构建Docke镜像

基于Java8构建Docke镜像 搜索java8安装包 docker search java8 --no-trunc &#xff0c; --no-trunc展开描述信息 选择拉取 docker pull docker.io/mykro/java8-jre&#xff0c;为了减少磁盘占用&#xff0c;选择jre版本基础镜像 在宿主机创建文件夹iot&#xff0c;并把所需…

Reactor模型

目录 1.Reactor模型是什么2.Reactor 模型应用场景3.使用 Reactor 模型的软件4.Reactor 模型 与 Actor 模型 的关系 本文主要介绍Reactor模型基本概念以及应用场景。 1.Reactor模型是什么 Reactor模型是一种事件驱动的设计模式&#xff0c;用于处理服务请求&#xff0c;它是由…

第15节:Vue3 DOM 更新完成nextTick()

下面是一个示例&#xff0c;演示了如何在UniApp中使用Vue3框架使用nextTick()&#xff1a; <template> <view> <button click"changeText">点击改变文本</button> <text>{{ message }}</text> </view> </templa…

python笔记(1)安装环境

1&#xff0c;官网下载自己电脑位数的安装包 https://www.python.org/downloads/windows/ install时勾选中add to path&#xff0c;把路径自动添加到环境变量 安装pycharm就不讲了 安装后选中自己的python安装包 file-> setting->project:yourprojectname ->pyt…