leetcode刷题(javaScript)——堆相关场景题总结

news2024/11/15 11:17:41

堆是什么?堆都能用树表示,并且一般树的实现都是利用链表。平时使用的最多的是二叉堆,它可以用完全二叉树表示,二叉堆易于存储,并且便于索引。在堆的实现时注意:因为是数组,所以父子节点的关系就不需要特殊的结构去维护了,索引之前通过计算就可以得到,省掉了很多麻烦,如果是链表结构,就会复杂很多。

在JavaScript刷题中,堆(Heap)通常用于解决一些需要高效处理优先级的问题,例如找出最大或最小的K个元素、实现优先队列等。堆在刷题中的应用场景包括但不限于以下几个方面:

  1. 找出最大或最小的K个元素:通过维护一个大小为K的最大堆或最小堆,可以快速找出数组中最大或最小的K个元素。

  2. 合并K个有序数组:可以使用堆来合并K个有序数组,实现高效的合并操作。

  3. 实现优先队列:堆可以用于实现优先队列,保证队列中优先级高的元素先出队。

 数组存储二叉堆

        二叉堆是一种完全二叉树,分为最小堆和最大堆两种类型。用数组存储二叉堆, 完全二叉树要求叶子节点从左往右填满,才能开始填充下一层。

  1. 二叉堆:二叉堆是一种完全二叉树,通常使用数组来表示。在最小堆中,父节点的值小于或等于其子节点的值;在最大堆中,父节点的值大于或等于其子节点的值。

  2. 最小堆:在最小堆中,父节点的值小于或等于其子节点的值。根节点是堆中的最小值。

  3. 最大堆:在最大堆中,父节点的值大于或等于其子节点的值。根节点是堆中的最大值。

注:完全二叉树和满二叉树不同,完全二叉树允许叶子节点不铺满。

动画演示创建堆的过程

 

同一组数据最小堆和最大堆是唯一的吗?

同一组数据的最小堆或最大堆不是唯一的。以最小堆为例:最小堆是一种特殊的二叉堆,它满足以下两个性质:

  1. 父节点的值小于或等于其子节点的值。
  2. 堆中任意节点的子树也是一个最小堆。

        由于最小堆只要满足上述性质即可,因此对于同一组数据,可以有多种不同的最小堆表示方式。这是因为在构建最小堆的过程中,可以选择不同的节点作为根节点,从而得到不同的堆结构。

所以,同一组数据的最小堆并不是唯一的,可以有多种不同的表示方式。

如何找到节点i父节点和子节点呢?

        二叉堆在数组是按层次遍历进行存储的,从上至下,从左至右。因此子节点的index要大于父节点的index。在二叉堆中,可以通过以下方式计算父节点和子节点的索引:

  • 父节点索引计算:对于节点i,其父节点的索引为(i-1)/2。
  • 左子节点索引计算:对于节点i,其左子节点的索引为2*i+1。
  • 右子节点索引计算:对于节点i,其右子节点的索引为2*i+2。

如何删除节点?

在删除一个元素之后,整体往前移动是比较费时的,这也是随机存储结构的短板。因此,堆在删除元素的时候是把最后一个叶子节点补充到树根节点。在通过节点移动将堆调整为最小堆或最大堆。

如何通过js构建最大堆

首先构造器得有吧,创建一个空的heap数组;

其次将获取父节点index、左右子节点index、堆大小size、栈顶元素获取、两节点交换这些简单的辅助函数写一下;

主函数insert插入节点:每次从heap的尾部也就是最后一个叶子节点插入,插进去的叶子节点得向上找它的位置吧,写一个up方法,找到插入节点位置;

up方法实现:找到节点的父元素,看父元素是否比自己小,如果小的换就交换,并且递归up方法,直到所有元素都调整好。

删除节点pop方法:这里依然将最后节点移到第一个跟节点,此时根节点一定是最小的,不符合最大堆规则,所以调整跟节点向下移动,写一个down方法移动根节点。这里注意堆的size如果等于1直接删除而不用移动节点。

down方法实现:找到根的左右子节点,选择比根大的节点进行交换,并递归down方法,直到所有节点都调整好。

交换节点swap可以使用数组解构进行交换,而不用单独定义中间值。

