【算法训练-排序算法 一】【手撕排序】快速排序、堆排序、归并排序

news2025/1/20 5:50:58

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【手撕排序系列】,使用【数组】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。手撕排序系列共3道常考题,分别是【快速排序、归并排序、堆排序】这个顺序也是面试出现频度的顺序
在这里插入图片描述

明确目标题后,附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

快速排序【MID】

首先来最高频的题目,快速排序

题干

在这里插入图片描述

解题思路

使用快速排序的思路来解决,快速排序(Quick Sort)是一种基于分治思想的排序算法,它通过将数组分成较小和较大的两部分,并分别对这两部分进行排序,最终将整个数组排序。快速排序是一种高效的排序算法,通常在平均情况下具有较快的执行速度。
在这里插入图片描述

下面是快速排序的基本思想和步骤:

  1. [划分]选择基准元素(Pivot): 从数组中选择一个元素作为基准元素。

  2. [划分]划分(Partition): 将数组分成两部分,使得基准元素左边的元素都小于等于基准元素,右边的元素都大于基准元素。这一步骤通常称为“划分”。

  3. [解决]递归排序: 递归地对基准元素左边和右边的子数组进行排序。也就是说,对小于基准元素的子数组和大于基准元素的子数组分别执行快速排序。

  4. [合并] 合并: 由于子数组都是在原数组中进行排序,所以最终整个数组也就被排序了。

这些步骤使得较大问题被分解成较小的子问题,这些子问题又能通过递归地应用快速排序来解决。在最好情况下,每次划分都能将数组均匀分成两半,这使得算法的时间复杂度为O(n log n)

然而,需要注意的是,快速排序的性能高度依赖于基准元素的选择。最坏情况下,如果每次划分都使数组分成极不平衡的两部分,算法的时间复杂度可能会退化到O(n^2)。为了应对这种情况,通常可以选择合适的基准元素,如随机选择或者采用三数取中等方法。

总之,快速排序是一种常用且高效的排序算法,尤其适用于大规模数据的排序。

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构
算法快速排序(分治算法)、二分查找
技巧双指针

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param n int整型 the n
     * @return int整型
     */
    public int[] sortArray(int[] nums) {
        return quikSort(nums, 0, nums.length - 1);
    }

    // 对数组进行快速排序
    private int[] quikSort(int[] nums, int start, int end) {
        int privot;
        if (start < end) {
            // 1 获取数组基准值元素位置
            privot = partition(nums, start, end);
            // 2 分治:左半边元素快排
            quikSort(nums, start, privot - 1);
            // 3 分治:右半边元素快排
            quikSort(nums, privot + 1, end);
        }
        return nums;
    }

    // 单次归位基准值方法
    private int partition(int[] nums, int start, int end) {
        // 0 随机选择基准数大小,并与最左侧位置交换
        int randomValueIndex = new Random().nextInt(end - start + 1) + start;
        swap(nums, start, randomValueIndex);

        // 1 数组左边第一个为基准值,双指针分别执行开始和结束
        int privot = nums[start];
        int i = start;
        int j = end;

        // 2 开始交互,直到ij碰面,顺排
        while (i < j) {
            // 2-1 从右边开始找,如果值一直大于基准值则一直循环,直到找到小于基准值的元素,终止
            // 需要注意,满足条件情况下,因为基准值在最左边,最后要与j交换,所以j停止元素一定要小于基准值,所以优先满足j的条件,j要先走
            while (i < j && nums[j] >= privot) {
                j--;
            }
            // 2-2 从左边开始找,如果值一直小于基准值则一直循环,直到找到大于基准值的元素,终止
            while (i < j && nums[i] <= privot) {
                i++;
            }
            // 2-3 找到要交换的元素,且依然满足i<j的条件,交换
            if (i < j) {
                swap(nums, i, j);
            }
        }

        // 3 i和j碰面,说明该交换的元素已经交换完了,最后交换基准值与碰面的值
        swap(nums, start, j);

        // 4 返回基准值的当前位置,需要按此位置分割
        return j;
    }

    // 元素交换方法
    private void swap(int[] numbers, int i, int j) {
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }

}

需要注意,为了保证平衡性,可以选择随机找个值作为基准值

int randomValueIndex = new Random().nextInt(end - start + 1) + start; 
swap(partNums, start, randomValueIndex);

复杂度分析

快速排序的时间复杂度和空间复杂度如下:

