经典算法题总结:十大排序算法,外部排序和Google排序简介

news2024/11/15 11:51:58

十大排序算法

就地性:顾名思义,原地排序通过在原数组上直接操作实现排序,无须借助额外的辅助数组,从而节省内存。通常情况下,原地排序的数据搬运操作较少,运行速度也更快。

稳定性:稳定排序在完成排序后,相等元素在数组中的相对顺序不发生改变。

自适应性:自适应排序的时间复杂度会受输入数据的影响,即最佳时间复杂度、最差时间复杂度、平均时间复杂度并不完全相等。

基于比较的排序算法

常用排序
快速排序

快速排序(Quick Sort)是一种常用的高效的排序算法,它是一种基于比较的排序算法,利用了分治(自顶向下)的思想。快速排序的主要思想是选择一个基准元素,将数组分成两个子数组,一个子数组的所有元素都小于基准元素,另一个子数组的所有元素都大于基准元素,然后递归地对这两个子数组进行排序,最后将它们合并起来。

快速排序的基本步骤如下:

  1. 选择基准元素:从数组中选择一个元素作为基准(pivot)元素。选择合适的基准元素可以影响算法的性能。
  2. 划分:将数组中的其他元素与基准元素进行比较,将小于基准元素的元素放在左边,大于基准元素的元素放在右边。最终基准元素将位于其正确的位置,左边是小于它的元素,右边是大于它的元素。
  3. 递归:递归地对左右两个子数组进行快速排序。

快速排序的平均时间复杂度为O(N * logN),其中N是待排序数组的长度。然而,在最坏情况下,如果选择的基准元素始终是数组中的最小或最大元素,快速排序的时间复杂度可能达到O(N^2),但这种情况相对较少出现。快速排序的实际性能通常比其他基于比较的排序算法好,尤其在大规模数据集上。

优化方案:选择基准元素(随机化(常用),三数取中法),双指针(三向划分)快速排序,小规模子数据使用插入排序。

在快速排序中,如果每次选择基准元素都是数组中的最小或最大元素,那么每次分割都会将数组分成一个元素和 N - 1 个元素的子数组,导致算法的时间复杂度退化为 O(N^2)。随机选择基准元素可以降低这种最坏情况发生的概率,从而避免退化。

import java.util.Random;

/**
 * 随机化基准元素优化快速排序
 */
public class QuickSort {
    private Random random = new Random();

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2) {
            return nums;
        }
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void quickSort(int[] nums, int low, int high) {
        // 边界情况
        if (low >= high) {
            return;
        }
        // 自上而下递归根据基准元素进行划分子数组
        int pivot = partition(nums, low, high);
        quickSort(nums, low, pivot - 1);
        quickSort(nums, pivot + 1, high);
    }

    private int partition(int[] nums, int low, int high) {
        // 随机化基准元素,避免基本有序情况下的不平衡划分,将基准元素移到当前子数组的起始位置
        int randomIndex = low + random.nextInt( high - low + 1);
        int pivot = nums[randomIndex];
        nums[randomIndex] = nums[low];
        nums[low] = pivot;

        // 双指针遍历后,数组基准元素位置左边小于基准元素,右边大于基准元素
        while (low < high) {
            while (low < high && nums[high] > pivot) {
                high--;
            }
            nums[low] = nums[high];
            while (low < high && nums[low] < pivot) {
                low++;
            }
            nums[high] = nums[low];
        }
        nums[low] = pivot;
        return low;
    }

}

通常快速排序的效率更高,主要有以下原因:

  • 出现最差情况的概率很低:虽然快速排序的最差时间复杂度为 O(N^2),没有归并排序稳定,但在绝大多数情况下,快速排序能在 O(N * logN) 的时间复杂度下运行。
  • 缓存使用效率高:在执行哨兵划分操作时,系统可将整个子数组加载到缓存,因此访问元素的效率较高。而像“堆排序”这类算法需要跳跃式访问元素,从而缺乏这一特性。
归并排序

归并排序(Merge Sort)是一种基于分治(自底向上)的排序算法,它的主要思想是将一个未排序的数组划分为两个子数组,分别对这两个子数组进行排序,然后再将排好序的子数组合并成一个有序数组。归并排序的关键步骤在于"合并"操作,这是通过将两个已排序的子数组按照顺序合并而实现的。