class MaxHeap {
    constructor() {
        this.heap = [];
    }
    //获取父元素下标
    getParentIndex(index) {
        return Math.floor((index - 1) / 2);
    }
    //获取左子树下标
    getLeftIndex(index) {
        return 2 * index + 1;
    }
    //获取右子树下标
    getRightIndex(index) {
        return 2 * index + 2;
    }
    //获取堆大小
    size() {
        return this.heap.length;
    }
    //获取堆顶元素
    peek() {
        return this.heap[0];
    }
    //交换节点
    swap(id1, id2) {
        [this.heap[id2], this.heap[id1]] = [this.heap[id1], this.heap[id2]];
    }
    //插入节点
    insert(value) {
        this.heap.push(value);
        this.up(this.size() - 1);
    }
    //删除节点
    pop() {
        let last = this.heap.pop();
        if (this.size() === 0) return;
        this.heap[0] = last;
        this.down(0);
    }
    //向上移动节点
    up(index) {
        const parentIndex = this.getParentIndex(index);
        if (this.heap[index] > this.heap[parentIndex]) {
            this.swap(parentIndex, index);
            this.up(parentIndex);
        }
    }
    //向下移动节点
    down(index) {
        const leftIndex = this.getLeftIndex(index);
        const rightIndex = this.getRightIndex(index);
        let smallIndex = index;

        if (
            leftIndex < this.size() &&
            this.heap[leftIndex] > this.heap[smallIndex]
        ) {
            smallIndex = leftIndex;
        }
        if (
            rightIndex < this.size() &&
            this.heap[rightIndex] > this.heap[smallIndex]
        ) {
          smallIndex = rightIndex;
        }
       if (smallIndex != index) {
           this.swap(smallIndex, index);
           this.down(smallIndex);
       }
    }
}

 这里让根节点从左右节点中与最小的节点进行交换。注意减少交换次数,避免多次递归

如何通过js构建最小堆

与创建最小堆类似。创建堆,往堆里新增元素,删除堆顶,获取堆的父节点下标,获取堆左右子节点下标

 代码示例

class MinHeap {
  constructor() {
    this.heap = [];
  }
  getParentIndex(index) {
    return Math.floor((index - 1) / 2);
  }
  getLeftIndex(index) {
    return index * 2 + 1;
  }
  getRightIndex(index) {
    return index * 2 + 2;
  }
  swap(i1, i2) {
    [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
  }
  //往堆最后添加节点,触发元素上移
  //当前元素与其跟节点进行比较如果大于其跟节点与根节点进行交换,重复操作
  up(index) {
    //如果是0就不移动
    if (index == 0) return;
    //获取父元素
    const parentIndex = this.getParentIndex(index);
    if (this.heap[parentIndex] > this.heap[index]) {
      this.swap(parentIndex, index);
      //对交换后parentIndex继续向上递归
      this.up(parentIndex);
    }
  }
  //从堆顶删除元素时,将子节点移到堆顶,触发元素下移
  down(index) {
    const leftIndex = this.getLeftIndex(index);
    const rightIndex = this.getRightIndex(index);
    let smallIndex = index;
    //左子树小于根节点,交换左子树与根
    if (
      leftIndex < this.size() &&
      this.heap[leftIndex] < this.heap[smallIndex]
    ) {
      smallIndex = leftIndex;
    }
    //同理,右子树小于根节点,交换右子树与根
    if (
      rightIndex < this.size() &&
      this.heap[rightIndex] < this.heap[smallIndex]
    ) {
      smallIndex = rightIndex;
    }
    if (smallIndex != index) {
      this.swap(smallIndex, index);
      this.down(smallIndex);
    }
  }

  //往堆里增加元素
  insert(value) {
    this.heap.push(value);
    this.up(this.heap.length - 1);
  }
  //将堆顶元素弹出
  //删除节点
  pop() {
    let last = this.heap.pop();
    if (this.size() === 0) return;
    this.heap[0] = last;
    this.down(0);
  }

  //获取堆顶
  peek() {
    return this.heap[0];
  }
  //获取堆大小
  size() {
    return this.heap.length;
  }
}