时间复杂度:

  • 平均情况: 在平均情况下,快速排序的时间复杂度为O(n log n),其中n是待排序数组的长度。这是因为每次划分都能将数组大致均匀地分成两部分,导致递归的深度大约为log n,而每次划分的过程需要O(n)的时间。

  • 最坏情况: 在最坏情况下,如果每次划分都导致一个极不平衡的分割(例如每次选取的基准元素都是当前子数组的最大或最小元素),那么快速排序的时间复杂度可能退化到O(n^2)。这是因为需要执行n次划分,每次划分都需要O(n)的时间。为了避免最坏情况,通常采用随机选择基准元素或者三数取中法来减少极端情况的发生。

  • 最好情况: 快速排序的最好情况时间复杂度为O(n log n),与平均情况相同。这种情况发生在每次划分都能将数组准确地分成相等的两部分时。

空间复杂度:

快速排序的空间复杂度主要取决于递归调用的深度和每次划分所使用的额外空间。

  • 递归调用的深度: 在递归调用中,每次只需要保存一个基准元素的索引和部分数组的边界信息。因此,递归调用的深度为O(log n)

  • 每次划分所使用的额外空间: 每次划分需要O(1)的额外空间来存储基准元素和进行交换。

综合考虑,快速排序的空间复杂度为O(log n)。这是因为虽然递归调用的深度为O(log n),但在每层递归中所需的额外空间是常数级别的。这使得快速排序在空间上比某些其他排序算法(如归并排序)更加节省。

归并排序【MID】

然后再做一道次高频的题目:归并排序,题干与快排一样

题干

在这里插入图片描述

解题思路

基本思路:借助额外空间,合并两个有序数组,得到更长的有序数组。归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾

接下来实现递归的归并排序

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构
算法归并排序
技巧双指针

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param n int整型 the n
     * @return int整型
     */
    public int[] sortArray(int[] nums) {
        mergeSort(nums, 0, nums.length - 1);
        return nums;
    }

    // 对数组进行递归的归并排序
    private void mergeSort(int[] nums, int left, int right) {
        // 1 与快排不同的是,分割点直接选择中间位置
        int middle = left + (right - left) / 2;

        // 2 只要start<end,就一直进行归并
        if (left < right) {
            // 2-1 先分别归并左右两边,左右两边有序了,则最终有序
            mergeSort(nums, left, middle);
            mergeSort(nums, middle + 1, right);

            // 2-2 最后合并左右两边有序数组
            merge(nums, left, middle, right);
        }
    }

    // 单次划分的归并
    private void merge(int[] nums, int left, int middle, int right) {
        // 1 设置归并临时结果集,大小为归并后的总长度
        int[] result = new int[right - left + 1];

        // 2 定义两个排序数组的指针和结果集指针
        int i = left;
        int j = middle + 1;
        int k = 0;

        // 3 开始比较已排序集合值并将结果加入结果集
        while (i <= middle && j <= right) {
            if (nums[i] < nums[j]) {
                result[k++] = nums[i++];
            } else {
                result[k++] = nums[j++];
            }
        }

        // 4 如果有一个已经用完了,补充另一个排序数组
        while (i <= middle) {
            result[k++] = nums[i++];
        }
        while (j <= right) {
            result[k++] = nums[j++];
        }

        // 5 需要把这一段合并后已排序结果合并到整体nums的分段上
        for (int index = 0; index < result.length; index++) {
            nums[left + index] = result[index];
        }
    }

}

复杂度分析

时间复杂度:O(Nlog⁡N),这里 N 是数组的长度
空间复杂度:O(N),辅助数组与输入数组规模相当

堆排序【MID】

最后做一道频度最低的题目:堆排序

题干

题干与快排及归并排序一致
在这里插入图片描述

解题思路

这里简单介绍下:数组中下标为 i 的节点的左子节点,就是下标为 i∗2 的节点,右子节点就是下标为 i∗2+1 的节点,父节点就是下标为 2i​ 的节点

1 建堆

我们首先将数组原地建成一个堆。所谓“原地”就是,不借助另一个数组,就在原数组上操作。建堆的过程是从后往前处理数组,并且每个数据都是从上往下堆化,因为叶子节点往下堆化只能自己跟自己比较,所以我们直接从最后一个非叶子节点开始,依次堆化就行了
在这里插入图片描述

2 排序

