跟着Datawhale重学数据结构与算法(3)---排序算法

开源链接:【 教程地址 】【电子网站】
【写博客的目的是记录自己学习过程,方便自己复盘,专业课复习】

数组排序:

数组排序
冒泡排序
选择排序
插入排序
归并排序
希尔排序
快速排序
堆排序
计数排序
桶排序
基数排序

1.冒泡排序

1.1 算法思想
其实就跟线性代数里面的求逆序数的思想一样,ε=(1234),数当前数字与前面数字相比,有几个比它大的,然后加起来就是交换次数。
ε=(2143)=0+1+0+1=2,那么要成从小打到排列就要交换2次。
冒泡排序的本质也是两两交换,再用上面举例子,最坏的情况是ε=(4321)=0+1+2+3=6,我得交换6次。
step1:把最大的元素通过交换到正确的位置,我需要交换三次。
step2:把第二大元素通过俩俩交换到正确的位置,我需要交换俩次。
......
last step:最后剩的那个元素就是最小元素,无需排序,因此就完成了排序。
你可以推广到n个元素的排序。

可见,如果我们要用程序实现这个算法,就要使用双层循环,内层循环用来保证每次将一个元素放到对的位置,外层循环用来遍历整个数组。

1.2代码实现:
/* 冒泡排序 */
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}
void bubbleSort(vector<int> &nums) {
    for (int i = nums.size() - 1; i > 0; i--) {
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
            }
        }
    }
}
1.3 优化代码

我们想这样一个问题,假如还是上面那个逆序数,如果这个数组他没有排好,那么他一定存在一个元素会发生交换操作,那反过来说,(逆否命题也成立),如果元素没有发生交换,那么说明这个数组元素是排列好了的。因此,我们就可以用一个bool变量放在内层交换函数后面,如果发生了交换,那么继续,如果没发生交换,那么直接退出for循环。

/* 冒泡排序 */
void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}
void bubbleSortWithFlag(vector<int> &nums) {
    for (int i = nums.size() - 1; i > 0; i--) {
        bool flag = false; // 初始化标志位
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
                flag = true;
		}
        }
        if (!flag)
            break; // 此轮“冒泡”未交换任何元素,直接跳出
    }
}

要掌握的一些排序算法的复杂度:
在这里插入图片描述

2.选择排序

2.1 算法思想
就是分为两个状态空间,一个是未排序的状态空间,一个是已经排序好的状态空间,原理也非常简单,就是
每轮从未排序的区间选择最小的元素放在排序好的空间的末尾。,直到未排序好的状态空间仅剩一个元素肯定为数组的最大值。
2.2 代码实现
void selectionSort(vector<int> &nums) {
    int n = nums.size();
    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;
            }
        }
        if (minIndex != i) {
            swap(nums[i], nums[minIndex]);
        }
    }
}

3.插入排序

3.1 算法思想
其实插入排序跟我们打扑克的时候整理扑克的时候很像,所以他的算法流程其实也很简单。
step1:先假设我们揭到的第一张牌为基准。
step2:从第二张牌开始,依次与基准进行比较将他插入到合适的位置,这样前两个元素就排好序了。
以此类推.......
依次将当前元素插入到已经排序好的子数组中。

插入排序的核心思想是不断地将当前元素插入到已经排序好的子数组中,以保持子数组的有序性。

3.2 代码实现:
void insertionSort(vector<int> &nums) {
    int n = nums.size();
    for (int i = 1; i < n; i++) {
        int key = nums[i]; // 当前待插入的元素
        int j = i - 1;//排序好的子序列个数

        // 将当前元素插入到已经排序好的子数组中的合适位置
        // 从后往前进行比较,不符合条件就插入到此位置中。
        while (j >= 0 && nums[j] > key) {
            nums[j + 1] = nums[j]; // 将比当前元素大的元素往后移动一个位置
            j--;
        }
        nums[j + 1] = key; // 插入当前元素到合适的位置
    }
}

4.归并排序

4.1 算法思想
分割数组:将待排序数组递归地分割成长度大约相等的两个子数组,直到每个子数组只包含一个元素为止。
合并数组:将两个有序的子数组合并成一个有序的数组。合并过程中,逐个比较两个子数组的元素,并将较小的元素放入临时数组中,直到两个子数组都被合并完毕。
重复步骤 1 和步骤 2:对分割后的子数组进行递归排序,并合并已经排序好的子数组,直到所有子数组都合并成一个完整的有序数组。