归并排序的过程可以分为以下几个步骤:

  1. 分解:将待排序的数组分成两个子数组,通常是将数组分成相等大小的两部分,直到每个子数组的大小为1或0为止,因为一个元素的数组是有序的。
  2. 排序:对每个子数组进行递归地排序。这个步骤通过将问题逐步分解为更小的子问题,直到达到基本情况(即单个元素的数组),然后逐步合并子问题的解来实现排序。
  3. 合并:将已排序的子数组合并为一个有序数组。这是归并排序的核心步骤,它通过比较两个子数组中的元素,并按照顺序将它们合并到一个新的数组中,直到所有元素都被合并。

归并排序具有稳定性,即相等元素的顺序在排序后仍然保持不变。其时间复杂度为O(N * logN),其中N是数组的大小,这使得归并排序在大规模数据集上表现出色。然而,它需要额外的空间来存储临时数组,所以空间复杂度为O(N)。

/**
 * 归并排序
 */
public class MergeSort {

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2) {
            return nums;
        }
        mergeSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void mergeSort(int[] nums, int left, int right) {
        // 边界情况
        if (left >= right) {
            return;
        }
        int mid = left + (right - left) / 2;
        // 自上而下分治
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        // 自下而上合并
        mergeArray(nums, left, mid, right);
    }

    private void mergeArray(int[] nums, int left, int mid, int right) {
        int[] helperArray = new int[right - left + 1];
        int leftPointer = left;
        int rightPointer = mid + 1;
        int i = 0; // 辅助数组索引
        while (leftPointer <= mid && rightPointer <= right) {
            helperArray[i++] = nums[leftPointer] < nums[rightPointer] ?
                    nums[leftPointer++] : nums[rightPointer++];
        }
        while (leftPointer <= mid) {
            helperArray[i++] = nums[leftPointer++];
        }
        while (rightPointer <= right) {
            helperArray[i++] = nums[rightPointer++];
        }
        for (i = 0; i < helperArray.length; i++) {
            nums[left + i] = helperArray[i];
        }
    }

}
堆排序

堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法,它是一种选择排序的一种改进,具有较好的时间和空间复杂度。堆是一种特殊的完全二叉树,分为最大堆和最小堆两种类型,其中最大堆要求父节点的值大于或等于子节点的值,最小堆要求父节点的值小于或等于子节点的值。

堆排序的基本思想:首先将待排序的数组构建成一个二叉堆(通常使用最大堆),出元素的时候将堆顶元素(即最大元素)与堆的最后一个元素交换,并从堆中移除,然后对剩余的元素重新调整为一个合法的堆,重复这个过程直到堆为空,得到一个有序的数组。

以下是堆排序的关键步骤:

  1. 构建最大堆:从最后一个非叶子节点开始,自底向上地将数组调整为一个最大堆(构建最大堆的时间复杂度是 O(N))。
  2. 交换堆顶元素和末尾元素:将堆顶元素与堆的最后一个元素交换,然后将堆的大小减一。
  3. 调整堆:从堆顶开始,通过逐级下沉(或上浮)操作将堆重新调整为最大堆。(调整堆的时间复杂度是O(logN))
  4. 重复步骤 2 和 3,直到堆为空。

Poor use of cache memory:平均时间上,堆排序的时间常数比快排要大一些,因此通常会慢一些,但是堆排序最差时间也是O(N * logN),这点比快排好。快排在递归进行部分的排序的时候,只会访问局部的数据,因此缓存能够更大概率的命中;而堆排序的建堆过程是整个数组各个位置都访问到的,后面则是所有未排序数据各个位置都可能访问到的,所以不利于缓存发挥作用。简单的说就是快排的存取模型的局部性更强,堆排序差一些

/**
 * 堆排序
 * 非叶子节点:在完全二叉树中,所有非叶子节点的索引都在 0 到 n / 2 - 1 之间,其中 n 是数组的长度。
 * 叶子节点:叶子节点是树的最底层节点,没有子节点。在完全二叉树中,从 n / 2 到 n - 1 的节点都是叶子节点。
 */