建堆结束之后,数组中的数据已经是按照大顶堆的特性来组织的。数组中的第一个元素就是堆顶,也就是最大的元素。我们把它跟最后一个元素交换,那最大元素就放到了下标为 n 的位置。这个过程有点类似上面讲的“删除堆顶元素”的操作,当堆顶元素移除之后,我们把下标为 n 的元素放到堆顶,然后再通过堆化的方法,将剩下的 n−1 个元素重新构建成堆。堆化完成之后,我们再取堆顶的元素,放到下标是 n−1 的位置,一直重复这个过程,直到最后堆中只剩下标为 1 的一个元素,排序工作就完成了
在这里插入图片描述

代码实现

给出代码实现基本档案

基本数据结构数组
辅助数据结构
算法堆排序
技巧双指针

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param n int整型 the n
     * @return int整型
     */
    public int[] sortArray(int[] nums) {
        heapSort(nums);
        return nums;
    }

    // 1 堆排序函数
    private void heapSort(int[] nums) {
        // 1 构建大顶堆,此时数组0的位置即为堆顶元素,也就是数组最大值
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);

        // 2 将堆顶元素与数组末尾元素交换
        for (int i = nums.length - 1; i >= 1; i--) {
            // 2-1 交换末尾元素与堆顶元素
            swap(nums, i, 0);
            // 2-2 缩小堆的生成范围,不包含末尾元素,因为末尾元素已调整完
            heapSize--;
            // 2-3 因为将一个末尾元素放到了堆顶,且其不一定是剩余元素中最大的,所以需要重新进行堆调整
            heapify(nums, 0, heapSize);
        }
    }

    // 2 构建大顶堆函数
    private void buildMaxHeap(int[] nums, int heapSize) {
        for (int i = heapSize / 2; i >= 0; i--) {
            heapify(nums, i, heapSize);
        }
    }

    // 3 堆调整函数
    private void heapify(int[] nums, int i, int heapsize) {
        // 逐层的进行元素调整:这里包含=号是因为要允许存在只有左节点的情况
        while (2 * i + 1 <= nums.length) {
            // 1 确定节点的左右节点位置
            int leftChild = 2 * i;
            int rightChild = 2 * i + 1;
            int maxPos = 0;

            // 2 当前层:进行堆调整,获取(当前、左子节点、右子节点)中值最大的索引,与堆顶进行交换
            maxPos = i;
            if (leftChild < heapsize && nums[i] < nums[leftChild]  ) {
                maxPos = leftChild;
            }
            if ( rightChild < heapsize && nums[maxPos] < nums[rightChild] ) {
                maxPos = rightChild;
            }

            // 3 如果maxPos不是i,则交换
            if (i != maxPos) {
                swap(nums, i, maxPos);
                // 继续下沉进行堆调整
                i = maxPos;
            } else {
                break;
            }
        }

    }

    // 4元素交换方法
    private void swap(int[] numbers, int i, int j) {
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

复杂度分析

在堆排序算法中,建立堆的时间复杂度通常为O(n),其中n是要排序的元素数量。这是因为堆的构建分为两个阶段:堆的构建和堆的调整。

  1. 堆的构建:首先,将待排序的n个元素按照从左到右的顺序依次插入堆中,这个过程是线性时间的,即O(n)。

  2. 堆的调整:然后,需要对堆进行调整以满足堆的性质(通常是最大堆或最小堆)。这个调整阶段的时间复杂度取决于堆的高度,通常为O(log n)。在堆排序的整个过程中,堆的调整阶段需要执行n次,所以总时间复杂度是O(n log n)

总的来说,堆排序的建堆阶段的时间复杂度是O(n),而排序阶段的时间复杂度是O(n log n)。因此,堆排序的总时间复杂度为O(n + n log n),通常被表示为O(n log n),因为在渐进分析中,线性时间的操作通常被忽略。

由于堆是原地构建,所以空间复杂度为O(N)

拓展知识:分治算法、堆的基本概念、算法比较

1 分治算法

分治法是一种解决问题的算法设计范式,它将一个问题分解成多个相似的子问题,然后解决这些子问题,并将它们的解合并以得出原始问题的解。分治法的核心思想是将大问题分解成更小的、相似的子问题,通过解决子问题来解决原始问题。

分治法通常包含三个步骤:分解(Divide)、解决(Conquer)、合并(Combine)。

  1. 分解(Divide): 将原始问题划分为更小、相似的子问题。这一步骤通常是递归地进行的,即将问题逐步分解为更小规模的子问题。

  2. 解决(Conquer): 递归地解决子问题。当子问题足够小,可以直接求解时,就停止分解,转而解决这些子问题。

  3. 合并(Combine): 将子问题的解合并以得出原始问题的解。这是分治法的关键步骤,将各个子问题的解整合起来形成更大问题的解。

分治法通常用于解决一些可以被分解成相似子问题的问题,如排序、搜索、求解最短路径等。典型的分治算法包括归并排序快速排序。以下是一个分治法的示例:

归并排序:

  1. 分解(Divide): 将数组分成两半,分别对这两半进行排序。

  2. 解决(Conquer): 对分解得到的子数组递归地进行排序,直到子数组长度足够小。

  3. 合并(Combine): 将排好序的子数组合并,得到完整的有序数组。

分治法的优点在于它可以将问题分解成独立的子问题,每个子问题的求解都相对简单。这使得算法设计和理解变得更加清晰。然而,分治法有时会在子问题的合并阶段引入额外的开销,因此在设计分治算法时需要权衡分解和合并的成本。

堆的基本概念

在计算机科学中,一个"堆"(Heap)通常指的是一种特殊的数据结构,它是一种树状结构,通常用于优先队列和堆排序等算法。
在这里插入图片描述

堆具有以下特点:

  1. 完全二叉树结构:堆通常是一棵完全二叉树,这意味着树的所有层级都被填满,除了最底层,最底层的节点从左向右依次填充。这个特性使得堆可以有效地使用数组来表示,因为树的节点可以在数组中按照特定的规则排列,从而节省内存和提高访问效率。

  2. 堆序性质:堆被维护为满足"堆序性质"(Heap Property)的树,这意味着在最大堆(Max Heap)中,对于任意节点i,其父节点的值必须大于或等于i的值;而在最小堆(Min Heap)中,父节点的值必须小于或等于i的值。这个性质使得在堆中的根节点永远是最大值(最大堆)或最小值(最小堆)。

堆被广泛用于解决一些基本问题,如:

  • 优先队列:通过使用最小堆或最大堆,可以实现高效的优先队列,其中具有最高(或最低)优先级的元素在队列的顶部。

  • 堆排序:堆排序是一种基于堆数据结构的排序算法,它利用堆的特性来进行排序。在堆排序中,首先将未排序的元素构建为一个堆,然后反复删除堆顶元素,将其放入已排序部分,直到堆为空。

  • 调度算法:堆可以用于操作系统中的进程调度,其中具有最高优先级的进程被安排在最前面。

  • 最短路径算法:一些最短路径算法,如Dijkstra算法,使用最小堆来快速查找最小距离的节点。

总之,堆是一种重要的数据结构,它提供了高效的方式来管理数据,特别是在需要按优先级对数据进行操作时。最大堆和最小堆分别用于找到最大值和最小值,这使得堆在许多领域中非常有用。

3 排序算法比较

以下是快速排序、归并排序和堆排序的比较,包括时间复杂度、空间复杂度和一些其他关键特点:

特性快速排序归并排序堆排序
时间复杂度平均情况 O(n log n)平均情况 O(n log n)平均情况 O(n log n)
最坏情况 O(n^2)最坏情况 O(n log n)最坏情况 O(n log n)
最佳情况 O(n log n)最佳情况 O(n log n)最佳情况 O(n log n)
稳定性不稳定稳定不稳定
空间复杂度平均情况 O(log n)平均情况 O(n)平均情况 O(1)
最坏情况 O(n)最坏情况 O(n)最坏情况 O(1)
适用性通常用于大型数据集通常用于大型数据集通常用于内存受限情况
且需要稳定排序时
分治策略
额外的数据移动较多较少较少
需要的额外空间递归调用的栈空间辅助数组常数额外空间
(in-place)
实现复杂度中等中等相对较高

总结:

  • 快速排序通常在平均情况下具有较好的性能,但在最坏情况下性能较差,因此不适用于某些特定情况。
  • 归并排序具有一致的性能,但需要较大的额外内存空间,通常不适用于内存受限的情况。
  • 堆排序通常需要较少的额外内存空间,但在排序稳定性和实现复杂性方面有一些局限性,适合内存受限的情况。

选择排序算法应根据具体情况和性能要求来决定,没有一种算法适用于所有情况。

4 稳定性分析

“稳定性"是指排序算法在处理具有相等键值的元素时,能否保持它们在原始序列中的相对顺序。一个排序算法被称为"稳定”,如果对于相等的元素,它们的相对顺序在排序后仍然保持不变。相反,如果排序算法不能保持相等元素的相对顺序,那么它被称为"不稳定"。

下面是对快速排序、归并排序和堆排序的稳定性解释:

  1. 快速排序:快速排序是一个不稳定的排序算法。在快速排序中,相等元素的相对顺序可能会发生变化,具体取决于选择的划分策略和元素交换操作。

  2. 归并排序:归并排序是一个稳定的排序算法。在归并排序中,相等元素的相对顺序始终保持不变。这是因为在合并过程中,如果有相等的元素,它们会按照它们在原始数组中的顺序放置在合并后的数组中。

  3. 堆排序:堆排序通常是一个不稳定的排序算法。虽然堆排序的堆构建过程可能会改变相等元素的相对顺序,但在堆化和排序的过程中,相等元素的相对顺序通常不会被保持。

稳定性在某些应用中很重要,特别是在需要按多个条件进行排序或者需要保持原始数据的某种有序性时。在这些情况下,稳定的排序算法更有用。如果稳定性不是关键因素,那么可以选择性能更高的不稳定排序算法。

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

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

相关文章

PCL点云处理之配准中的匹配对连线可视化显示 Correspondences(二百一十九)

PCL点云处理之配准中的匹配对连线可视化显示 Correspondences(二百一十九) 一、算法介绍二、算法实现1.可视化代码2.完整代码(特征匹配+可视化)最终效果一、算法介绍 关于点云配准中的匹配对,如果能够可视化将极大提高实验的准确性,还好PCL提供了这样的可视化工具,做法…

【Java零基础入门到就业】第一天:java简介和cmd窗口的一些常见命令

1、java简介 Java是一种基于类的、面向对象的编程语言&#xff0c;它被设计成具有尽可能少的实现依赖。它旨在让应用程序开发人员编写一次&#xff0c;并在任何地方运行(WORA)&#xff0c;这意味着编译后的Java代码可以在所有支持Java的平台上运行&#xff0c;而无需重新编译。…

pikachu靶场搭建及通关

一、靶场搭建 下载工具&#xff1a;phpstudy Pikachu靶机下载地址&#xff1a; https://github.com/zhuifengshaonianhanlu/pikachu 下载后解压缩并放入如下文件夹&#xff08;网站根目录&#xff09; 建议修改文件名称为 pikachu 修改配置文件&#xff08;mysql 用户名&…

ORA-00600: internal error code, arguments

通过rman将11g异机升级到19c时&#xff0c;应用归档时报错&#xff0c;报错如下 RMAN> recover database ; Starting recover at 2023-10-15 21:10:02 allocated channel: ORA_DISK_1 channel ORA_DISK_1: SID5776 device typeDISK starting media recovery media recove…

在vs code中创建一个名为 “django_env“ 的虚拟环境报错?!以下方法可以解决

# vs code 终端窗口中运行&#xff1a; mkvirtualenv django_env # 拓展&#xff1a; mkvirtualenv django_env 是一个命令&#xff0c;用于创建一个名为 "django_env" 的虚拟环境。虚拟环境是一种用于隔离不同Python项目所需依赖的工具。通过创建虚拟环境&#x…

在pycharm中运行js文件,附加node.js下载步骤

文章目录 一、前言二、node.js安装和配置(如果之前就安装好了可以直接跳过)1、进入官网下载安装包2、在本地安装node.js3、环境配置4、验证是否安装成功5、修改下载位置(默认是在c盘&#xff0c;这个根据个人需求)6、设置默认模块包7、测试一下是否修改成功(要进入管理员模式的…

YOLOv5网络结构图

网络结构图&#xff08;简易版和详细版&#xff09; 网络框架介绍 前言&#xff1a; YOLOv5是一种基于轻量级卷积神经网络&#xff08;CNN&#xff09;的目标检测算法&#xff0c;整体可以分为三个部分&#xff0c; backbone&#xff0c;neck&#xff0c;head。 如上图所示…

测试左移右移-理论篇

目录 前言一、浅解左移1.什么是测试左移&#xff1f;1.1对产品1.2对开发1.3对测试1.4对运维 二、浅解右移1.1对产品1.2对开发1.3对测试1.4对运维 三、总结 前言 测试左移右移&#xff0c;很多人说能让测试更拥有主动权&#xff0c;展示出测试岗位也是有很大的价值&#xff0c;…

分享一个制作AI视频的好工具

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 同行十二年&#xff0c;不知木兰是女郎。 前几天在【AI破局俱乐部】看到了【元峰】老师的分享&#xff0c;干货满满&#xff0c;这个分享是关于数字人的…

动态规划算法(3)--0-1背包、石子合并、数字三角形

目录 一、0-1背包 1、概述 2、暴力枚举法 3、动态规划 二、石子合并问题 1、概述 2、动态规划 3、环形石子怎么办&#xff1f; 三、数字三角形问题 1、概述 2、递归 3、线性规划 四、租用游艇问题 一、0-1背包 1、概述 0-1背包&#xff1a;给定多种物品和一个固定…

OpenCV完美实现两张图片的全景拼接(详细教程)

目录 1&#xff0c;主要步骤 1.1 导入需要的包和模块&#xff0c;并读取两张待拼接的图片&#xff0c;这里我们假设它们为 left.jpg 和 right.jpg。 1.2 创建SIFT检测器 1.3 创建一个基于 FLANN 的匹配器 1.4 筛选过程删除掉一些不合适的匹配点&#xff0c;只保留最好的…

[Spring] SpringMVC 简介(三)

目录 九、SpringMVC 中的 AJAX 请求 1、简单示例 2、RequestBody&#xff08;重点关注“赋值形式”&#xff09; 3、ResponseBody&#xff08;经常用&#xff09; 4、为什么不用手动接收 JSON 字符串、转换 JSON 字符串 5、RestController 十、文件上传与下载 1、Respo…

03_51单片机点亮LED灯

51单片机是一种非常常见的单片机型号&#xff0c;广泛应用于各种嵌入式系统和电子设备中。LED灯是一种常见的输出设备&#xff0c;用于显示信息或指示状态。下面是关于51单片机控制LED灯的介绍&#xff1a; 1. 连接LED灯&#xff1a;将LED的正极连接到51单片机的一个I/O引脚&a…

英语——歌曲篇——All Out Of Love

All Out Of Love [Air Supply失落的爱] 歌词 I’m lying alone with my head on the phone Thinking of you till it hurts I know you hurt too but what else can we do Tormented and torn apart I wish I could carry your smile in my heart For times when my life se…

MacOS ventura跳过配置锁

Macbook pro 2021跳配置锁 1.什么是配置锁&#xff1f; 配置锁顾名思义就是美国一些企业和公司向苹果工公司定制采购的机器&#xff0c;这些机器一般供应内部员工使用&#xff0c;这种机器和正常机没有什么区别&#xff0c;也是无锁三网机器&#xff0c;功能和正常机器一摸一…

去雨去雪去雾算法本地实验速度对比

在进行去雨去雪去雾算法的实验过程中&#xff0c;博主使用自己的电脑进行实验&#xff0c;尽管能够成功运行&#xff0c;但速度却属实不尽人意&#xff0c;博主的笔记本显卡是GTX960&#xff0c;显存为4G&#xff0c;在进行实验的过程中&#xff0c;batch-size只能设置为3&…

找不到mfc140u.dll,无法继续执行代码 cdr

在计算机系统中&#xff0c;DLL文件&#xff08;动态链接库&#xff09;是一种非常重要的资源。它们包含了许多可以在程序运行时被调用的代码和数据。然而&#xff0c;当某个特定的DLL文件丢失或损坏时&#xff0c;可能会导致程序无法正常运行。本文将详细介绍五个解决mfc140u.…

flink教程

文章目录 来自于尚硅谷教程1. Flink概述1.1 特点1.2 与SparkStreaming对比 2. Flink部署2.1 集群角色2.2 部署模式2.3 Standalone运行模式2.3.1 本地会话模式部署2.3.2 应用模式 2.4 YARN运行模式2.4.1 会话模式部署2.4.2 应用模式部署 2.5 历史服务 3. 系统架构3.1 并行度3.2 …

线性回归原理

1、 线性回归的原理 1.1 线性回归应用场景 房价预测 销售额度预测 金融:贷款额度预测、利用线性回归以及系数分析因子1.2 什么是线性回归 1.2.1定义与公式 线性回归(Linear regression)是利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的…

在Unity中挂载C#脚本的三种方法

第一种 ①在Project&#xff08;工程&#xff09;窗口的某个文件夹中&#xff08;也可以选择新建在Assets&#xff08;资源根目录&#xff09;中&#xff09;&#xff0c;然后单击鼠标右键&#xff0c;选择Create->C# Script 注意&#xff1a;扩展名在Unity编辑器中是隐藏…