归并排序的时间复杂度为 O(n log n),其中 n 是待排序数组的大小。它的优点是稳定性好、适用于大型数据集和链表等数据结构。
我感觉这个方法很amazing。用到了递归的思想,分而治之,再合并。跟深度学习中的深度可分离卷积(Depthwise Convolution)或者说shuffleNet很像,其实就是分别卷积,然后再使用1x1卷积共享参数,保证信息的交流。算法是互通的,对于归并排序来说,先拆分着进行排序,然后再合并起来进行排序。
在这里插入图片描述

4.2 代码实现
// 合并两个有序数组
void merge(vector<int> &nums, int left, int mid, int right) {
    int n1 = mid - left + 1; // 左侧子数组的长度
    int n2 = right - mid; // 右侧子数组的长度
    // 创建临时数组用于存放合并后的结果
    vector<int> temp(n1 + n2);
    // 将左右两个子数组合并到临时数组中
    int i = left, j = mid + 1, k = 0;
    while (i <= mid && j <= right) {
        if (nums[i] <= nums[j]) {
            temp[k++] = nums[i++];
        } else {
            temp[k++] = nums[j++];
        }
    }

    // 将剩余的元素复制到临时数组中
    while (i <= mid) {
        temp[k++] = nums[i++];
    }
    while (j <= right) {
        temp[k++] = nums[j++];
    }

    // 将临时数组的元素复制回原数组中
    for (int p = 0; p < k; p++) {
        nums[left + p] = temp[p];
    }
}

// 归并排序
void mergeSort(vector<int> &nums, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(nums, left, mid); // 对左侧子数组进行排序
        mergeSort(nums, mid + 1, right); // 对右侧子数组进行排序
        merge(nums, left, mid, right); // 合并两个有序子数组
    }
}

5.希尔排序

5.1 算法思想

希尔排序是一种改进的插入排序算法,也称为缩小增量排序。它的基本思想是将待排序数组按一定步长进行分组,对每组进行插入排序,然后逐步减小步长,重复上述步骤,直到步长为 1,此时数组基本有序,最后再进行一次插入排序。

希尔排序的关键在于选择合适的步长序列,也就是元素间隔数gap,不同的步长序列会影响排序的效率。通常使用的步长序列有希尔原始提出的序列(逐步除以 2)、Hibbard 序列、Sedgewick 序列等。

我感觉哈其实有种层次聚类的意思,通过逐步减小步长来逐渐减小逆序对,来使得数组变得有序,来减少元素的移动次数。我记得层次聚类有种方法就是从大到小每次选择一个阈值进行聚类,逐渐减小阈值,直到所有样本都得到合理的分类即可。

5.2 代码实现
class Solution {
public:
    std::vector<int> shellSort(std::vector<int>& nums) {
        int size = nums.size();
        int gap = size / 2;

        while (gap > 0) {
            for (int i = gap; i < size; i++) {
                int temp = nums[i];
                int j = i;
                while (j >= gap && nums[j - gap] > temp) {
                    nums[j] = nums[j - gap];
                    j -= gap;
                }
                nums[j] = temp;
            }
            gap /= 2;
        }
        return nums;
    }

    std::vector<int> sortArray(std::vector<int>& nums) {
        return shellSort(nums);
    }
};

6.快速排序

6.1 算法思想
快速排序并不快,它使用分治策略来对一个数组进行排序。快速排序的基本思想是选择一个元素作为基准(pivot),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据要小,(左边比基准都小,右边比基准都大),然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。(由大到小)。

在这里插入图片描述

6.2 代码实现:
void quickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        // 选取基准元素为左端点
        int pivot = arr[low];
        int i = low;
        // 将小于基准的元素移动到分割点左侧,大于基准的元素移动到右侧
        for (int j = low + 1; j <= high; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr[i], arr[j]);
            }
        }
        // 基准元素交换到中间
        swap(arr[low], arr[i]);
        // 递归排序分割点左侧和右侧的子数组
        quickSort(arr, low, i - 1);
        quickSort(arr, i + 1, high);
    }
}

7.堆排序

7.1 算法理解

