【数据结构】七大排序算法详解

目录

♫什么是排序

♪排序的概念

♪排序的稳定性

♪排序的分类

♪常见的排序算法

♫直接插入排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫希尔排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫直接选择排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫堆排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫冒泡排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫快速排序

♪基本思想

♪算法实现

♪算法的优化

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫归并排序

♪基本思想

♪算法实现

♪算法的稳定性

♪时间复杂度

♪空间复杂度

♫排序算法的比较


♫什么是排序

♪排序的概念

排序是将一组数据,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

♪排序的稳定性

将一组数据进行排序后,如果原数据与排序后的数据中相同元素的相对位置仍然保持不变,那么该排序就是一个稳定的排序,否则就不是一个稳定的排序。

注:一个本身稳定的排序可以通过不稳定的算法实现,而一个不稳定的排序是不能通过稳定的算法实现的。

♪排序的分类

排序分为内部排序和外部排序两种:

1. 内部排序:数据元素全部放在内存中的排序。
2. 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序

♪常见的排序算法

常见的排序算法有插入排序(直接插入排序、希尔排序),选择排序(直接选择排序、堆排序),交换排序(冒泡排序、快速排序),归并排序等,这些排序算法也是我们接下来(以从小到大排序为例)重点要了解的东西。

♫直接插入排序

♪基本思想

直接插入排序是一种简单的插入排序算法,它的基本思想是:从第二个元素开始,每个元素都往前找合适的位置插入,直到所有的记录插入完为止,就能得到一个有序序列

♪算法实现

//直接插入排序(从小到大)public void insertSort(int[] arr) {//空数组,不用排if (arr == null) {return;}//从第二个数开始到最后一个数for (int i = 1; i < arr.length; i++) {//将待插入的数拿出来int tmp = arr[i];int j = 0;//从后往前找合适的插入位置(待插入值比j位置的值大的位置的后面)for (j = i - 1; j >= 0; j--) {//判断待插入值是否比j位置的值小if (tmp < arr[j]) {//小,位置不对,将该位置的值往后挪,继续找arr[j + 1] = arr[j];} else {//大,找到合适位置,退出循环arr[j + 1] = tmp;break;}}//当待插入值最小时,将待插入值插入到第一个位置;当找到合适位置,将待插入值插入合适位置arr[j + 1] = tmp;}}

♪算法稳定性

当相邻的元素比较大小时,若相等,直接插入排序不会改变它们的位置,因此保证了排序前后相等元素的相对位置不变,故直接插入排序是稳定的排序。

♪时间复杂度

最好情况(排序一组有序序列):对n-1个元素进行插入操作,由于每个元素都不需要移动,故时间复杂度为O(n)
最坏情况(排序一组逆序序列):对n-1个元素进行插入操作,每个元素对应的移动次数依次为:1、2、3、....、n-1,等差数列求和得(n-1)(1+n-1)/2=n(n-1)/2=(n^2-n)/2,故算法时间复杂度为O(n^2)

注:数组越稳定,使用直接插入排序时效率越高

♪空间复杂度

只需要一个额外的空间存放待插入值,故空间复杂度为O(1)

♫希尔排序

♪基本思想

我们知道序列越有序,直接插入排序的效率越高,而希尔排序就是先通过局部的直接插入排序将序列变得相对有序后再进行整体的直接插入排序的排序算法,希尔排序也叫作缩小增量排序。希尔排序法的基本思想是:① 先取gap作为第一个增量,把序列根据gap分组。② 所有距离为gap的倍数的记录放在同一个组中,在各组内进行直接插入排序。③ 取第二个缩小的增量gap2,重复上述的分组和排序,直至所取的增量gap=1,即所有记录放在同一组中进行直接插入排序为止。

以gap为元素个数的一半,每次gap的值缩小一半为例:

gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样整体的直接插入排序就会很快。

♪算法实现