public class HeapSort {

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2) {
            return nums;
        }
        int n = nums.length;
        // 从最后一个非叶子节点自下而上进行调整
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(nums, n, i);
        }

        for (int i = n - 1; i >= 0; i--) {
            swap(nums, 0, i);
            heapify(nums, i, 0);
        }

        return nums;
    }

    /**
     * 堆化方法,维护堆的性质
     * @param nums 数组表示的堆
     * @param n 堆的大小
     * @param i 当前需要堆化的节点的索引
     */
    private void heapify(int[] nums, int n, int i) {
        int largest = i;
        int left = 2 * i + 1; // 左子节点的索引
        int right = 2 * i + 2; // 右子节点的索引
        if (left < n && nums[left] > nums[largest]) {
            largest = left;
        }
        if (right < n && nums[right] > nums[largest]) {
            largest = right;
        }
        if (largest != i) {
            swap(nums, largest, i);
            heapify(nums, n, largest);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

}

排序算法:堆排序【图解+代码】

低级排序
冒泡排序
public class BubbleSort {
    
    // 冒泡排序
    public void bubbleSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = arr.length - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j, j + 1);
                }
            }
        }
    }

}

我们发现,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返回结果。因此,可以增加一个标志位 flag 来监测这种情况,一旦出现就立即返回。

public class BubbleSort {

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2) {
            return nums;
        }
        boolean flag; // 标志该轮循环数组是否已经有序

        for (int i = nums.length - 1; i > 0; i--) {
            flag = true;
            for (int j = 0; j < i; j++) {
                if (nums[j] > nums[j + 1]) {
                    swap(nums, i, j);
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
        }

        return nums;
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

}
选择排序
public class SelectionSort {
    
    public void selectionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            // 在未排序部分找到最小值的索引
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[j] < arr[minIndex] ? j : minIndex;
            }
            // 将最小值交换到已排序部分的末尾
            swap(arr, i, minIndex);
        }
    }

}
直接插入排序
public class InsertionSort {
    
    // 插入排序,比较有效地排序方法
    public void insertionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 1; i < arr.length; i++) {
            // [0, i) 范围内有序,这里的 i 是要比较的元素位置
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
                swap(arr, j, j + 1);
            }
        }
    }

}

尽管插入排序的时间复杂度更高,但在数据量较小的情况下,插入排序通常更快(递归的栈空间的消耗)

插入排序在时间复杂度的常数因素上比冒泡排序和选择排序更小:

  1. 较少的交换操作:插入排序在排序过程中,通常需要较少的交换操作。每次插入一个元素时,只需将其与前面已排序部分进行比较,然后移动这些元素,而不是每次都交换元素的位置。相对于冒泡排序来说,冒泡排序每次比较后都可能进行交换操作,交换操作的次数较多,效率较低。
  2. 较少的比较操作:插入排序在每次插入时,会逐步将元素插入到正确位置,而不像冒泡,选择排序那样需要反复遍历数组来找到未排序部分的最小元素。
希尔排序

希尔排序(Shell Sort)是一种插入排序的一种改进,它通过将数组分成若干个子序列来进行排序,逐步减小子序列的长度,最终完成排序。希尔排序的核心思想是将间隔较大的元素先排好序,然后逐步减小间隔,直到间隔为1,最后进行一次插入排序。

希尔排序的步骤:

  1. 选择一个增量序列(也称为间隔序列),常用的增量序列有希尔增量、Hibbard增量和Sedgewick增量等。
  2. 根据增量序列将数组分成若干个子序列,对每个子序列进行插入排序。
  3. 逐步减小增量,重复步骤 2,直到增量为1,此时进行一次完整的插入排序,完成排序。

希尔增量序列:最希尔增量是最简单的增量序列,其取值为 N / 2,每次递减一半,直到增量为1。其中,N 是数组的长度。最坏时间复杂度 O(N^2),平均时间复杂度 O(N^1.3)~O(N^1.5)。

Hibbard增量序列:Hibbard增量序列采用 2^k - 1(k 为非负整数)作为增量。Hibbard增量的性能较好,但在实际应用中可能并不总是最优。最坏时间复杂度O(N^3/2),平均时间复杂度O(N^5/4)。