堆得结构分为大根堆和小根堆,是一个完全二叉树(从形式上讲它是个缺失的的三角形,但所缺失的部分一定是右下角某个连续的部
分,最后那一行可能不是完整的)。大小根堆从字面意思上就可以理解为树从根节点向下排列的顺序。
在这里插入图片描述在这里插入图片描述
采用顺序结构(数组)的形式来表示完全二,能够chong分利用存储空间。

7.2 代码实现
//使用数组来存储大顶堆(左孩子和哟孩子都小于父节点)
//维护堆的性质:如果父节点不满足大顶堆的性质,那就将孩子的最大值与父节点进行交换
//这样就满足了大顶堆的性质,复杂度O(logn)
建堆:(变成大顶堆),复杂度O(n)

//SO:首先将数列变成大顶堆的形式,然后自下而上,从底部与根节点进行交换,交换后底部元素
//从树中删除,删除完后放在数列的最右端,更新此时的二叉树和数组,然后继续将第二个元素与根节点
//元素进行交换,继续....直到树只剩一个元素,操作完成。
void heapify(int arr[], int n, int i){
/*数组,数组长度,待维护节点下标*/
int largest = i;
int lson = i*2 +1;
int rson = i*2 +2;

if(lson < n && arr[largest] < arr[lson])
	largest = lson;
if(rson < n && arr[largest] < arr[rson])
	largest = rson;
if (largest !=i)
{
	swap(&arr[largest],&arr[i]);
	heapify(arr,n,largest);
}
}
//堆排序入口
void heap_sort(int arr[], int n){
	int i;
	//建堆
	for(i=n/2-1;i>=0;i--)
		heapify(arr,n,i);
	//排序
	for(i=n-1;i>0;i--)
	{
		swap(&arr[i],&arr[0]);
		heapify(arr,i,0);
	}
}

稳定性:不稳定

8. 计数排序

8.1算法原理

通过计数而不是比较来进行排序,适用于范围比较小的整数序列
原始数组->计数数组->累计数组->最终结果
在这里插入图片描述

8.2 代码实现:
void counting_sort(int arr[], int len)
{
    if (len < 1) return;

    // 寻找最大的元素
    int max = arr[0];
    for (size_t i = 1; i < len; i++)
        if (arr[i] > max) max = arr[i];

    // 分配一个长度为max+1的数组存储计数,并初始化为0
    int *count = (int *)malloc(sizeof(int) * (max + 1));
    memset(count, 0, sizeof(int) * (max + 1));

    // 计数
    for (size_t i = 0; i < len; i++)
        count[arr[i]]++;

    // 统计计数的累计值
    for (size_t i = 1; i < max + 1; i++)
        count[i] += count[i - 1];

    // 创建一个临时数组保存结果
    int *output = (int *)malloc(sizeof(int) * len);

    // 将元素放到正确的位置上
    for (size_t i = 0; i < len; i++)
    {
        output[count[arr[i]] - 1] = arr[i];
        count[arr[i]]--;
    }

    // 将结果复制回原数组
    for (size_t i = 0; i < len; i++)
        arr[i] = output[i];
}

9.桶排序

9.1 算法原理

将待排序的元素分到有限数量的桶中,每个桶再分别进行排序,最后将各个桶中的元素合并得到排序结果。桶排序的核心思想是将一组数据分割成一定数量的桶,然后对每个桶中的数据进行排序,最后将所有桶中的数据按照顺序合并起来。首先,初始化一个足够数量的空桶。然后遍历待排序的数组,将每个元素放入相应的桶中;对每个非空的桶中的元素进行排序(可以使用其他排序算法,如插入排序);按照桶的顺序将所有非空桶中的元素合并得到排序结果。

9.2 代码实现
// 桶排序函数
void bucketSort(vector<float>& arr) {
    int n = arr.size();
    // 创建足够数量的空桶
    vector<vector<float>> buckets(n);

    // 将每个元素放入相应的桶中
    for (int i = 0; i < n; ++i) {
        int bucket_index = n * arr[i];
        buckets[bucket_index].push_back(arr[i]);
    }

    // 对每个非空桶中的元素进行排序(这里使用插入排序)
    for (int i = 0; i < n; ++i) {
        sort(buckets[i].begin(), buckets[i].end());
    }

    // 合并所有非空桶中的元素得到排序结果
    int index = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < buckets[i].size(); ++j) {
            arr[index++] = buckets[i][j];
        }
    }
}