这里依然以gap为元素个数的一半,每次gap的值缩小一半为例:

    //希尔排序(从小到大)public void shellSort(int[] arr) {//可数组,不用排if (arr == null) {return;}//增量的大小取数组元素的个数的一半int gap = arr.length / 2;//增量每次缩小一半,直至为减为1for (; gap > 0; gap /= 2) {//对数组进行指定增量的直接插入排序shell(arr, gap);}}//指定增量的直接插入排序public void shell(int[] arr, int gap) {//从第一组的第二个元素开始直到整个数组的最后一个元素for (int i = gap; i < arr.length; i++) {//取出待插入的元素int tmp = arr[i];int j = 0;//从每组待插入元素的位置到该组的第一个元素for (j = i - gap; j >= 0; j -= gap) {//判断待插入值是否比j位置的值小if (tmp < arr[j]) {//小,位置不对,将该位置的值往后挪,继续找arr[j + gap] = arr[j];} else {//大,找到合适位置,退出循环arr[j + gap] = tmp;break;}}//当待插入值最小时,将待插入值插入到第一个位置;当找到合适位置,将待插入值插入合适位置arr[j + gap] = tmp;}}

♪算法稳定性

因为希尔排序的是缩小增量的插入排序(无法保证每次分组相同元素都在同一组),故希尔排序是不稳定的排序。

♪时间复杂度

希尔排序的时间复杂度根据gap取法的不同而不同,上述取法的时间复杂度约为O(n^1.25)~O(1.6*n^1.25)之间,其中n为数组中元素的个数。

♪空间复杂度

希尔排序的空间复杂度和直接插入排序一样都是O(1)

♫直接选择排序

♪基本思想

直接选择排序的基本思想是:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

♪算法实现

    //选择排序(从小到大)public void selectSort(int[] arr) {//空数组,不用排if (arr == null) {return;}//从第一个元素开始到倒二个元素for (int i = 0; i < arr.length-1; i++) {//记录i及其i后面元素的最小值int minIndex = i;//找出i及其后面最小元素的下标for (int j = i + 1; j < arr.length; j++) {if (arr[minIndex] > arr[j]) {minIndex = j;}}//将最小元素的位置移到i位置if (i != minIndex) {int tmp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = tmp;}}}

♪算法稳定性

在选择排序中,如果数组中有两个相等的元素,在选择排序的过程中,可能会先选中后面的元素,导致它们的原始位置交换,这样就破坏了它们在原数组中的相对顺序,故直接选择排序是不稳定的排序。

♪时间复杂度

对n-1个元素进行操作,每个元素对应的比较次数依次为:n-1,n-2,n-3,...,1,等差数列求和得(n-1)(1+n-1)/2=n(n-1)/2=(n^2-n)/2,故算法时间复杂度为O(n^2)

♪空间复杂度

直接选择排序只需要一个额外的常数级别的空间来交换元素,因此直接选择排序的空间复杂度为O(1)

♫堆排序

♪基本思想

堆排序是指利用堆这种数据结构所设计的一种排序算法,它是通过堆来进行选择数据(排升序要建大堆,排降序建小堆),是选择排序的一种。堆排序的基本思想是:

①.将待排序序列构造成一个大/小根堆。②.将其与末尾元素进行交换, 此时末尾就为最大值。③.然后将剩余的个元素重新调整成一个大/小根堆,反复执行上述操作, 便能得到一个有序序列了。

♪算法实现

    //堆排序(从小到大)public void heapSort(int[] arr) {//空数组,不用排if (arr == null) {return;}//创建大根堆createBigHeap(arr);int end = arr.length - 1;while (end > 0) {//将堆顶元素和堆的最后一个元素交换int tmp = arr[0];arr[0] = arr[end];arr[end] = tmp;//将剩余元素重新调整为大根堆shiftDown(arr, 0, end);//堆的个数减一end--;}}//创建大根堆private void createBigHeap(int[] arr) {//从最右边的叶子节点开始for (int parent = (arr.length - 1 - 1) / 2; parent >= 0; parent--) {//向下调整shiftDown(arr, parent, arr.length);}}//向下调整private void shiftDown(int[] arr, int parent, int len) {//child为parent的左孩子(右孩子可能不存在)int child = 2 * parent + 1;while (child < len) {//child为parent的左右孩子节点中的最小节点if (child + 1 < len && arr[child] < arr[child + 1]) {child = child + 1;}//大根堆:parent节点应该是parent节点和child节点中最大的那个节点if (arr[parent] < arr[child]) {int tmp = 0;arr[parent] = tmp;arr[parent] = arr[child];arr[child] = tmp;//继续向下调整parent = child;child = 2 * parent + 1;} else {//已经是大根堆了,退出循环break;}}}