Sedgewick增量序列:Sedgewick增量序列通过 4^k + 3 * 2^(k-1) 和 2^(k+2) * (2^(k+2) - 3) + 1 两种增量交替使用,最坏时间复杂度O(N^(4/3)),平均时间复杂度 O(N^(7/6))。

基于比较的排序算法时间复杂度下限证明

经过k次比较最多能区分的序列个数是2^k,如果有M种序列,需要logM次比较才能区分出大小。那有N个元素的数组有 N! 种排序可能,需要logN!次比较才能区分出大小,使用斯特林公式可知最低复杂度是N * logN。

排序算法会出现不稳定的状态原因

比较操作不考虑相等情况:许多排序算法是基于比较操作的,它们只考虑元素的大小关系,而不考虑相等情况。当两个元素的大小相等时,排序算法可能会交换它们的位置,从而破坏了它们在原始序列中的相对顺序,导致排序结果不稳定。

基于非比较的排序算法

计数排序

计数排序适用于待排序元素的范围比较小且非负整数的情况。它的基本思想是,统计每个元素出现的次数,然后根据统计信息构建有序的结果序列。

桶排序

桶排序适用于待排序元素服从均匀分布的情况,它将待排序元素划分为一定数量的桶,然后对每个桶内的元素进行排序,最后将排序后的桶依次合并成有序的结果。

桶排序的时间复杂度理论上可以达到 O(N),关键在于将元素均匀分配到各个桶中,因为实际数据往往不是均匀分布的。为实现平均分配,我们可以先设定一条大致的分界线,将数据粗略地分到 3 个桶中。分配完毕后,再将商品较多的桶继续划分为 3 个桶,直至所有桶中的元素数量大致相等

如果我们提前知道商品价格的概率分布,则可以根据数据概率分布设置每个桶的价格分界线。值得注意的是,数据分布并不一定需要特意统计,也可以根据数据特点采用某种概率模型进行近似。

基数排序

基数排序是一种非比较的排序算法,它适用于整数或字符串等具有固定位数的元素。基数排序的基本思想是将待排序的元素从低位到高位依次进行排序,以实现整体的排序。具体来说,基数排序将元素按照各个位上的值进行桶排序,从最低位到最高位依次进行,最终得到有序的结果。

思考:如何实现在 O(1) 时间复杂度的排序算法?

可以对有限的数字范围内的元素建立一个大哈希表,直接进行映射。

延伸:JDK 中的排序:Arrays.sort 的源码实现

数组长度

所使用的排序算法

length < 47

插入排序

47 <= length < 286

快速排序

length >= 286 且数组基本有序

归并排序

length >= 286 且数组基本无序

快速排序

基本有序的判断方法:逆序对计数法。

延伸:Google 是如何做到如此快的速度对关键词网页进行高质量排序

Google 之所以能够实现如此快速的搜索结果排序,是因为其搜索引擎背后运用了一系列高效的技术和算法。以下是一些关键因素:

  1. 索引技术: Google 使用了高度优化的索引技术,例如倒排索引(Inverted Indexing)。这使得系统能够快速定位包含特定关键词的网页,而不需要遍历整个数据集;

  1. PageRank 算法: Google 的搜索排序算法中,PageRank 算法是一个关键的组成部分。PageRank 基于网页之间的链接关系来评估网页的重要性。这使得搜索结果更加倾向于显示具有高质量内容和受欢迎的网页;
  2. 分布式计算: Google 的搜索系统是基于大规模的分布式计算架构构建的。这意味着搜索索引和计算是分布在多台服务器上完成的,可以并行处理大量的数据。采用分布式计算可以显著提高搜索速度;
  3. 机器学习: Google 使用机器学习技术来不断改进搜索排序。通过分析用户的搜索行为、点击模式以及网页的内容,机器学习模型可以自动调整搜索结果的排序,以提供更符合用户需求的结果。

需要注意的是,Google 搜索引擎的高速度是整个系统综合运作的结果,包括硬件、分布式架构、算法优化等多个方面。这种高效性是通过多年来不断的研究和工程实践来实现的。

延伸:外部排序算法