10.基数排序

10.1 算法思想

基数排序是一种非比较性的排序算法,它按照数字的位数来进行排序。基数排序可以分为 LSD最低位优先(Least Significant Digit)和 MSD最高位优先(Most Significant Digit)两种实现方式。下面我们来介绍 LSD 基数排序的算法思想和实现。

LSD 基数排序的基本思想是从数字的最低位(个位)开始,依次对数字进行排序,直到最高位(最高位数的位数)。在每一轮排序过程中,对所有数字根据当前位上的数字进行桶排序。通过这样的逐位排序过程,最终可以得到排好序的结果。
这个方法跟人类很像,我们通常来比较两个事物所有的特征(差异)来区分,LSD这个就相当于先从个位进行排序,个位排完后,在向上观察最大的特征差异,然后到第一位的比较。

10.2 代码实现
//代码也非常简单
void radixSort(vector<int>& arr) {
    // 获取数组中的最大值
    int max_num = *max_element(arr.begin(), arr.end());

    // LSD 基数排序
    for (int digit = 1; max_num / digit > 0; digit *= 10) {
        // 创建 10 个桶,用于存放当前位上的数字
        vector<vector<int>> buckets(10);

        // 将数组中的元素按照当前位上的数字放入相应的桶中
        for (int num : arr) {
            int digit_value = getDigit(num, digit);
            buckets[digit_value].push_back(num);
        }

        // 从桶中按顺序取出元素,替换原数组中的元素
        int index = 0;
        for (auto& bucket : buckets) {
        // auto 是为了让编译器自动推导出 bucket 的类型
            for (int num : bucket) {
                arr[index++] = num;
            }
            bucket.clear();
        }
    }
}

Leetcode之旅~:

【1】LCR.164破解闯关密码

直接偷懒,写到这累的不行了,我直接sort(快速排序)+拼接字符串。

class Solution {
public:
    // 自定义比较函数,用于确定两个数字拼接后的结果的大小
    static bool compare(const int& x, const int& y) {
        string xy = to_string(x) + to_string(y);
        string yx = to_string(y) + to_string(x);
        return xy < yx;
    }

    string crackPassword(vector<int>& password) {
        // 将数组按照自定义的比较函数排序
        sort(password.begin(), password.end(), compare);
        
        // 将排序后的数组拼接成字符串
        string result = "";
        for (int num : password) {
            result += to_string(num);
        }
        return result;
    }
};

【2】移动0

直接双指针遍历,移动非零元素,然后遍历完后,直接left后面全部赋0.

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int left = 0; // left 指针
        int n = nums.size();
        
        // 遍历数组
        for (int right = 0; right < n; right++) {
            if (nums[right] != 0) {
                // 将非零元素放到 left 指针位置
                nums[left++] = nums[right];
            }
        }
        
        // 将 left 指针后面的所有元素置为 0
        while (left < n) {
            nums[left++] = 0;
        }
    }
};

【3】[数组排序](https://leetcode.cn/problems/sort-an-array/)

直接用前面冒泡排序的代码-》超出时间限制emmmm
快速排序也超出时间限制了,这怎么搞,我想想,数组太大了,那就小规模用插入排序更快一些,大数组用快速。

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        quickSort(nums, 0, nums.size() - 1);
        return nums;
    }
    
    // 快速排序函数
    void quickSort(vector<int>& nums, int low, int high) {
        if (low < high) {
            // 对小规模数组(<=10)使用插入排序
            if (high - low + 1 <= 10) {
                insertionSort(nums, low, high);
                return;
            }
            
            // 随机选择基准元素
            int pivotIndex = rand() % (high - low + 1) + low;
            swap(nums[pivotIndex], nums[high]);
            pivotIndex = partition(nums, low, high);
            quickSort(nums, low, pivotIndex - 1);
            quickSort(nums, pivotIndex + 1, high);
        }
    }
    
    // 划分函数,返回分割点的位置
    int partition(vector<int>& nums, int low, int high) {
        int pivot = nums[high]; // 基准元素
        int i = low - 1; // 定义分割点的位置

        for (int j = low; j < high; j++) {
            if (nums[j] < pivot) {
                i++;
                swap(nums[i], nums[j]);
            }
        }
        
        swap(nums[i + 1], nums[high]); // 将基准元素放到正确的位置
        return i + 1;
    }
    
    // 插入排序函数
    void insertionSort(vector<int>& nums, int low, int high) {
        for (int i = low + 1; i <= high; i++) {
            int key = nums[i];
            int j = i - 1;
            while (j >= low && nums[j] > key) {
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = key;
        }
    }
};