♪算法稳定性

因为在调整堆的过程中,会进行多次元素的交换,这样可能会打破原本相等元素之间的相对位置关系,故堆排序是不稳定的排序。

♪时间复杂度

初始化堆的时间复杂度为O(n),删除操作的时间复杂度为O(logn),每删除一个元素,总元素减一,n-1次的删除操作的时间应该为:log(n)+log(n-1)+…+log(3)+log(2) = log(n!),
又因(n/2)^(n/2) ≤ n ≤ n^n,即 1/4*nlog(n) ≤ n! ≤ nlogn,去掉常数后时间复杂度为O(nlogn),故堆排序的时间复杂度为O(nlogn)

♪空间复杂度

堆排序是一种原地排序算法,所有操作都在原始的数组中进行,故堆排序的空间复杂度为O(1)

♫冒泡排序

♪基本思想

冒泡排序可以看成冒泡的过程,每次排序都会让一个“泡泡”(指一个元素)浮到数组的正确位置。冒泡排序的基本思想是:①从数组的第一个元素开始,比较相邻的两个元素。②如果相邻的两个元素顺序不符合要求(比如升序排序时,前一个数大于后一个数),则交换这两个元素的位置。③继续比较下一个相邻的两个元素,直到数组的末尾。④重复以上步骤,每次比较时都从数组的第一个元素开始,直到数组完全有序为止。

♪算法实现

    //冒泡排序(从小到大)public void bubbleSort(int[] arr) {//空数组,不用排if (arr == null) {return;}//总共需要对对n-1个元素进行比较for (int i = 0; i < arr.length - 1; i++) {boolean flg = false;//每个元素和n-1-i个元素进行比较(i为序列后面已经有序的元素)for (int j = 0; j < arr.length - 1 - i; j++) {//比较相邻两个元素,大的往后移if (arr[j] < arr[j + 1]) {int tmp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = tmp;//进行过交换操作,当前序列仍未有序flg = true;}}//不需要进行交换操作,当前序列已经有序了if (flg == false) {break;}}}

♪算法稳定性

在冒泡排序中只有前后元素比较大小并进行交换,而相同元素之间是不会交换的,故冒泡排序是一种稳定的排序。

♪时间复杂度

在最坏的情况下,即序列本来就是逆序的情况下,需要进行n-1次遍历,每次遍历需要比较和交换n-i次,因此总的比较和交换次数是n(n-1)/2,故冒泡排序的时间复杂度是O(n^2)

♪空间复杂度

因为冒泡排序只需要一个常量级别的额外空间来交换相邻的元素,故冒泡排序的空间复杂度为O(1)

♫快速排序

♪基本思想

快速排序(Quicksort)是一种高效的排序算法,基于分治的思想,通过不断地将待排序序列划分为独立的两部分,然后对每一部分递归地进行快速排序,最终得到有序序列。 快速排序的基本思想是: 任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

♪算法实现

    //快速排序(从小到大)public void quickSort(int[] arr) {//空数组,不用排if (arr == null) {return;}//对整个数组进行快速排序quick(arr, 0, arr.length - 1);}private void quick(int[] arr, int start, int end) {//递归结束条件if (end <= start) {return;}//获取基准数的位置int pivot = partition(arr, start, end);//对基准数的左边进行快速排序quick(arr, start, pivot - 1);//对基准数的右边进行快速排序quick(arr, pivot + 1, end);}
其中找基准数的代码根据基准值划分区间的方式不同,算法的实现也就不同,常见的划分区间的方式有:
♩. Hoare法
Hoare法的具体步骤如下:

①.首先选择一个pivot元素,将待排序数组分成左右两部分;

②.然后将左右两部分分别用一个指针来遍历数组;

③.左指针指向左部分的第一个元素,右指针指向右部分的最后一个元素;

④.左指针向右移动,直到找到第一个大于等于pivot的元素;

⑤.右指针向左移动,直到找到第一个小于等于pivot的元素;

⑥.如果左指针和右指针还没有相遇,则将它们所指的元素交换;

⑦.重复步骤4-6,直到左指针和右指针相遇;

⑧.最后将pivot元素放到相遇点的位置上,使得左部分的元素都小于等于pivot,右部分的元素都大于等于pivot;

