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

目录

♫什么是排序

♪排序的概念

♪排序的稳定性

♪排序的分类

♪常见的排序算法

♫直接插入排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫希尔排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫直接选择排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫堆排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫冒泡排序

♪基本思想

♪算法实现

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫快速排序

♪基本思想

♪算法实现

♪算法的优化

♪算法稳定性

♪时间复杂度

♪空间复杂度

♫归并排序

♪基本思想

♪算法实现

♪算法的稳定性

♪时间复杂度

♪空间复杂度

♫排序算法的比较


♫什么是排序

♪排序的概念

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

♪排序的稳定性

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

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

♪排序的分类

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

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.mzph.cn/news/86095.shtml

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

相关文章

【日常业务开发】Java实现异步编程

【日常业务开发】Java实现异步编程 Java实现异步编程什么是异步异步的八种实现方式异步编程线程异步Future异步CompletableFuture实现异步Spring的Async异步Spring ApplicationEvent事件实现异步消息队列ThreadUtil异步工具类Guava异步 CompletableFuture异步编排工具类创建异步…

unity自己对象池的使用

unity出了自己的对象池 这里记录一下用法 命名空间就是这个 一般有两种用法&#xff0c;第一种是在using里面获取&#xff0c;脱离这个域就释放。第二种是在Get和Release配合使用 // This version will only be returned to the pool if we call Release on it.//只有使用Re…

Android进阶之路 - 盈利、亏损金额格式化

在金融类型的app中&#xff0c;关于金额、数字都相对敏感和常见一些&#xff0c;在此仅记录我在金融行业期间学到的皮毛&#xff0c;如后续遇到新的场景也会加入该篇 该篇大多采用 Kotlin 扩展函数的方式进行记录&#xff0c;尽可能熟悉 Kotlin 基础知识 兄弟 Blog StringUti…

MediaPipe+OpenCV 实现实时手势识别(附Python源码)

MediaPipe官网&#xff1a;https://developers.google.com/mediapipe MediaPipe仓库&#xff1a;https://github.com/google/mediapipe 一、MediaPipe介绍 MediaPipe 是一个由 Google 开发的开源跨平台机器学习框架&#xff0c;用于构建视觉和感知应用程序。它提供了一系列预训…

Redis 面霸篇:从高频问题透视核心原理

Redis 为什么这么快&#xff1f; 很多人只知道是 K/V NoSQl 内存数据库&#xff0c;单线程……这都是没有全面理解 Redis 导致无法继续深问下去。 这个问题是基础摸底&#xff0c;我们可以从 Redis 不同数据类型底层的数据结构实现、完全基于内存、IO 多路复用网络模型、线程…

HTML5day02综合案例2

案例展示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>注册信息</title> </head> &l…

新思路,4.9+氧化应激相关基因构建风险模型

今天给同学们分享一篇氧化应激预后模型的生信文章“Construction of an oxidative stress-related lncRNAs signature to predict prognosis and the immune response in gastric cancer”&#xff0c;这篇文章于2023年5月31日发表在Scientific Reports期刊上&#xff0c;影响因…

分库分表MySQL

目录 Mycat入门 分片配置 分片配置(配置Mycat的用户以及用户的权限) 启动服务 登录Mycat Mycat配置 schema.xml 1.schema标签:配置逻辑库,逻辑表的相关信息 1-1.核心属性 1-2.table标签 2.datanode标签:配置数据节点的相关信息 核心属性 3.datahost标签:配置的是节…