 215. 数组中的第K个最大元素

解决数组中的第K个最大元素问题时,通常使用最小堆来实现。通过维护一个大小为K的最小堆,可以在O(NlogK)的时间复杂度内找到数组中的第K个最大元素。

但是题目最近新增了一个要求:就是必须让算法的时间复杂度控制在O(n),当K很大时使用堆有可能会超出时间复杂度,所以要减少不必要的交换移动。

直到今天(2024.3.14)使用堆排序仍然可以跑通全部案例。

注:官方给的快速选择,甚至是"三数取中"的方法来选择基准值也都无法达到时间复杂度,会在第40/41个案例卡住。

使用最小堆获取数组中第K个最大元素的思路:

  1. 初始化一个大小为K的最小堆:将数组中的前K个元素放入最小堆中。

  2. 遍历数组剩余元素:从第K+1个元素开始遍历数组,对于每个元素,如果大于最小堆的堆顶元素(堆中最小的元素),则将该元素加入最小堆,并移除堆顶元素。

  3. 返回堆顶元素:遍历完成后,最小堆的堆顶元素即为数组中的第K个最大元素。

使用最小堆的优势在于可以保持堆的大小为K,只需维护K个元素,避免了对整个数组进行排序或维护大堆的复杂度。

class MinHeap {
    constructor() {
        this.heap = [];
    }
    getParentIndex(index) {
        return Math.floor((index - 1) / 2);
    }
    getLeftIndex(index) {
        return index * 2 + 1;
    }
    getRightIndex(index) {
        return index * 2 + 2;
    }
    swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
    }
    //往堆最后添加节点,触发元素上移
    //当前元素与其跟节点进行比较如果大于其跟节点与根节点进行交换,重复操作
    up(index) {
        //如果是0就不移动
        if (index == 0) return;
        //获取父元素
        const parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] > this.heap[index]) {
            this.swap(parentIndex, index);
            //对交换后parentIndex继续向上递归
            this.up(parentIndex);
        }
    }
    //从堆顶删除元素时,将子节点移到堆顶,触发元素下移
    down(index) {
        const leftIndex = this.getLeftIndex(index);
        const rightIndex = this.getRightIndex(index);
        let smallIndex = index;
        //左子树小于根节点,交换左子树与根
        if (leftIndex < this.size() && this.heap[leftIndex] < this.heap[smallIndex]) {
            smallIndex = leftIndex;
        }
        //同理,右子树小于根节点,交换右子树与根
        if (rightIndex < this.size() && this.heap[rightIndex] < this.heap[smallIndex]) {
            smallIndex = rightIndex;
        }
        if (smallIndex != index) {
            this.swap(smallIndex, index);
            this.down(smallIndex);
        }
    }

    //往堆里增加元素
    insert(value) {
        this.heap.push(value);
        this.up(this.heap.length - 1);
    }
    //将堆顶元素弹出
    //删除节点
    pop() {
        let last = this.heap.pop();
        if (this.size() === 0) return;
        this.heap[0] = last;
        this.down(0);
    }

    //获取堆顶
    peek() {
        return this.heap[0];
    }
    //获取堆大小
    size() {
        return this.heap.length;
    }
}
// 寻找第 k 个最大的元素
var findKthLargest = function (nums, k) {
    let minHeap = new MinHeap();
    nums.forEach(item => {
        minHeap.insert(item);
        if (minHeap.size() > k) {
            //每次pop的是栈顶,及k+1个元素中最小的
            minHeap.pop();
        }
    })
    return minHeap.peek();
};

 算法可以通过全部的41个测试用例

 703. 数据流中的第 K 大元素

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

请实现 KthLargest 类:

  • KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
  • int add(int val)val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

 思路:也是用最小堆来实现,最小堆前面已经介绍过堆的插入删除,直接将创建堆对象的类拿来用即可。

动态构建一个长度为k的最小堆,对顶即第k大的的元素。因为比k小的元素都被pop出去了

/**
 * @param {number} k
 * @param {number[]} nums
 */
var KthLargest = function (k, nums) {
    //构建一个k个长度的最小堆
    this.k = k;
    this.minHeap = new MinHeap();

    // 初始化最小堆
    for (let num of nums) {
        this.add(num);
    }
};

/** 
 * @param {number} val
 * @return {number}
 */
