C++秋招必知必会(常用排序算法)

参考引用

  • Hello 算法

常用排序算法

在这里插入图片描述

1. 选择排序

1.1 算法原理与流程
  • 开启一个循环,每轮从未排序区间选择最小的元素,将其放到已排序区间的末尾
  • 设数组的长度为 n n n,选择排序的算法流程如下图
    • 初始状态下,所有元素未排序,即未排序(索引)区间为 [ 0 , n − 1 ] [0, n-1] [0,n1]
    • 选取区间 [ 0 , n − 1 ] [0, n-1] [0,n1] 中的最小元素,将其与索引 0 0 0 处元素交换。完成后,数组前 1 1 1 个元素已排序
    • 选取区间 [ 1 , n − 1 ] [1, n-1] [1,n1] 中的最小元素,将其与索引 1 1 1 处元素交换。完成后,数组前 2 2 2 个元素已排序
    • 以此类推。经过 n − 1 n-1 n1 轮选择与交换后,数组前 n − 1 n-1 n1 个元素已排序
    • 仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成
1.2 算法实现
  • 时间复杂度(非自适应排序) O ( n 2 ) O(n^2) O(n2)
    • 外循环共 n − 1 n-1 n1 轮,第一轮的未排序区间长度为 n n n,最后一轮的未排序区间长度为 2 2 2,即各轮外循环分别包含 n 、 n − 1 、 . . . 、 3 、 2 n、n-1、...、3、2 nn1...32 轮内循环,求和为 ( n − 1 ) ( n + 2 ) 2 \frac{(n-1)(n+2)}2 2(n1)(n+2)
  • 空间复杂度(原地排序) O ( 1 ) O(1) O(1)
    • 指针 i 和 j 使用常数大小的额外空间
  • 非稳定排序:元素 nums[i] 有可能被交换至与其相等的元素的右边,导致两者的相对顺序发生改变
    在这里插入图片描述
#include <iostream>
#include <vector>

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

/* 选择排序 */
void selectionSort(std::vector<int> &nums) {
    int n = nums.size();
    // 外循环:未排序区间为 [i, n-1]
    for (int i = 0; i < n - 1; i++) {
        // 内循环:找到未排序区间内的最小元素索引
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (nums[j] < nums[minIndex])
                minIndex = j; // 记录最小元素的索引
        }
        // 将该最小元素与未排序区间的首个元素交换
        swap(nums[i], nums[minIndex]);
    }
}

int main() {
    std::vector<int> nums = {5, 2, 9, 1, 3};
    
    std::cout << "Original array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    selectionSort(nums);
    
    std::cout << "Sorted array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

2. 冒泡排序

2.1 算法原理与流程
  • 冒泡排序通过连续地比较与交换相邻元素实现排序,利用元素交换操作来模拟:从数组最左端开始向右遍历,依次比较相邻元素大小,如果 “左元素 > 右元素” 就交换二者,遍历完成后,最大的元素会被移动到数组的最右端
  • 设数组的长度为 n n n,冒泡排序的步骤如下图
    • 1、首先,对 n n n 个元素执行 “冒泡”,将数组的最大元素交换至正确位置
    • 2、接下来,对剩余 n − 1 n-1 n1 个元素执行 “冒泡”,将第二大元素交换至正确位置
    • 3、以此类推,经过 n − 1 n-1 n1 轮 “冒泡” 后,前 n − 1 n-1 n1 大的元素都被交换至正确位置
    • 4、仅剩的一个元素必定是最小元素,无须排序,因此数组排序完成
      在这里插入图片描述
2.2 算法实现
  • 时间复杂度(自适应排序) O ( n 2 ) O(n^2) O(n2)
    • 各轮 “冒泡” 遍历的数组长度依次为 n − 1 、 n − 2 、 . . . 2 、 1 n-1、n-2、...2、1 n1n2...21,总和 ( n − 1 ) n 2 \frac{(n-1)n}2 2(n1)n
    • 输入数组完全有序时,可达最佳时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度(原地排序) O ( 1 ) O(1) O(1)
    • 指针 i 和 j 使用常数大小的额外空间
  • 稳定排序:由于在 “冒泡” 中遇到相等元素不交换
#include <iostream>
#include <vector>

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

void bubbleSort(std::vector<int> &nums) {
    // 外循环:未排序区间为 [0, i]
    for (int i = nums.size() - 1; i > 0; i--) {
        bool flag = false; // 初始化标志位
        // 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交换 nums[j] 与 nums[j + 1]
                swap(nums[j], nums[j + 1]);
                flag = true; // 记录交换元素
            }
        }
        if (!flag)
            break; // 此轮“冒泡”未交换任何元素,直接跳出
    }
}