通过了,累了,不敲了

参考资料:

[1] 【教程地址 】[电子网站]
[2] Hello 算法教程
[3] https://blog.csdn.net/qq_39181839/article/details/109478094
[4] https://blog.csdn.net/u010452388/article/details/81283998

感谢:DataWhale社区

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

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

相关文章

【java】equals()方法

什么是Object类 Object类是所有类的始祖&#xff0c;在Java中每个类都是由它扩展而来的。 equals()方法 在 Object 类当中&#xff0c;equals()的底层是“”&#xff0c;String 类将其重写。 和equals() 有什么区别 对于基本数据类型而言&#xff0c;比较值是否相同&#x…

污水处理设备运维注意事项有哪些

污水处理设备的运维是确保污水处理效率和处理质量的关键环节。良好的运维不仅可以延长设备的使用寿命&#xff0c;还能确保污水处理过程的稳定性和可靠性。以下是一些污水处理设备运维的重要注意事项&#xff1a; 1. 定期检查和维护 设备检查&#xff1a;定期对污水处理设备进…

FFmpeg下载教程(Windows版)

文章目录 下载地址步骤 下载地址 https://ffmpeg.org/download.html 步骤

LeetCode53. 最大子数组和

LeetCode53. 最大子数组和 解题思路dp 代码 /* 数组长度n 9,连续的区间 那区间长度为1的区间数量是&#xff0c;9个 区间长度为2的区间数量是8个 区间长度为3的连续的区间数量为7个 .... 区间长度为9的区间数量为1个 */ class Solution { public:int maxSubArray(vector<…

文献速递:肺癌早期诊断---早期肺癌诊断:基于深度学习的循环外泌体光谱分析

Title 题目 Early-Stage Lung Cancer Diagnosis by Deep Learning-Based Spectroscopic Analysis of Circulating Exosomes 早期肺癌诊断&#xff1a;基于深度学习的循环外泌体光谱分析 Abstract 摘要 Lung cancer has a high mortality rate, but an early diagnosis can …

【极速前进】20240422:预训练RHO-1、合成数据CodecLM、网页到HTML数据集、MLLM消融实验MM1、Branch-Train-Mix

一、RHO-1&#xff1a;不是所有的token都是必须的 论文地址&#xff1a;https://arxiv.org/pdf/2404.07965.pdf 1. 不是所有token均相等&#xff1a;token损失值的训练动态。 ​ 使用来自OpenWebMath的15B token来持续预训练Tinyllama-1B&#xff0c;每1B token保存一个che…

【STM32+HAL+Proteus】系列学习教程---ADC(查询、中断、DMA模式下的电压采集)

实现目标 1、学会STM32CubeMX软件关于ADC的配置 2、掌握ADC三种模式&#xff08;查询、中断、DMA&#xff09;编程 3、具体目标&#xff1a;1、将开发板单片机采集到的电压值上传至上位机串口调试助手显示。 一、ADC 概述 1、什么是ADC? ADC&#xff08;Analog to Digit…

MyBatis入门学习二(配置文件、mapper文件、动态SQL)

目录 1、映射配置文件 1.1 properties 1.2 settings 1.3 typeAliases 1.4 typeHandlers 1.5 plugins 1.5 environments 1.6 mappers 2、映射Mapper文件 2.1 CRUD 2.1.1 select 2.1.2 Insert 2.1.3 update 2.1.4 delete 2.1.5 parameterType传入多个参数 2.1.5 …

图像在神经网络中的预处理与后处理的原理和作用(最详细版本)

1. 问题引出及内容介绍 相信大家在学习与图像任务相关的神经网络时&#xff0c;经常会见到这样一个预处理方式。 self.to_tensor_norm transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 具体原理及作用稍后解释&…

2024年vue 开发环境 Node.js于win10环境下的安装

2024年vue 开发环境 Node.js于win10环境下的安装 导航 文章目录 2024年vue 开发环境 Node.js于win10环境下的安装导航一、下载node.js二、安装node.js三、测试(一)四、环境配置五、测试(二)六、安装淘宝镜像七、安装vue脚手架 一、下载node.js Node.js 官方网站下载&#xff…

如何利用交易形态的失败进行现货黄金?

进行现货黄金理财&#xff0c;除了需要投资者对黄金投资有热情之外&#xff0c;有方法也是很重要的&#xff0c;光有热情而没有技术&#xff0c;我们的资金很可能会成为其他人的囊中之物。但如果有了现货黄金理财的技术&#xff0c;情况就可能扭转过来。下面我们就从买入的角度…

关于豆瓣电影数据抓取以及可视化

首先我们可以先了解以下网络爬虫的定义&#xff1a; 爬虫是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。它可以在互联网上自动抓取网页内容&#xff0c;将这些信息存储起来。爬虫可以抓取网站的所有网页&#xff0c;从而获取对于我们有价值的信…

BGP的路径属性

路径属性 l每条BGP路由都拥有多个的路径属性&#xff0c;有些是必须携带的&#xff0c;有些是可选添加的 lBGP的路径属性将影响最优路由的选择 lBGP路径属性是描述路由的一组参数&#xff0c;BGP根据路由的属性选择最佳路由&#xff0c;可以人为置值&#xff0c;以便执行路由…

【AI】Deepstream入门(2)Ubuntu20.04安装Deepstream

1、安装GPU驱动 本人显卡型号:RTX4060 Laptop(笔记本专用显卡) 【AI】惠普暗夜精灵9安装Ubuntu20.04+nvidia驱动 2、安装cuda、cuDNN 【AI】Ubuntu20.04安装cuda、cuDNN 3、安装TensorRT 1)下载 下载地址:https://docs.nvidia.com/deeplearning/tensorrt/archives/i…

vue报错:Do not mutate vuex store state outside mutation handlers.

vue报错&#xff1a;Do not mutate vuex store state outside mutation handlers. 原因&#xff1a;在vuex store的state外部直接修改了state的值&#xff0c;但是Vuex要求所有的state的修改必须在vuex中&#xff0c;不允许直接咋组件中修改store中的状态&#xff0c;除非通过m…

FPM 快速报表开发

背景&#xff1a; 使用FPM开发报表时&#xff0c;如果报表字段过多&#xff0c;页面拖拽等操作不方便 报表数量过多时&#xff0c;新建应用操作步骤较为繁琐 更习惯通过少量代码而非页面操作去实现功能 处理&#xff1a; 将FPM报表开发简化为类似GUI端ALV的开发过程:&#xff…

Spring Boot | Spring Boot “自定义“ Redis缓存 “序列化机制“

目录: Spring Boot "自定义" Redis缓存 "序列化机制" &#xff1a;一、基于 "注解" 的 "Redis缓存管理" 的 "默认序列化机制" 和 "自定义序列化机制"1.1 基于 "注解" 的 "Redis缓存管理" 的 …

基于OpenCV的人脸签到系统

效果图 目录文件 camerathread.h 功能实现全写在.h里了 class CameraThread : public QThread {Q_OBJECT public:CameraThread(){//打开序号为0的摄像头m_cap.open(0);if (!m_cap.isOpened()) {qDebug() << "Error: Cannot open camera";}//判断是否有文件,人脸…

Unity 实现原神中的元素反应

一、元素反应 原神中共有七种元素&#xff0c;分别是水、火、冰、岩、风、雷、草。这七种元素能互相作用 Demo下载&#xff1a;Download 元素反应表格图示&#xff0c;可能不够精准 /火水雷冰草岩风绽放原激化火/蒸发超载融化燃烧结晶扩散烈绽放/水蒸发/感电冻结/碎冰绽放结晶…

数据分析:甲基化分析-从DNA methylation的IDAT文件到CpG site的Beta values

介绍 DNA Methylation和疾病的发生发展存在密切相关&#xff0c;它一般通过CH3替换碱基5‘碳的H原子&#xff0c;进而调控基因的转录。常用的DNA methylation是Illumina Infinium methylation arrays&#xff0c;该芯片有450K和850K&#xff08;也即是EPIC&#xff09;。 该脚…