⑨.递归地对左右两部分进行排序,直到整个数组有序。

代码实现:
    //Hoare法找基准数private int partition(int[] arr, int left, int right) {int n = left;//初始基准int pivot = arr[left];while (left < right) {//从右边开始找比基准数大的while (left < right && arr[right] >= pivot) {right--;}//从左边找比基准数小的while (left < right && arr[left] <= pivot) {left++;}//左边小的和右边打的交换int tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;}//left和right相遇了,将left和初始基准数交换int tmp = arr[n];arr[n] = arr[left];arr[left] = tmp;//返回新基准数:leftreturn left;}
注:只能先从右边找比基准数大的数,如果先从左边找比基准数小的数的话最后和基准数交换的时候会把比基准数大的数换到基准数前面。
♩挖坑法
挖坑法的具体步骤如下:

①.选定一个基准元素,通常选择第一个元素或最后一个元素。

②.将序列分为两部分,一部分为小于基准元素的部分,一部分为大于等于基准元素的部分。

③.在小于基准元素的部分中,从右到左寻找第一个大于等于基准元素的元素,将该元素赋值到基准元素的位置。

④.在大于等于基准元素的部分中,从左到右寻找第一个小于基准元素的元素,将该元素赋值到第3步所挖的坑中。

⑤.重复3、4步,直到左右两部分扫描相遇,然后将基准元素放回该位置。

⑥.递归地对左右两部分进行快速排序

代码实现:

//挖坑法找基准数
private int partition(int[] arr, int left, int right) {//记录初始基准数int pivot = arr[left];while (left < right) {//从右边开始找比基准数大的while (left < right && arr[right] >= pivot) {right--;}//将右边大的放到left位置arr[left] = arr[right];//从右边开始找比基准数大的while (left < right && arr[left] <= pivot) {left++;}//将左边小的放到left位置arr[right] = arr[left];}//left和right相遇,将初始基准数放到left位置arr[left] = pivot;//返回新基准数:leftreturn left;
}
♩前后指针法
前后指针法的步骤为:

①.选取一个基准数,一般选择第一个数或最后一个数;

②.定义前后指针i和j,分别指向数列的开头和结尾;

③.从后往前找到第一个比基准数小的数,记录其坐标j;

④.从前往后找到第一个比基准数大的数,记录其坐标i;

⑤.如果i < j,则交换i和j的位置,使得比基准数大的数在右侧,比基准数小的数在左侧;

⑥.重复③、④、⑤步骤,直到i >= j,此时前后指针相遇;

⑦.交换基准数和前后指针相遇的位置,即将基准数放入正确的位置;

⑧.将数列分成两个部分,分别对左边和右边的部分重复以上步骤,直到整个数列有序。

代码实现:

    //前后指针法找基准数private int partition(int[] arr, int left, int right) {int prev = left, cur = left+1;while (cur <= right) {while (arr[cur] < arr[left] && arr[++prev] != arr[cur]) {int tmp = arr[cur];arr[cur] = arr[prev];arr[prev] = tmp;}cur++;}int tmp = arr[left];arr[left] = arr[prev];arr[prev] = tmp;return prev;}

♪算法的优化

♩三数取中