KthLargest.prototype.add = function (val) {
    this.minHeap.insert(val);
    while (this.minHeap.size() > this.k) {
        this.minHeap.pop();
    }
    return this.minHeap.peek();
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * var obj = new KthLargest(k, nums)
 * var param_1 = obj.add(val)
 */
class MinHeap {
  constructor() {
    this.heap = [];
  }
  getParentIndex(index) {
    return Math.floor((index - 1) / 2);
  }
  getLeftIndex(index) {
    return index * 2 + 1;
  }
  getRightIndex(index) {
    return index * 2 + 2;
  }
  swap(i1, i2) {
    [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
  }
  //往堆最后添加节点,触发元素上移
  //当前元素与其跟节点进行比较如果大于其跟节点与根节点进行交换,重复操作
  up(index) {
    //如果是0就不移动
    if (index == 0) return;
    //获取父元素
    const parentIndex = this.getParentIndex(index);
    if (this.heap[parentIndex] > this.heap[index]) {
      this.swap(parentIndex, index);
      //对交换后parentIndex继续向上递归
      this.up(parentIndex);
    }
  }
  //从堆顶删除元素时,将子节点移到堆顶,触发元素下移
  down(index) {
    const leftIndex = this.getLeftIndex(index);
    const rightIndex = this.getRightIndex(index);
    let smallIndex = index;
    //左子树小于根节点,交换左子树与根
    if (
      leftIndex < this.size() &&
      this.heap[leftIndex] < this.heap[smallIndex]
    ) {
      smallIndex = leftIndex;
    }
    //同理,右子树小于根节点,交换右子树与根
    if (
      rightIndex < this.size() &&
      this.heap[rightIndex] < this.heap[smallIndex]
    ) {
      smallIndex = rightIndex;
    }
    if (smallIndex != index) {
      this.swap(smallIndex, index);
      this.down(smallIndex);
    }
  }

  //往堆里增加元素
  insert(value) {
    this.heap.push(value);
    this.up(this.heap.length - 1);
  }
  //将堆顶元素弹出
  //删除节点
  pop() {
    let last = this.heap.pop();
    if (this.size() === 0) return;
    this.heap[0] = last;
    this.down(0);
  }

  //获取堆顶
  peek() {
    return this.heap[0];
  }
  //获取堆大小
  size() {
    return this.heap.length;
  }
}

 1046. 最后一块石头的重量

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0

 思想:构建一个最大堆,从堆顶取两个值分别为y,x判断如果y>x则将y-x放入堆中并将堆调整为最大堆。递归直到堆长度小于2。如果堆里还有值则返回堆顶元素,否则为0

/**
 * @param {number[]} stones
 * @return {number}
 */
var lastStoneWeight = function (stones) {
    if (stones.length == 1) return stones[0];
    let maxHeap = new MaxHeap();
    stones.forEach((item) => maxHeap.insert(item));
    while (maxHeap.size() > 1) {
        let y = maxHeap.peek();
        maxHeap.pop();
        let x = maxHeap.peek();
        maxHeap.pop();
        if (y > x) {
            maxHeap.insert(y - x);
        }
    }
    return maxHeap.size() ? maxHeap.peek() : 0;
};
/**
 * @param {number[]} stones
 * @return {number}
 */
var lastStoneWeight = function (stones) {
    if (stones.length == 1) return stones[0];
    let maxHeap = new MaxHeap();
    stones.forEach((item) => maxHeap.insert(item));
    while (maxHeap.size() > 1) {
        let y = maxHeap.peek();
        maxHeap.pop();
        let x = maxHeap.peek();
        maxHeap.pop();
        if (y > x) {
            maxHeap.insert(y - x);
        }
    }
    return maxHeap.size() ? maxHeap.peek() : 0;
};
class MaxHeap {
    constructor() {
        this.heap = [];
    }
    //获取父元素下标
    getParentIndex(index) {
        return Math.floor((index - 1) / 2);
    }
    //获取左子树下标
    getLeftIndex(index) {
        return 2 * index + 1;
    }
    //获取右子树下标
    getRightIndex(index) {
        return 2 * index + 2;
    }
    //获取堆大小
    size() {
        return this.heap.length;
    }
    //获取堆顶元素
    peek() {
        return this.heap[0];
    }
    //交换节点
    swap(id1, id2) {
        [this.heap[id2], this.heap[id1]] = [this.heap[id1], this.heap[id2]];
    }
    //插入节点
    insert(value) {
        this.heap.push(value);
        this.up(this.size() - 1);
    }
    //删除节点
    pop() {
        let last = this.heap.pop();
        if (this.size() === 0) return;
        this.heap[0] = last;
        this.down(0);
    }
    //向上移动节点
    up(index) {
        const parentIndex = this.getParentIndex(index);
        if (this.heap[index] > this.heap[parentIndex]) {
            this.swap(parentIndex, index);
            this.up(parentIndex);
        }
    }
    //向下移动节点
    down(index) {
        const leftIndex = this.getLeftIndex(index);
        const rightIndex = this.getRightIndex(index);
        let smallIndex = index;

        if (
            leftIndex < this.size() &&
            this.heap[leftIndex] > this.heap[smallIndex]
        ) {
            smallIndex = leftIndex;
        }
        if (
            rightIndex < this.size() &&
            this.heap[rightIndex] > this.heap[smallIndex]
        ) {
          smallIndex = rightIndex;
        }
       if (smallIndex != index) {
           this.swap(smallIndex, index);
           this.down(smallIndex);
       }
    }
}

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

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

相关文章

学习SSM的记录(六)-- Spring MVC

目录 Spring MVC 简介和体验 Spring MVC原理简单解析 Spring MVC涉及的组件 Spring MVC 快速体验 Spring MVC 接收数据 访问路径设置 接收参数&#xff08;重点&#xff09; param和json参数比较 param参数接收 路径参数接收 json参数接收 EnableWebMvc注解 接收C…

探秘知乎的排名算法:知乎撰写高质量内容的秘诀

知乎作为一个知识问答社区&#xff0c;用户众多、内容繁杂&#xff0c;那么究竟是什么样的原则决定了知乎上的排名呢&#xff1f;腾轩科技传媒探讨知乎排名的规则&#xff0c;并分享如何撰写高质量的文章。 知乎排名的算法 在知乎这个巨大的社交平台上&#xff0c;任何一个用户…

DDL - 建立数据库,建表代码版(Way 2)

一、DB操作 show databases; create database DBOFRYX; drop database DBOFRYX; use DBOFRYX; 二、表操作&#xff08;表和表结构、字段是A、B两姐妹&#xff09; (1) use DBOFRYX; show tables; (2) create table TABOFRYX( name varchar(50) comment "姓名"…

ChatGPT提问技巧——对话提示

ChatGPT提问技巧——对话提示 对话提示是一种允许模型生成模拟两个或多个实体之间对话的文本的技术。 通过向模型提供上下文和一组角色或实体&#xff0c;以及他们的角色和背景&#xff0c;并要求模型生成他们之间的对话。 因此&#xff0c;应向模型提供一个上下文和一组角色…

大语言模型智能体简介

大语言模型&#xff08;LLM&#xff09;智能体&#xff0c;是一种利用大语言模型进行复杂任务执行的应用。这种智能体通过结合大语言模型与关键模块&#xff0c;如规划和记忆&#xff0c;来执行任务。构建这类智能体时&#xff0c;LLM充当着控制中心或“大脑”的角色&#xff0…

浏览器设置字体1px时,不能正常显示

问题如下&#xff0c;某版本浏览器&#xff0c;字体很小时&#xff0c;无法正常显示&#xff0c;解决方案有三种。 解决方案1 解决方案2 解决方案3

WebServer -- 面试题(下)

&#x1f442; 夏风 - Gifty - 单曲 - 网易云音乐 目录 &#x1f33c;前言 &#x1f382;面试题(下) 4&#xff09;HTTP报文解析 为什么要用状态机 状态转移图画一下 https 协议为什么安全 https 的 ssl 连接过程 GET 和 POST 的区别 5&#xff09;数据库注册登录 登…

十五、软考-系统架构设计师笔记-面向服务架构设计理论与实践

1、SOA相关概念 面向服务的架构(SOA)的定义 SOA 是一个组件模型&#xff0c;它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的&#xff0c;它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构…

[RoarCTF 2019]Easy Java -不会编程的崽

考察一下大家对java-web知识点的掌握 熟悉的登录界面让你想起了某位故人没有&#xff0c;哈哈&#xff0c;但是并非sql注入。一番基础尝试无果后&#xff0c;看看help吧 这个url让你想起某位故人了吗&#xff1f;对文件下载。但是似乎没有响应。改成post请求即可。 我看见pk了&…

unity3d Animal Controller的Animal组件中General基础部分理解

控制器介绍 动物脚本负责控制动物的所有运动逻辑.它管理所有的动画师和刚体参数,以及所有的状态和模式,动物可以做。 动物控制器 是一个动画框架控制器,根动或到位,为任何生物或人形。它利用刚体与物理世界的互动和动画师的玩动画。 States States 是不互相重叠的动画。例如…

canvas绘制圆形进度条

canvas绘制圆形进度条 用canvas绘制了一个圆形进度条&#xff0c;记录一下&#xff1a; 效果如下&#xff1a; 感觉效果还行&#xff0c;不过有待优化 代码如下&#xff1a; 我是用Vue写的 组件的代码&#xff1a; progressCanvas.vue <template><div><can…

Linux VM虚拟环境 设置静态IP

目录 查看自己的网卡配置说明重启网卡实例测试配置情况测试网络 查看自己的网卡 ip a配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens32配置说明 [rootlinux-server ~]# cd /etc/sysconfig/network-scripts/ #网卡配置文件存放路径 [rootlinux-server network-scri…

C/C++中{}的用法总结(全)

C基础专栏&#xff1a;http://t.csdnimg.cn/UjhPR 目录 1.定义初始化列表&#xff08;Initializer List&#xff09; 2.类成员初始化列表 3.无默认构造函数的类的默认初始化&#xff08;C11 及以后版本&#xff09; 4.初始化器列表构造函数&#xff08;C11 及以后版本&…

js判断对象是否有某个属性

前端判断后端接口是否返回某个字段的时候 <script>var obj { name: "John", age: 30 };console.log(obj.hasOwnProperty("name")); // 输出 trueconsole.log(obj.hasOwnProperty("email")); // 输出 falselet obj11 { name: "Joh…

一文搞懂多模态:BeiT-3之前的14个多模态+4个周边原理解读

在人工智能的世界里&#xff0c;多模态学习不断地展现出其重要性。这个领域的迅速发展不仅促进了不同类型数据之间的深度融合&#xff0c;还为机器理解世界提供了更加丰富和细腻的视角。随着科技的不断演进&#xff0c;人工智能模型已经开始渐渐具备处理和理解从文本、图像&…

MySQL主从复制的原理

MySQL 主从复制是一种数据复制技术&#xff0c;用于在多个数据库服务器之间的数据同步。在主从复制架构中&#xff0c;一个服务器被设置为主服务器&#xff0c;充当数据源&#xff0c;其他服务器被设置为从服务器&#xff0c;用来复制主服务器的数据。 1.主从复制优点 主从复…

Unity Timeline学习笔记(3) - SignalTrack信号轨道和自定义带参数的Marker信号和轨道

信号轨道&#xff0c;顾名思义就是运行到某处发送一个信号。 普通用法 普通用法就是没有任何封装的&#xff0c;个人感觉特别难用&#xff0c;但是有必要理解一下工作原理。 添加信号 我们添加一个信号资源 生成后可以看到资源文件&#xff0c;这个是可以拖到SignalTrack上…

web 服务搭建

实验拓扑图&#xff1a; 实验环境搭建 Centos7-5作为Client&#xff08;12.0.0.12/24&#xff09;&#xff1b;Centos7-1作为网关服务器&#xff08;配置两块网卡ens33 192.168.246.7/24&#xff0c;ens36 12.0.0.1/24&#xff09;&#xff1b;Centos7-2作为Web1&#xff08;19…

2024年云仓酒庄深惠公司发布会圆满落幕:共襄盛举,携手共赢

原标题&#xff1a;2024年云仓酒庄深惠公司发布会圆满落幕&#xff1a;共襄盛举&#xff0c;携手共赢 随着岁月的流转&#xff0c;云仓酒庄以其卓越的品质和不懈的创新精神&#xff0c;在葡萄酒行业中独树一帜。2024年&#xff0c;云仓酒庄再次迎来了一场盛大的发布会——深惠…

一文读懂『归并排序』算法(Merge Sort)

目录 归并排序算法&#xff08;Merge Sort&#xff09;简介 算法基本思想和流程&#xff08;时间复杂度O(nlogn) &#xff09; ​编辑代码实现 归并排序模板&#xff08;背诵&#xff09; 归并排序算法&#xff08;Merge Sort&#xff09;简介 归并排序&#xff08;Merge …