外部排序算法用于处理不能完全装入内存的大型数据集。由于数据量超出了内存容量,需要借助外存(如磁盘)进行排序。外部排序算法的主要思想是将数据分成适当大小的块,逐块排序后再进行合并。以下是外部排序的基本步骤:

分块:假设有一个非常大的数据集,需要对其进行排序,但内存有限。首先,将数据集分成若干个块,每个块的大小应能在内存中完全处理。

  1. 从数据集中读取一个块的数据到内存中。
  2. 对该块进行内部排序。
  3. 将排序后的块写回外存。
  4. 重复上述步骤,直到所有数据块都已排序并写回外存。

多路归并排序:假设已经有n个排序后的数据块,现在需要将它们合并成一个整体的有序数据集。这一步使用多路归并排序来完成。

  1. 从每个已排序的块中选取第一个元素,构建一个最小堆。
  2. 将堆顶元素(最小的元素)输出到最终的有序输出文件中,并从相应的块中读取下一个元素,替换堆顶元素,并调整堆以维持最小堆性质。
  3. 重复步骤2,直到所有块中的所有元素都已处理完毕。

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

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

相关文章

搭建知识中台:让企业告别低效率

在当今这个信息爆炸、知识更新日新月异的时代&#xff0c;企业面临着前所未有的挑战与机遇。如何在浩瀚的信息海洋中高效筛选、整合并利用知识资源&#xff0c;成为决定企业竞争力的关键因素之一。因此&#xff0c;搭建知识中台&#xff0c;构建企业知识管理的核心枢纽&#xf…

【HBZ分享】Mysql的Explain的各字段含义

Explain各个字段的含义 id 对于 SELECT 语句&#xff0c;每个查询都会被分配一个唯一的ID表示查询的标识符&#xff0c;数字越大越先执行 select_type 表示查询类型或者子查询类型&#xff0c;使用不同的 select_type 来帮助评估查询性能&#xff0c;并确定可以采取哪些优化方法…

DC-4靶机渗透测试

一、靶机下载地址 https://www.vulnhub.com/entry/dc-4,313/ 二、信息收集 1、主机发现 # 使用命令 nmap 192.168.145.0/24 -sn | grep -B 2 "00:0C:29:43:49:A5" 2、端口扫描 # 使用命令 nmap 192.168.145.217 -p- -sV 3、指纹识别 # 使用命令 whatweb "…

低代码开发平台:技术概览、效率与质量的权衡及挑战与机遇

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《热点时事》 期待您的关注 目录 一、技术概览 基本概念 主要特点 市场现状 主流平台优缺点分析 二、效率与质量的权衡 提高…

C++练习备忘录

1. 保留两位小数输出格式 #include <iostream> #include <iomanip> using namespace std; int main() {double S 0;S (15 25) * 20 / 2;cout << fixed << setprecision(2) << S;return 0; }2. 设置输出宽度 #include <iostream> #inclu…

花式表演无人机技术详解

花式表演无人机作为现代科技与艺术融合的典范&#xff0c;以其独特的飞行姿态、绚烂的灯光效果及精准的控制能力&#xff0c;在各类庆典、体育赛事、音乐会等合中展现出非凡的魅力。本文将从以下几个方面对花式表演无人机技术进行详细解析。 1. 三维建模与编程 在花式表演无人…

SpringBoot整合日志功能(slf4j+logback)详解

目录 一、日志门面与日志实现 1.1 什么是日志门面和日志实现&#xff1f; 1.2 为什么需要日志门面&#xff1f; 二、简介 三、日志格式 四、记录日志 4.1 使用日志工厂 4.2 使用Lombok的Slf4j注解 五、日志级别 5.1 日志级别介绍 5.2 配置日志级别 5.3 指定某个包下…

SpringBoot配置--Profile

目录 使用profile 的原因&#xff1f; proflie 的配置方式 多profile文件方式 profile 激活方式 1 配置文件 2 命令行参数 小结 使用profile 的原因&#xff1f; 用来完成不同环境下&#xff0c;配置动态切换功能的&#xff08;具体什么意思呢&#xff1f;假设你在A电脑…

【算法速刷(8/100)】LeetCode —— 21.合并两个有序链表