int main() {
    std::vector<int> nums = {5, 2, 9, 1, 3};
    
    std::cout << "Original array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    bubbleSort(nums);
    
    std::cout << "Sorted array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

3. 插入排序

3.1 算法原理与流程
  • 插入排序的工作原理

    • 在未排序区间选择一个基准元素,将该元素与其左侧已排序区间的元素逐一比较大小,并将该元素插入到正确的位置,设基准元素为 base,需要将从目标索引到 base 之间的所有元素向右移动一位,然后将 base 赋值给目标索引
      在这里插入图片描述
  • 插入排序的整体流程

    • 初始状态下,数组的第 1 1 1 个元素已完成排序
    • 选取数组的第 2 2 2 个元素作为 base,将其插入到正确位置后,数组的前 2 2 2 个元素已排序
    • 选取第 3 3 3 个元素作为 base,将其插入到正确位置后,数组的前 3 3 3 个元素已排序
    • 以此类推,在最后一轮中,选取最后一个元素作为 base,将其插入到正确位置后,所有元素均已排序
      在这里插入图片描述
3.2 算法实现
  • 时间复杂度(自适应排序) O ( n 2 ) O(n^2) O(n2)
    • 每次插入操作分别需要循环 n − 1 、 n − 2 、 . . . 2 、 1 n-1、n-2、...2、1 n1n2...21,总和 ( n − 1 ) n 2 \frac{(n-1)n}2 2(n1)n
    • 遇到有序数据时,插入操作会提前终止,当输入数组完全有序时,插入排序最佳时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度(原地排序) O ( 1 ) O(1) O(1)
    • 指针 i 和 j 使用常数大小的额外空间
  • 稳定排序:插入过程中会将元素插入到相等元素的右侧,不会改变它们的顺序
#include <iostream>
#include <vector>

void insertionSort(std::vector<int> &nums) {
    // 外循环:已排序元素数量为 1, 2, ..., n
    for (int i = 1; i < nums.size(); i++) {
        int base = nums[i];
        int j = i - 1;
        // 内循环:将 base 插入到已排序部分的正确位置
        while (j >= 0 && nums[j] > base) {
            nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
            j--;
        }
        // j 已在上面 while 循环中 j--,故此处为 j + 1 
        nums[j + 1] = base; // 将 base 赋值到正确位置
    }
}

int main() {
    std::vector<int> nums = {5, 2, 9, 1, 3};
    
    std::cout << "Original array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    insertionSort(nums);
    
    std::cout << "Sorted array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

插入排序的使用频率显著高于冒泡排序和选择排序

  • 冒泡排序基于元素交换实现,需要借助一个临时变量,共涉及 3 个单元操作;插入排序基于元素赋值实现,仅需 1 个单元操作。因此,冒泡排序的计算开销通常比插入排序更高
  • 选择排序在任何情况下的时间复杂度都为 O ( n 2 ) O(n^2) O(n2)。如果给定一组部分有序的数据,插入排序通常比选择排序效率更高

4. 快速排序

4.1 算法原理与流程
  • 快速排序是一种基于分治策略的排序算法,核心操作是 “哨兵划分”,其目标是:选择数组中的某个元素作为 “基准数”,将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧
  • 哨兵划分的流程
    • 1、选取数组最左端元素作为基准数,初始化两个指针 i i i j j j 分别指向数组的两端
    • 2、设置两个循环,从右向左找首个小于基准数的元素,从左向右找首个大于基准数的元素,然后交换这两个元素
    • 3、循环执行步骤 2,直到 i i i j j j 相遇时停止,最后将基准数交换至两个子数组的分界线
    • 哨兵划分完成后,原数组被划分成三部分:左子数组、基准数、右子数组,且满足 “左子数组任意元素 ≤ 基准数 ≤ 右子数组任意元素”,哨兵划分的实质是将一个较长数组的排序问题简化为两个较短数组的排序问题

在这里插入图片描述

  • 快速排序的整体流程
    • 首先,对原数组执行一次 “哨兵划分”,得到未排序的左子数组和右子数组
    • 然后,对左子数组和右子数组分别递归执行 “哨兵划分”
    • 持续递归,直至子数组长度为 1 1 1 时终止,从而完成整个数组的排序

    递归:通过重复将问题分解为同类的子问题而解决问题的方法

在这里插入图片描述

4.2 算法实现
  • 时间复杂度(自适应排序) O ( n l o g n ) O(nlogn) O(nlogn)
    • 在平均情况下,哨兵划分的递归层数为 l o g n logn logn,每层中的总循环数为 n n n,总体使用 O ( n l o g n ) O(nlogn) O(nlogn) 时间
    • 在最差情况下,每轮哨兵划分操作都将长度为 n n n 的数组划分为长度为 0 0 0 n − 1 n-1 n1 的两个子数组,此时递归层数达到 n n n,每层中的循环数为 n n n,总体使用 O ( n 2 ) O(n^2) O(n2) 时间
  • 空间复杂度(原地排序) O ( n ) O(n) O(n)
    • 在输入数组完全倒序的情况下,达到最差递归深度 n n n,使用 O ( n ) O(n) O(n) 栈帧空间
    • 排序操作是在原数组上进行的,未借助额外数组
  • 非稳定排序:在哨兵划分的最后一步,基准数可能会被交换至相等元素的右侧
#include <iostream>
#include <vector>

/* 元素交换 */
void swap(std::vector<int> &nums, int i, int j) {
    int tmp = nums[i];
    nums[i] = nums[j];
    nums[j] = tmp;
}

/* 哨兵划分 */
int partition(std::vector<int> &nums, int left, int right) {
    // 以 nums[left] 为基准数
    int i = left, j = right;
    while (i < j) {
        while (i < j && nums[j] >= nums[left])
            j--; // 从右向左找首个小于基准数的元素
        while (i < j && nums[i] <= nums[left])
            i++;          // 从左向右找首个大于基准数的元素
        swap(nums, i, j); // 交换这两个元素
    }
    swap(nums, i, left); // 将基准数交换至两子数组的分界线
    return i;            // 返回基准数的索引
}

/* 快速排序 */
void quickSort(std::vector<int> &nums, int left, int right) {
    // 子数组长度为 1 时终止递归
    if (left >= right)
        return;
    // 哨兵划分
    int pivot = partition(nums, left, right);
    // 递归左子数组、右子数组
    quickSort(nums, left, pivot - 1);
    quickSort(nums, pivot + 1, right);
}

int main() {
    std::vector<int> nums = {5, 2, 9, 1, 3};
    
    std::cout << "Original array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    quickSort(nums, 0, nums.size() - 1);
    
    std::cout << "Sorted array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}
  • 快速排序为什么快?
    • 出现最差情况的概率很低
      • 虽然快速排序的最差时间复杂度为 O ( n 2 ) O(n^2) O(n2)没有归并排序稳定,但绝大多数情况下,快速排序能在 O ( n l o g n ) O(nlogn) O(nlogn) 的时间复杂度下运行
    • 缓存使用效率高
      • 在执行哨兵划分操作时,系统可将整个子数组加载到缓存,因此访问元素的效率较高,而堆排序需要跳跃式访问元素,从而缺乏这一特性
    • 复杂度的常数系数小
      • 在上述三种分治排序算法中(快速、归并、堆排序),快速排序的比较、赋值、交换等操作的总数量最少,这与 “插入排序” 比 “冒泡排序” 更快的原因类似

5. 归并排序

5.1 算法原理与流程
  • 归并排序是一种基于分治策略的排序算法,包含 “划分” 和 “合并” 阶段
    • “划分阶段” 从顶至底递归地将数组从中点切分为两个子数组
      • 1、计算数组中点 mid ,递归划分左子数组(区间 [left, mid] )和右子数组(区间 [mid + 1, right] )
      • 2、递归执行步骤 1,直至子数组区间长度为 1 时终止
    • “合并阶段” 从底至顶地将左子数组和右子数组合并为一个有序数组
      • 从长度为 1 的子数组开始合并,合并阶段中的每个子数组都是有序的
        在这里插入图片描述
5.2 算法实现
  • 时间复杂度(非自适应排序) O ( n l o g n ) O(nlogn) O(nlogn)
    • 划分产生高度为 l o g n logn logn 的递归树,每层合并的总操作数量为 n n n,因此总体时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
  • 空间复杂度(非原地排序) O ( n ) O(n) O(n)
    • 递归深度为 l o g n logn logn,使用 O ( l o g n ) O(logn) O(logn) 大小的栈帧空间。合并操作需要借助辅助数组实现,使用 O ( n ) O(n) O(n) 大小的额外空间
  • 稳定排序:在合并过程中,相等元素的次序保持不变
#include <iostream>
#include <vector>

/* 合并左子数组和右子数组 */
void merge(std::vector<int> &nums, int left, int mid, int right) {
    // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
    // 创建一个临时数组 tmp ,用于存放合并后的结果
    std::vector<int> tmp(right - left + 1);
    // 初始化左子数组和右子数组的起始索引
    int i = left, j = mid + 1, k = 0;
    // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
    while (i <= mid && j <= right) {
        if (nums[i] <= nums[j])
            tmp[k++] = nums[i++];
        else
            tmp[k++] = nums[j++];
    }
    // 将左子数组和右子数组的剩余元素复制到临时数组中
    while (i <= mid) {
        tmp[k++] = nums[i++];
    }
    while (j <= right) {
        tmp[k++] = nums[j++];
    }
    // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
    for (k = 0; k < tmp.size(); k++) {
        nums[left + k] = tmp[k];
    }
}

/* 归并排序 */
void mergeSort(std::vector<int> &nums, int left, int right) {
    // 终止条件
    if (left >= right)
        return; // 当子数组长度为 1 时终止递归
    // 划分阶段
    int mid = (left + right) / 2;    // 计算中点
    mergeSort(nums, left, mid);      // 递归左子数组
    mergeSort(nums, mid + 1, right); // 递归右子数组
    // 合并阶段
    merge(nums, left, mid, right);
}

int main() {
    std::vector<int> nums = {5, 2, 9, 1, 3};
    
    std::cout << "Original array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    mergeSort(nums, 0, nums.size() - 1);
    
    std::cout << "Sorted array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

6. 堆排序

6.1 算法原理与流程
  • 堆排序是一种基于堆数据结构实现的分治策略排序算法
  • 设数组的长度为 n n n,堆排序的流程如下
    • 1、输入数组并建立大顶堆。完成后,最大元素位于堆顶
    • 2、将堆顶元素(第一个元素)与堆底元素(最后一个元素)交换。完成交换后,堆的长度减 1 1 1,已排序元素数量加 1 1 1
    • 3、从堆顶元素开始,从顶到底执行堆化操作(sift down)。完成堆化后,堆的性质得到修复
    • 4、循环执行第 2 步和第 3 步。循环 n − 1 n-1 n1 轮后,即可完成数组排序
      在这里插入图片描述
6.2 算法实现
  • 时间复杂度(非自适应排序) O ( n l o g n ) O(nlogn) O(nlogn)
    • 建堆操作使用 O ( n ) O(n) O(n) 时间,从堆中提取最大元素的时间复杂度为 O ( l o g n ) O(logn) O(logn),共循环 n − 1 n-1 n1
  • 空间复杂度(原地排序) O ( 1 ) O(1) O(1)
    • 几个指针变量使用 $O(1) 空间,元素交换和堆化操作都是在原数组上进行的
  • 非稳定排序:在交换堆顶元素和堆底元素时,相等元素的相对位置可能发生变化
#include <iostream>
#include <vector>

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
void siftDown(std::vector<int> &nums, int n, int i) {
    while (true) {
        // 判断节点 i, l, r 中值最大的节点,记为 ma
        int l = 2 * i + 1;
        int r = 2 * i + 2;
        int ma = i;
        if (l < n && nums[l] > nums[ma])
            ma = l;
        if (r < n && nums[r] > nums[ma])
            ma = r;
        // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
        if (ma == i) {
            break;
        }
        // 交换两节点
        swap(nums[i], nums[ma]);
        // 循环向下堆化
        i = ma;
    }
}

/* 堆排序 */
void heapSort(std::vector<int> &nums) {
    // 建堆操作:堆化除叶节点以外的其他所有节点
    for (int i = nums.size() / 2 - 1; i >= 0; --i) {
        siftDown(nums, nums.size(), i);
    }
    // 从堆中提取最大元素,循环 n-1 轮
    for (int i = nums.size() - 1; i > 0; --i) {
        // 交换根节点与最右叶节点(交换首元素与尾元素)
        swap(nums[0], nums[i]);
        // 以根节点为起点,从顶至底进行堆化
        siftDown(nums, i, 0);
    }
}

int main() {
    std::vector<int> nums = {5, 2, 9, 1, 3};
    
    std::cout << "Original array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    heapSort(nums);
    
    std::cout << "Sorted array: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

7. 桶排序

7.1 算法原理与流程
  • 桶排序是分治策略的一个典型应用,原理如下
    • 通过设置一些具有大小顺序的桶,每个桶对应一个数据范围,将数据平均分配到各个桶中
    • 然后,在每个桶内部分别执行排序
    • 最终按照桶的顺序将所有数据合并
  • 考虑一个长度为 n n n 的数组,其元素是范围 [ 0 , 1 ) [0,1) [0,1)内的浮点数,桶排序的流程如下
    • 初始化 k k k 个桶,将 n n n 个元素分配到 k k k 个桶中
    • 对每个桶分别执行排序
    • 按照桶从小到大的顺序合并结果
      在这里插入图片描述
7.2 算法实现
  • 桶排序适用于处理体量很大的数据如:输入数据包含 100 万个元素,由于空间限制,系统内存无法一次性加载所有数据。此时,可以将数据分成 1000 个桶,然后分别对每个桶进行排序,最后将结果合并
    • 时间复杂度 O ( n + k ) O(n+k) O(n+k):假设元素在各个桶内平均分布,那么每个桶内的元素数量为 n k \frac{n}{k} kn。假设排序单个桶使用 O ( n k log ⁡ n k ) O(\frac{\mathrm{n}}{k}\log\frac{\mathrm{n}}{k}) O(knlogkn) 时间,则排序所有桶使用 O ( n log ⁡ n k ) O(n\log{\frac{n}{k}}) O(nlogkn) 时间。当桶数量 k k k 比较大时,时间复杂度则趋向于 O ( n ) O(n) O(n)。合并结果时需要遍历所有桶和元素,花费 O ( n + k ) O(n+k) O(n+k) 时间
    • 自适应排序:在最差情况下,所有数据被分配到一个桶中,且排序该桶使用 O ( n 2 ) O(n^2) O(n2) 时间
    • 空间复杂度(非原地排序) O ( n + k ) O(n+k) O(n+k):需要借助 k k k 个桶和总共 n n n 个元素的额外空间

    桶排序是否稳定取决于排序桶内元素的算法是否稳定

#include <iostream>
#include <vector>
using namespace std;

void bubbleSort(vector<float>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // Swap arr[j] and arr[j+1]
                float temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

/* 桶排序 */
void bucketSort(vector<float> &nums) {
    // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
    int k = nums.size() / 2;
    vector<vector<float>> buckets(k);
    // 1. 将数组元素分配到各个桶中
    for (float num : nums) {
        // 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
        int i = num * k;
        // 将 num 添加进桶 bucket_idx
        buckets[i].push_back(num);
    }
    // 2. 对各个桶执行排序
    for (vector<float> &bucket : buckets) {
        // 使用内置排序函数,也可以替换成其他排序算法
        bubbleSort(bucket); // 使用冒泡排序手动实现替代 std::sort
    }
    // 3. 遍历桶合并结果
    int i = 0;
    for (vector<float> &bucket : buckets) {
        for (float num : bucket) {
            nums[i++] = num;
        }
    }
}

int main() {
    std::vector<float> nums = {0.8, 0.2, 0.3, 0.5, 0.1, 0.9, 0.7, 0.6, 0.4};
    
    cout << "Original array:" << endl;
    for (float num : nums) {
        cout << num << " ";
    }
    cout << endl;
    
    bucketSort(nums);
    
    cout << "Sorted array:" << endl;
    for (float num : nums) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QEMU源码全解析 —— virtio(22)

接前一篇文章&#xff1a;QEMU源码全解析 —— virtio&#xff08;21&#xff09; 前几回讲解了virtio驱动的加载。本回开始讲解virtio驱动的初始化。 在讲解virtio驱动的初始化之前&#xff0c;先要介绍virtio配置的函数集合变量virtio_pci_config_ops。实际上前文书也有提到…

电脑亮度怎么调节?一文详解,亮度调整随心所欲!

在我们日常使用电脑的时候&#xff0c;屏幕亮度的合理调节是关乎舒适度和视觉体验的一项重要任务。无论是在白天面对明亮的阳光&#xff0c;还是在夜晚为了减少眼睛疲劳&#xff0c;灵活地调整电脑屏幕亮度&#xff0c;让其适应不同环境和个人需求&#xff0c;成为了提高工作效…

虚拟列表【vue】等高虚拟列表/非等高虚拟列表

文章目录 1、等高虚拟列表2、非等高虚拟列表 1、等高虚拟列表 参考文章1 参考文章2 <!-- eslint-disable vue/multi-word-component-names --> <template><divclass"waterfall-wrapper"ref"waterfallWrapperRef"scroll"handleScro…

vue里echarts的使用:画饼图和面积折线图

vue里echarts的使用,我们要先安装echarts,然后在main.js里引入: //命令安装echarts npm i echarts//main.js里引入挂载到原型上 import echarts from echarts Vue.prototype.$echarts = echarts最终我们实现的效果如下: 头部标题这里我们封装了一个全局公共组件common-he…

Spark: a little summary

转眼写spark一年半了&#xff0c;从之前写机器学习组件、做olap到后面做图计算&#xff0c;一直都是用的spark&#xff0c;惭愧的是没太看过里面的源码。这篇文章的目的是总结一下Spark里面比较重要的point&#xff0c;重点部分会稍微看一下源代码&#xff0c;因为spark是跟cli…

基于springboot+vue的靓车汽车销售网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Android中Transition过渡动画的简单使用

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 一、布局xml文件代码如下&#xff1a; <?xml version"1.0" encoding&quo…

【ctfshow—web】——信息搜集篇1(web1~20详解)

ctfshow—web题解 web1web2web3web4web5web6web7web8web9web10web11web12web13web14web15web16web17web18web19web20 web1 题目提示 开发注释未及时删除 那就找开发注释咯&#xff0c;可以用F12来查看&#xff0c;也可以CtrlU直接查看源代码呢 就拿到flag了 web2 题目提示 j…

H5移动端文件预览pdf

H5移动端文件预览pdf 需求&#xff1a;H5页面嵌入浙政钉&#xff0c;需要文件预览Pdf。 试用了多个插件&#xff0c;踩了很多坑&#xff0c;如果小伙伴有类似填坑经历&#xff0c;并成功解决&#xff0c;感谢留言指点&#xff01;&#xff01;&#xff01; 先讲最终方案&#x…

[云原生] 二进制安装K8S(中)

书接上文&#xff0c;我们继续部署剩余的插件 一、K8s的CNI网络插件模式 2.1 k8s的三种网络模式 K8S 中 Pod 网络通信&#xff1a; &#xff08;1&#xff09;Pod 内容器与容器之间的通信 在同一个 Pod 内的容器&#xff08;Pod 内的容器是不会跨宿主机的&#xff09;共享…

VG5032VDN 电压控制的晶体振荡器 (VCXO) 输出:LVDS

在今天繁复多变的电子市场中&#xff0c;设计师不断寻求更稳定、更灵活的时钟解决方案&#xff0c;以满足从通信网络到工业控制系统的广泛应用。VG5032VDN VCXO是一款高性能的电压控制晶体振荡器 它结合了高性能、多用途性和紧凑设计&#xff0c;是一款适合广泛应用的晶体振荡…

玩主机游戏能省去不少烦恼?+主机该购买哪台?

文/嘉兰SK 来到次世代&#xff0c;玩家们最关心的问题逐渐变成了购买的游戏能否支持升级。 各个游戏厂商也没有闲着。 此前还有标准版、黄金版、终极版、决定版等一系列。 想出很多招数。 于是很多新玩家开始疑惑&#xff1a;你们都说玩主机游戏可以省去很多麻烦&#xff0c;可…

航空航天5G智能工厂数字孪生可视化平台,推进航空航天数字化转型

航空航天5G智能工厂数字孪生可视化平台&#xff0c;推进航空航天数字化转型。随着科技的不断发展&#xff0c;数字化转型已经成为各行各业关注的焦点。航空航天业作为高端制造业的代表&#xff0c;也在积极探索数字化转型之路。为了更好地推进航空航天数字化转型&#xff0c;一…

设置主从复制时发生报错Could not find first log file name in binary log index file‘;解决方案

如图所示&#xff0c;slave_io_runnind:no,slave_sql_running:yes 此时&#xff0c;主从配置错误&#xff0c;我们可以查看Last_IO_Error:来查看报错信息 此时&#xff0c;我们需要停止从服务器的主从服务&#xff0c; mysql> stop slave; Query OK, 0 rows affected, 1 w…

【汽车电子】万字详解汽车标定与XCP协议

XCP协议基础 文章目录 XCP协议基础一、引言1.1 什么是标定1.2 什么时候进行标定1.3 标定的意义 二、XCP协议简介2.1 xcp简介2.2 XCP如何加快开发过程&#xff1f;2.3 XCP的主要作用 三、XCP工作过程3.1 工作过程3.2 通讯模型3.3 测量与标定 四、XCP报文解析4.1 数据包报文格式4…

petalinux_zynq7 驱动DAC以及ADC模块之六:qt显示adc波形

前文&#xff1a; petalinux_zynq7 C语言驱动DAC以及ADC模块之一&#xff1a;建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296petalinux_zynq7 C语言驱动DAC以及ADC模块之二&#xff1a;petalinuxhttps://blog.csdn.net/qq_27158179/article/details/1362…

【C语言】走迷宫之推箱子

前言&#xff1a; 在上一篇文章当中我介绍了一个走迷宫的写法&#xff0c;但是那个迷宫没什么可玩性和趣味性&#xff0c;所以我打算在迷宫的基础上加上一个推箱子&#xff0c;使之有更好的操作空间&#xff0c;从而增强了游戏的可玩性和趣味性。 1. 打印菜单 void menu() {…

BUUCTF第二十四、二十五题解题思路

目录 第二十四题CrackRTF 第二十五题[2019红帽杯]easyRE1 第二十四题CrackRTF 查壳 无壳&#xff0c;32位&#xff0c;用32位IDA打开&#xff0c;打开后的main函数很短&#xff0c;可以找到一句“jmz _main_0”——跳转到 _main_0&#xff0c;说明真正的主函数是_main_0&am…

opencv图像的本质

目的 OpenCV是一个跨平台的库&#xff0c;使用它我们可以开发实时的计算机视觉应用程序。 它主要集中在图像处理&#xff0c;视频采集和分析&#xff0c;包括人脸检测和物体检测等功能。 数字图像在计算机中是以矩阵形式存储的&#xff0c;矩阵中的每一个元素都描述一定的图像…

学生个性化成长平台搭建随笔记

1.Vue的自定义指令 在 Vue.js 中&#xff0c;我们可以通过 Vue.directive() 方法来定义自定义指令。具体来说&#xff0c;我们需要传递两个参数&#xff1a; 指令名称&#xff1a;表示我们要定义的指令名称&#xff0c;可以是一个字符串值&#xff0c;例如&#xff1a;has-rol…