力扣 -- 215. 数组中的第K个最大元素

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int QuickSelectSort(vector<int>& nums,int begin,int end,int k){//随机选keyint keynums[beginrand()%(end-begin1)];//left在左端点前一个位置int leftbegin-1;//right在右端点后一个位置in…

【Verilog语法】比较不同计数器的运算方式,其中有一个数是延迟打一拍的效果,目的是使得两个计数器的结果相同。

比较不同计数器的运算方式&#xff0c;其中有一个数是延迟打一拍的效果&#xff0c;目的是使得两个计数器的结果相同。 1&#xff0c;第一种2&#xff0c;第二种3&#xff0c;第三种 第三种方案&#xff0c;完成实现。 1&#xff0c;第一种 &#xff08;1&#xff09;RTL modu…

【深度学习】图像去噪(2)——常见网络学习

【深度学习】图像去噪 是在 【深度学习】计算机视觉 系列文章的基础上&#xff0c;再次针对深度学习&#xff08;尤其是图像去噪方面&#xff09;的基础知识有更深入学习和巩固。 1 DnCNN 1.1 网络结构 1.1.1 残差学习 1.1.2 Batch Normalization (BN) 1.1.2.1 背景和目标…

如何使用固态硬盘+硬盘盒子+U盘创造移动双系统

本文背景 这学期上了一节鸟水课《大数据实践》&#xff0c;老师要求扩展硬盘盒&#xff0c;以部署大数据工具进行 机器挖掘等大数据领域工作 参考视频链接&#xff1a;无需启动盘&#xff0c;用虚拟机将ubuntu安装到移动硬盘上_哔哩哔哩_bilibili 项目使用设备 1.绿联&#…

软件工程之总体设计

总体设计是软件工程中的一个重要阶段&#xff0c;它关注整个系统的结构和组织&#xff0c;旨在将系统需求转化为可执行的软件解决方案。总体设计决定了系统的架构、模块划分、功能组织以及数据流和控制流等关键方面。 可行性研究 具体方面&#xff1a;经济可行性、技术可行性…

RabbitMQ的工作模式——WorkQueues

1.工作队列模式 生产者代码 public class Producer_WorkQueues1 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory new ConnectionFactory();//2.设置参数factory.setHost("172.16.98.133&qu…

81《乡村振兴战略下传统村落文化旅游设计》许少辉瑞博士生辉少许——2023学生开学季许多少年辉光三农

81《乡村振兴战略下传统村落文化旅游设计》许少辉瑞博士生辉少许——2023学生开学季许多少年辉光三农

ESD门禁闸机的用途及优点

ESD门禁闸机是一种专门用于防止静电干扰的门禁设备&#xff0c;其主要用途包括&#xff1a; 防止静电干扰&#xff1a;ESD门禁闸机可以有效地防止静电干扰&#xff0c;保护电子元器件、电路板等敏感设备不受静电破坏。 控制人员进出&#xff1a;ESD门禁闸机可以通过身份验证等…

AWS入列CNCF基金会

7月27日&#xff0c;IT之家曾经报道&#xff0c;微软加入Linux旗下CNCF基金会&#xff0c;在这之后不到一个月的今天&#xff0c;亚马逊AWS也宣布&#xff0c;以铂金身份加入此基金会。 CNCF&#xff0c;全称Cloud Native Computing Fundation&#xff0c;该基金会旨在使得容器…

Netty简介及简单客户端/服务端示例代码

什么是Netty&#xff1f; Netty是一个NIO客户机-服务器框架&#xff0c;它支持快速而容易地开发网络应用程序&#xff0c;如协议服务器和客户机。它大大简化和简化了网络编程&#xff0c;如TCP和UDP套接字服务器。 “快速简单”并不意味着生成的应用程序将遭受可维护性或性能问…

【再识C进阶3(上)】详细地认识字符串函数、进行模拟字符串函数以及拓展内容

小编在写这篇博客时&#xff0c;经过了九一八&#xff0c;回想起了祖国曾经的伤疤&#xff0c;勿忘国耻&#xff0c;振兴中华&#xff01;加油&#xff0c;逐梦少年&#xff01; 前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;…

基于AVR128单片机智能电风扇控制系统

一、系统方案 模拟的电风扇的工作状态有3种&#xff1a;自然风、常风及睡眠风。使用三个按键S1-S3设置自然风、常风及睡眠风。 再使用两个按键S4和S5&#xff0c;S4用于定时电风扇定时时间长短的设置&#xff0c;每按一次S4键&#xff0c;定时时间增加10秒&#xff0c;最长60秒…