使用两个指针顺序遍历两个链表&#xff0c;每次都将最小值的那个加到结果链表上&#xff0c;最后如果两个链表不一样长&#xff0c;就将剩下的接到结果后面 无头结点 无头结点的情况下&#xff0c;处处都需要进行判空&#xff0c;将初次赋值和其他时候分为两个情况&#xff0c…

cloud compare二次插件化功能开发详细步骤(一)

点云处理&#xff0c;有一个出名的处理软件&#xff0c;cloud compare&#xff0c;简称cc&#xff0c;将自己实现的功能以插件形式集成到CC里&#xff0c;方便使用 前提 环境&#xff1a;cc 2.13&#xff0c;qt 5.15&#xff0c;cmake 3.18&#xff0c;vs2019【其他组合也可&…

二、AI工作流(低代码)的趋势崛起在即。带你轻松玩转输入-文本组件

对工作流感兴趣的小伙伴可以去试一试。&#x1f525;偷偷的告诉你&#xff0c;它的GPTo4.0不要&#x1f4b0;。传送门&#xff1a;https://www.nyai.chat/chat?invitenyai_1141439 一、能用AI工作流介绍 能用AI-工作流是一个“低代码”工具、它也是个人或者中小企业的提效工…

8G显存玩转书生大模型

基础任务 使用 Cli Demo 完成 InternLM2-Chat-1.8B 模型的部署&#xff0c;并生成 300 字小故事&#xff0c;记录复现过程并截图。 尝试很多方法无解后在网页端重新输入&#xff1a; import torch from transformers import AutoTokenizer, AutoModelForCausalLM使用了Tran…

sql注入(判断字符型/数字型)

目录 字符型 数字型 字符型闭合方式 less-1 less-4 sql注入常见类型包括字符型和数字型&#xff08;json这里不介绍&#xff09; 以sql-labs靶场为例&#xff1b; 字符型 less-1&#xff1a;输入参数id&#xff1a; 这里我将sql查找语句一起输出了&#xff1b; 我们发现…

书生大模型实战营-基础关-XTuner 微调个人小助手认知

XTuner 微调个人小助手认知 环境配置模型效果预览微调数据准备微调配置微调训练权重格式转换模型合并页面对话 环境配置 # 创建虚拟环境 conda create -n xtuner0812 python3.10 -y# 激活虚拟环境&#xff08;注意&#xff1a;后续的所有操作都需要在这个虚拟环境中进行&#…

锂电池剩余寿命预测 | Matlab基于LSTM-Attention的锂电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于LSTM-Attention的锂电池剩余寿命预测&#xff08;单变量&#xff09;&#xff0c;长短期记忆神经网络融合注意力机制&#xff08;自注意力机制&#xff0c;多头注意力机制&#xff09;&#xff08;单变量&…

有效字的字母异位词

给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true示例 2: 输…

8.14-LVS主从+nginx的haproxy+mysql的haproxy+读写分离

一、LVS-主从数据库 # nat # 添加规则 [rootDS ~]# ipvsadm -A -t 192.168.2.130:3306 -s rr [rootDS ~]# ipvsadm -a -t 192.168.2.130:3306 -r 192.168.2.40:3306 -m [rootDS ~]# ipvsadm -a -t 192.168.2.130:3306 -r 192.168.2.42:3310 -m [rootDS ~]# ipvsadm -Ln IP Vir…

javaweb学习笔记(8.10)

一、JS 1.1JS简介 Web标准&#xff1a;由3WC制订 三个组成部分&#xff1a; HTML---》网页的基础结构 CSS---》网页的表现效果 JavaScript---》网页的行为 简介&#xff1a;JS是一门跨平台、面向对象的脚本语言。用来控制网页行为的&#xff0c;使网页交互。 1.2JS的引入…

贷奇乐漏洞学习 --- 两个变态WAF绕过

代码分析 第一个WAF 代码 function dowith_sql($str) {$check preg_match(/select|insert|update|delete|\|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is, $str);if ($check) {echo "非法字符!";exit();}return $str;} 实现原理 这段PHP代码定义了一个…

Linux日常运维-主机名hosts

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 本小章内容就是Linux进阶部分的日常运维部分&#xff0c;掌握这些日常运维技巧或者方法在我们的日常运维过程中会带来很多方…