当取到的基准数为最大或最小时,快速排序的效率是比较低的,故我们可以通过选取最左边,中间,最右边三个数的中间值降低取到的基准数最大或最小的情况。

    private void quick(int[] arr, int start, int end) {//递归结束条件if (end <= start) {return;}//找到下标为start、end、(start+end)/2中的数中第二大的数的小标int MidIndex = findMidValOfIndex(arr, start, end);//确保三个数中第二大的在start位置if (MidIndex != start) {int tmp = arr[start];arr[start] = arr[MidIndex];arr[MidIndex] = tmp;}//获取下一个基准数的位置int pivot = partition(arr, start, end);//对基准数的左边进行快速排序quick(arr, start, pivot - 1);//对基准数的右边进行快速排序quick(arr, pivot + 1, end);}//找到下标为start、end、(start+end)/2中的数中第二大的数的小标private int findMidValOfIndex(int[] array, int start, int end) {int midIndex = (start+end) / 2;if(array[start] < array[end]) {if(array[midIndex] < array[start]) {return start;}else if(array[midIndex] > array[end]) {return end;}else {return midIndex;}}else {if(array[midIndex] > array[start]) {return start;}else if(array[midIndex] < array[end]) {return end;}else {return midIndex;}}}

♩小区间快排

快速排序越排是越有序的,故当待排序序列长度较小时,插入排序的性能可能比快速排序更优。因此可以在递归深度达到一定阈值时,使用插入排序来处理子序列。

    private void quick(int[] arr, int start, int end) {//递归结束条件if (end <= start) {return;}//递归到小区间时,用直接插入排序if (end - start + 1 <= 15) {insertSort(arr, start, end);return;}//找到下标为start、end、(start+end)/2中的数中第二大的数的小标int MidIndex = findMidValOfIndex(arr, start, end);//确保三个数中第二大的在start位置if (MidIndex != start) {int tmp = arr[start];arr[start] = arr[MidIndex];arr[MidIndex] = tmp;}//获取下一个基准数的位置int pivot = partition(arr, start, end);//对基准数的左边进行快速排序quick(arr, start, pivot - 1);//对基准数的右边进行快速排序quick(arr, pivot + 1, end);}//直接插入排序private void insertSort(int[] arr, int left, int right) {for (int i = left+1; i <= right; i++) {int tmp = arr[i];int j = 0;for (j = i-1; j >= left; j--) {if (arr[j] > tmp) {arr[j+1] = arr[j];} else {break;}}arr[j+1] = tmp;}}

♪算法稳定性

在快速排序中,如果两个元素的值相同,它们在排序后可能会交换位置,因此快速排序是不稳定的排序。

♪时间复杂度

快速排序的时间复杂度取决于分割点选择的方法,如果每次选择中位数作为分割点,则最坏时间复杂度为O(n^2),平均时间复杂度为O(n log n);如果分割点随机选择,则最坏时间复杂度为O(n^2)的概率很小,平均时间复杂度为O(n log n)。因此,快速排序的平均时间复杂度为O(n log n),最坏时间复杂度为O(n^2)

♪空间复杂度

快速排序的空间复杂度为 O(nlogn),其中 n 为排序的元素个数。这是因为快速排序是一种原地排序算法,它只需要将排序过程中所需的一些暂存空间复用,不会额外占用大量的内存空间。而在最坏情况下,快速排序的空间复杂度可能会退化为 O(n),但这种情况非常罕见。

♫归并排序

♪基本思想

归并排序是一种分治算法,是将一个大的问题分解成若干个小问题来解决,然后将这些小问题的解合并起来,得到原始问题的解。归并排序的基本思想是:先将待排序的序列分成两个部分,直到每个部分只有一个元素为止,再将两个单元素部分归并成一个有序序列,直到所有序列都合并成一个有序序列为止。

♪算法实现

//归并排序(从小到大)public void mergeSort(int[] arr) {mergeSortChild(arr, 0, arr.length-1);}public void mergeSortChild(int[] arr, int left, int right) {if (left <= right) {return;}int mid = (left+right) / 2;mergeSortChild(arr, left, mid);mergeSortChild(arr, mid+1, right);merge(arr,left,mid,right);}private static void merge(int[] arr,int left,int mid,int right) {int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;int[] tmpArr = new int[right-left+1];int k = 0;//表示tmpArr 的下标while (s1 <= e1  && s2 <= e2) {if(arr[s1] <= arr[s2]) {tmpArr[k++] = arr[s1++];}else{tmpArr[k++] = arr[s2++];}}while (s1 <= e1) {tmpArr[k++] = arr[s1++];}while (s2 <= e2) {tmpArr[k++] = arr[s2++];}//tmpArr当中 的数据 是right  left 之间有序的数据for (int i = 0; i < k; i++) {arr[i+left] = tmpArr[i];}}

♪算法的稳定性

归并排序在合并两个有序子序列的时候,如果两个元素的大小相同,那么在归并的结果中,左边的元素肯定会先被放入序列中,也就是说它们的相对顺序没有改变。故归并排序是一种稳定的排序。

♪时间复杂度

归并排序在排序过程中,先将待排序的序列递归地拆分为两个子序列,每次拆分会将序列长度缩小一半。然后将两个子序列合并成一个有序的序列,合并操作的时间复杂度为O(n)。整个排序过程中,序列被拆分成了logn级别的子序列,每个子序列都要进行一次合并操作,所以时间复杂度为O(nlogn)

♪空间复杂度

在归并排序中,需要创建一个额外的数组来保存排序的结果,在归并过程中,需要不断地合并两个有序数组,因此需要创建一个长度为n的数组来保存排序后的结果。另外,在归并排序的递归过程中,需要创建一个长度为n的临时数组来保存每次递归的中间结果,所以归并排序的空间复杂度为O(n)

♫排序算法的比较

排序算法最好时间复杂度最坏时间复杂度平均时间复杂度空间复杂度稳定性
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
插入排序O(n)O(n^2)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
希尔排序O(n)O(n^2)O(n^1.3)O(1)不稳定
堆排序O(n*logn)O(n*logn)O(n*logn)O(1)不稳定
快速排序O(n*logn)O(n^2)O(n*logn)O(logn)~O(n)不稳定
归并排序O(n*logn)O(n*logn)O(n*logn)O(n)稳定

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

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

相关文章

MongoDB【部署 02】mongodb使用配置文件启动、添加为系统服务及自启动(一个报错:[13436][NotMasterOrSecondary])

MongoDB使用配置文件启动、添加为系统服务及设置自启动 1.是什么2.下载安装启动配置2.1 下载2.2 安装2.3 配置2.4 使用配置文件启动 3.设置系统服务及自启动3.1 设置为系统服务3.2 自启动 1.是什么 【以下内容来自ChatGPT3.5】 MongoDB是一个流行的开源文档型数据库管理系统&a…

SpringBoot实战(二十四)集成 LoadBalancer

目录 一、简介1.定义2.取代 Ribbon3.主要特点与功能4.LoadBalancer 和 OpenFeign 的关系 二、使用场景一&#xff1a;Eureka LoadBalancer服务A&#xff1a;loadbalancer-consumer 消费者1.Maven依赖2.application.yml配置3.RestTemplateConfig.java4.DemoController.java 服务…

力扣刷题-链表理论基础

什么是链表 什么是链表&#xff0c;链表是一种通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域一个是指针域&#xff08;存放指向下一个节点的指针&#xff09;&#xff0c;最后一个节点的指针域指向null&#xff08;空指针的意思&a…

金融风控建模常用指标介绍(WOE, IV, KS, PSI)

金融风控建模常用指标介绍&#xff08;WOE, IV, KS, PSI&#xff09; 近期在做金融风控相关项目&#xff0c;有必要把特征和模型的衡量指标总结下&#xff0c;以备不时之需。这次主要介绍4个指标&#xff08;WOE, IV, KS, PSI&#xff09;。 WOE&#xff08;Weight of Evidenc…

力扣-228.汇总区间

AC Code 自己做出来的&#xff0c;代码写的很烂&#xff0c;但是也浅浅记录一下叭&#xff0c;下面有看答案思路写出来的双指针代码 class Solution { public:vector<string> summaryRanges(vector<int>& nums) {vector<string> ans;int n nums.size();…

上市公司-供应链数字化示范名单匹配(2000-2022年)

参考《经济管理》刘海建&#xff08;2023&#xff09;、《中国软科学》张树山&#xff08;2021&#xff09;的做法&#xff0c;将商务部公开的“供应链创新与应用试点企业、试点城市”分别与上市公司匹配&#xff0c;得到2份DID数据 一、数据介绍 数据名称&#xff1a;上市公司…

FPGA:卷积编码及维特比译码仿真

FPGA&#xff1a;卷积编码及维特比译码仿真 本篇记录一下在FPGA中完成卷积编码和维特比译码的过程&#xff0c;通过代码解释编码的过程和译码的过程&#xff0c;便于理解&#xff0c;同时也方便移植到其他工程中。 1. 准备工作 卷积编译码IP核—convolutionIP核和viterbiIP核…

工作流 Flowable 的使用

一、BPMN 业务流程建模与标注 通过 Status&#xff08;状态&#xff09; 字段维护流程状态&#xff0c;流程负责的审批人可能也是 Hard Code&#xff08;硬编码&#xff09;会出现以下问题&#xff1a; 1.流程健壮性差&#xff0c;但凡出现人员变动&#xff0c;或者组织结构调…

Linux部署项目

本文以人人权限管理系统为例&#xff0c;使用finalshell工具连接服务器。服务器使用的是腾讯云服务器。用自己虚拟机也可以完成项目部署。 后端代码renren-security: 采用SpringBoot2、MyBatis-Plus、Shiro框架&#xff0c;开发的一套权限系统&#xff0c;极低门槛&#xff0c…

【RocketMQ】(五)消息的消费

消费者从Broker拉取到消息之后&#xff0c;会将消息提交到线程池中进行消费&#xff0c;RocketMQ消息消费是批量进行的&#xff0c;如果一批消息的个数小于预先设置的批量消费大小&#xff0c;直接构建消费请求ConsumeRequest将消费请求提交到线程池处理&#xff0c;否则需要分…

OpenMesh 网格平滑

文章目录 一、简介二、相关参数二、实现代码三、实现效果参考资料一、简介 由于物理采样过程固有的局限性,三维扫描仪获得的网格通常是有噪声的。为了消除这种噪声,所谓的平滑算法被开发出来。这类方法有很多,OpenMesh主要为我们提供了两种平滑算法,一种是较为经典的Laplac…

火山引擎 ByteHouse:ClickHouse 如何保证海量数据一致性

背景 ClickHouse是一个开源的OLAP引擎&#xff0c;不仅被全球开发者广泛使用&#xff0c;在字节各个应用场景中也可以看到它的身影。基于高性能、分布式特点&#xff0c;ClickHouse可以满足大规模数据的分析和查询需求&#xff0c;因此字节研发团队以开源ClickHouse为基础&…

【【萌新的FPGA学习之实战流水灯】】

萌新的FPGA学习之实战流水灯 实验任务 本节的实验任务是使用领航者底板上的两个 PL LED 灯顺序点亮并熄灭&#xff0c;循环往复产生流水灯的效 果&#xff0c;流水间隔时间为 0.5s。 1MHz&#xff1d;1000000Hz 10的6次方 1ns&#xff1d;10的-9次方秒 开发板晶振50Mhz 计算得…

NIO简单介绍

一、什么是NIO 1、Java NIO全称java non-blocking IO&#xff0c; 是指JDK提供的新API。从JDK1.4开始&#xff0c;Java提供了一系列改进的输入/输出的新特性&#xff0c;被统称为NIO(即New IO)&#xff0c;是同步非阻塞的 2、NIO有三大核心部分: Channel(通道)&#xff0c; Buf…

Goland设置头注释

package ${GO_PACKAGE_NAME} * Author: 坐公交也用券 * HomePage: https://liumou.site * File: ${NAME}.go * Date: ${DATE} ${TIME} * Des: 文件作用

点分治维护dp+连通块上新型dp思路+乘积方面进行根号dp:0922T4

首先连通块&#xff0c;所以点分治肯定是 Trick1 钦定选根的连通块dp 对于钦定选根的连通块dp&#xff0c;有一种常见思路 先对原树求其dfn序&#xff0c;按dfn序倒序求解 具体的&#xff0c;对于当前点 i i i&#xff08;注意这里都是指dfn序&#xff09;&#xff0c;我们…

企业电子招标采购系统源码之从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

【智慧工地源码】智慧工地助力数字建造、智慧建造、安全建造、绿色建造

智慧工地围绕建设过程管理&#xff0c;建设项目与智能生产、科学管理建设项目信息生态系统集成在一起&#xff0c;该数据在虚拟现实环境中&#xff0c;将物联网收集的工程信息用于数据挖掘和分析&#xff0c;提供过程趋势预测和专家计划&#xff0c;实现工程建设的智能化管理&a…

[Linux]线程概念

[Linux]线程概念 文章目录 [Linux]线程概念什么是线程Linux系统下的线程实现线程是CPU调度的基本单位进程是系统分配资源的基本实体二级页表 线程的优点线程的缺点线程异常线程用途线程资源 什么是线程 线程是进程内部的一个执行分支&#xff0c;执行粒度比进程更细&#xff0…

【Java 基础篇】Java网络编程:下载进度监控实现详解

文件下载是许多应用程序的重要功能&#xff0c;而下载进度监控是提高用户体验的关键。在本文中&#xff0c;我们将详细介绍如何使用Java实现文件下载进度监控&#xff0c;以便用户可以实时了解文件下载的进度。 什么是下载进度监控 下载进度监控是一种用户界面元素或功能&…