【数据结构】常见八大排序算法总结

目录

前言

1.直接插入排序

2.希尔排序

3.选择排序

4.堆排序

5.冒泡排序

6.快速排序

6.1Hoare版本

6.2挖坑法

6.3前后指针法

6.4快速排序的递归实现 

6.5快速排序的非递归实现

7.归并排序

8.计数排序(非比较排序)

9.补充:基数排序

10.总结:排序算法的复杂度及稳定性分析


前言

排序 :排序就是使一串记录按照其中某个或某些关键字的大小,递增或者递减的排列起来的操作

内部排序:数据元素全部存放在内存中的排序

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

常见的排序算法:

以上排序算法都是比较排序,还有计数排序这类非比较排序算法,一下我们对各个排序算法进行代码实现

1.直接插入排序

直接插入排序是一种简单的插入排序算法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

过程:当插入第i(i>=1)个元素时,前面的array[0],...,array[i-1]已经有序,此时用array[i]的排序码与array[i-1],array[i-2],...的排序码依次比较,找到插入位置后将原来位置上的元素顺序后移,将array[i]插入

📖Note:

对于数组中的第一个数据,不需要进行比较,因此第一步操作的是a[1]处的数据,区间[0,0]上的数据即a[0]这一个数据是有序的,因此外层循环只需要对n-1次,对a[1]到a[n-1]共n-1个数据进行处理

对于要处理的a[i],依次与a[i-1],a[i-2]比较,如果大于a[i]则交换(升序排列的情况)

优化:备份i处的数据,依次与a[i-1],a[i-2]比较,大于a[i]则向后移动一个位置,否则在小于a[i]的数据元素的后一个位置插入a[i]

时间复杂度与空间复杂度:

插入排序是在存放原数据的数组上进行操作,所以直接插入排序算法的空间复杂度是O(1)

1️⃣当原数据的序列是逆序时,为最坏情况,此时直接插入排序算法的时间复杂度是O(N^2)

如上图,序列逆序时,数据的挪动次数为1+2+3+...+n-1 = n(n-1)/2

所以时间复杂度为O(N^2)

2️⃣当原数据的序列有序时,为最好情况,此时直接插入排序算法的时间复杂度是O(N)

如上图:序列顺序排列时,总共进行了n-1次比较,没有数据的挪动

因此时间复杂度为O(N)

总结:元素集合越接近有序,直接插入排序算法的时间效率越高

//直接插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; ++i){// [0,end]有序,end+1处的数据找到正确位置后,[0, end+1]有序int end = i;int tmp = a[end + 1];while (end >= 0){//升序排列if (a[end] > tmp){a[end + 1] = a[end];--end;}else{break;}}//此时,a[end]<=tmpa[end + 1] = tmp;}
}

2.希尔排序

希尔排序又称为缩小增量排序,希尔排序的基本思想:先选定一个整数gap,把待排序文件中的所有记录分成n/gap个组,每一组内的记录进行排序,然后更新(缩小)gap,重复上述分组和排序的操作,当gap=1时,所有记录在同一组内排序,即使所有记录有序

以下以初始增量为3为例

第一次分组如下图

每一组内的记录进行排序,使用直接插入排序算法

缩小gap=1,即对序列:5 1 2 5 6 3 8 7 4 9进行排序

每一组记录使用直接插入排序算法,当gap不为1时,每组记录的数据元素并不是连续排列的,而是间隔gap,因此需要对直接插入排序算法进行改造

for (int i = 0; i < n - gap; ++i)
{int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;
}

优化:for循环调整部分为i++时,每次直接插入排序排一组记录,当改为i+=gap时,可以实现多组记录同时排序

📖Note:

希尔排序是对直接插入排序的优化,当gap>1时都是预排序,目的是使数据集合接近有序,当数据集合接近有序时,直接插入排序的时间效率较高

gap的选择:

gap的取法有多种,最初Shell提出取gap = n/2,gap = gap/2,直到gap = 1;后来Knuth提出取gap = gap/3 + 1。我们采用Knuth提出的方式取值。

gap越大,大数可以越快的跳到后面;gap越小,跳的越慢,但数据集合越接近有序

时间复杂度和空间复杂度:

希尔排序算法不额外开辟空间,其空间复杂度为O(1)

对希尔排序的时间复杂度分析很困难,在特定情况下可以准确估算关键码的比较次数和对象移动次数,但想要弄清楚关键码的比较次数和对象移动次数与增量选择之间的依赖关系,目前还没有较完整的数学分析,Knuth在《计算机程序设计技巧》中的结论是:在n很大时,关键码的平均比较次数和对象平均移动次数大约在n^1.25到1.6*n^1.25范围内,这是在利用直接插入排序作为子序列排序方法的情况下得到的。

//希尔排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;//更新增量//for (int i = 0; i < n - gap; ++i)for (int i = 0; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

3.选择排序

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

直接选择排序的步骤

1️⃣在元素集合array[i]到array[n-1]中选择关键码最大(小)的数据元素

2️⃣若它不是这组元素中的最后一个或第一个元素,则将它与这组元素中的最后一个或第一个元素交换

3️⃣在剩余的array[i]到array[n-2](array[i+1]到array[n-1])集合中,重复上述步骤,直到集合剩余一个元素

简单来说直接选择排序将数据集合分成两部分,有序部分和无序部分

以排升序为例,每次选取待排数据集合即无序部分中的最大数放到无序部分的第一个位置即有序部分之后的第一个位置,就完成了一个数据元素的排序

优化:选择元素时选取最大元素和最小元素的操作同时进行,一趟比较选出最大的元素放在a[end]位置,选出最小的元素放在a[begin]位置,begin++,end--,重复上述操作

优化之后数组的两端有序部分,中间为无序部分

时间复杂度和空间复杂度:

直接选择排序算法不额外开辟空间,其空间复杂度为O(1)

1️⃣当原数据的序列是逆序时,为最坏情况,此时选择排序算法的时间复杂度是O(N^2)

遍历待排数据元素集合,共n个数据,选出最大值和最小值,需要比较n-1次

遍历待排数据元素集合,共n-2个数据,选出最大值和最小值,需要比较n-3次

综上所述,当原序列的数据元素逆序时,总共比较的次数为

(n-1)+(n-3)+...+1 = (n/2) *n  / 2

所以直接选择排序的时间复杂度为O(N^2)

2️⃣当原数据的序列有序时,为最好情况,此时直接插入排序算法的时间复杂度是O(N^2)

对于直接选择排序算法来说,逆序和顺序的时间复杂度相同,因为进行数据选择的操作是相同的,都是进行遍历选数,因此效率不高】

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//选择排序
void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){// 选出最小的放begin位置// 选出最大的放end位置int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; ++i){if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);// 修正maxi:特殊情况begin和maxi重叠时,执行一次交换,maxi记录的是最小值if (maxi == begin)maxi = mini;Swap(&a[end], &a[maxi]);++begin;--end;}
}

4.堆排序

冒泡排序详解可以参考:CSDN

5.冒泡排序

冒泡排序详解可以参考:CSDN

6.快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,快速排序的基本思想为:任取待排元素序列中的某个元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中的所有元素均小于基准值,右子序列中的所有元素均大于基准值,然后左右序列重复该过程,直到所有元素都排列在相应的位置上为止

快速排序递归实现与二叉树的前序遍历规则类似,需要注意的是如何按照基准值来对区间中数据进行划分,常见的方式有以下几种:

6.1Hoare版本

单趟排序:选定一个基准值key,一般情况下为第一个或最后一个元素,设置两个变量left和right分别指向数据元素集合的头和尾,left向后找大数,right向前找小数,找到则交换,直到二者相遇,将相遇位置的数与key交换。

单趟排序结束后,数据元素的排列为:小数  key  大数,其中小(大)数是指小(大)于key的数。key的位置已经确定,不需要再更改,分割出了两个子区间,若这两个子区间有序,则整体有序,因此递归对两个子区间进行排序即可使整体有序。

以下为单趟排序的参考代码:

int keyi = left;//选取第一个数作为关键值while (left < right){//right找小数while (left < right && a[right] >= a[keyi]){--right;}//left找大数while(left < right && a[left] <= a[keyi]){++left;}//判断left和right的关系,防止错过if (left < right){Swap(&a[left], &a[right]);}}//left和right相遇,交换相遇位置的值和关键值Swap(&a[left], &a[keyi]);

📖Note:

1️⃣与关键值比较大小时,需要注意与关键值相等的特殊情况

对left,找大数,遇到小于key的值时继续向后走;遇到等于key的值时,也应该向后继续寻找,如果不过滤等于key的值,则会产生死循环

当数据元素集合中为单值时,如果不过滤等于的情况,right找小数的循环将会是一个死循环;或者当key == a[right]时,也会陷入死循环

2️⃣当选取第一个数据元素作为key,left与right相遇时,交换相遇位置的数据与key,如何保证相遇的位置比key小?(key是第一个数据元素,属于小数区间)

left和right相遇有两种情况:

  1. right停下来,left撞到right相遇,相遇位置比key小:right找小数,所以right停下来的位置为小于key的数,即相遇位置为小于key的数,属于小数区间。因此若选取第一个数据元素做key,则right先走
  2. left停下来,right撞到left相遇,相遇位置比key大:left找大数,所以left停下来的位置为大于key的数,即相遇位置为大于key的数,属于大数区间。因此若选取最后一个数据元素做key,则left先走
// Hoare
int PartSort1(int* a, int left, int right)
{//取第一个数据元素为关键值int keyi = left;while (left < right){// R找小while (left < right && a[right] >= a[keyi]){--right;}// L找大while (left < right && a[left] <= a[keyi]){++left;}if (left < right)Swap(&a[left], &a[right]);}//相遇位置为meetiint meeti = left;Swap(&a[meeti], &a[keyi]);return meeti;
}

快速排序的优化:主要是对关键值key选取的优化

key的选择有三种方法:

  1. 随机选key
  2. 针对有序序列,先key为最中间位置的数据
  3. 三数取中:即第一个元素,中间位置元素,最后一个位置元素三数取中位数

三数取中的函数接口如下:

int GetMidIndex(int* a, int left, int right)
{int mid = left + (right - left) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}// a[left] >= a[mid]else{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}

对key的选择优化之后,可以减少递归的层数,有效避免栈溢出

关键值key的选择优化之后,Hoare版本的单趟排序可以优化:通过三数取中的方法确定关键值key后,将key与第一个数据交换,此时关键值key仍为第一个数据,因此其他地方不需要修改

// Hoare
int PartSort1(int* a, int left, int right)
{// 三数取中int mid = GetMidIndex(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;while (left < right){// R找小while (left < right && a[right] >= a[keyi]){--right;}// L找大while (left < right && a[left] <= a[keyi]){++left;}if (left < right)Swap(&a[left], &a[right]);}//相遇位置为meetiint meeti = left;Swap(&a[meeti], &a[keyi]);return meeti;
}

6.2挖坑法

挖坑法是对Hoare版本的改进,确定关键值key之后,将其备份,并将其作为一个坑位(数据集合中的第一个元素),right先走找小数,找到则将该小数填到坑位中,并更新该小数的位置为新的坑位,left再走找大数,找到后将该大数填到坑位中,并更新该大数的位置为新的坑位,重复上述操作,直到left和right相遇,相遇位置为一个坑位,填入关键值key

具体过程如下图所示;

// 挖坑法
int PartSort2(int* a, int left, int right)
{// 三数取中int mid = GetMidIndex(a, left, right);Swap(&a[left], &a[mid]);int key = a[left];int hole = left;while (left < right){// 右边找小,填到左边坑while (left < right && a[right] >= key){--right;}a[hole] = a[right];hole = right;// 左边找大,填到右边坑while (left < right && a[left] <= key){++left;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}

6.3前后指针法

前后指针法也是对Hoare版本的改进,确定关键值key(数据元素集合的第一个元素)并备份,设置两个指针,prev和cur,prev初始化为第一个数据元素,cur初始化为prev的下一个数据元素,当cur没有越界时,cur向后找小数,找到则交换prev指向的数据和cur指向的元素,prev++,cur++,重复上述操作,直到cur越界时,交换prev指向的数据元素和关键值key

📖Note:prev++的操作在交换操作之前,cur++的操作在交换操作之后

在cur找到小数时,prev指向的也为小数,其下一个位置为大数,所以prev需要先加1再交换

具体过程如下图所示:

特殊情况:++prev == cur时,会产生自己和自己交换的情况,因此需要过滤

// 前后指针法
int PartSort3(int* a, int left, int right)
{// 三数取中int mid = GetMidIndex(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){// cur找小if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[cur], &a[prev]);++cur;}Swap(&a[keyi], &a[prev]);return prev;
}

6.4快速排序的递归实现 

快速排序的递归实现:

单趟排序结束后,数据元素的排列为:小数  key  大数

小数区间为[begin ,keyi -1],大数区间为[keyi+1,end]

按照单趟排序的方法递归对这两个区间排序即可

递归结束的条件:区间中只有一个值时递归调用结束,begin==end;或者begin>end,区间无效时递归调用结束

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}//每趟排序区间的划分:Hoare,挖坑法,前后指针法//调用相应接口即可int keyi = PartSort1(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

6.5快速排序的非递归实现

函数的调用需要在开辟栈帧,如果递归调用的层数太深,可能会造成栈溢出

我们可以借助数据结构中的栈来优化快速排序,数据结构中的栈是在堆区开辟空间,堆区的空间要远远大于栈区空间

实际上,快速排序的非递归是借助数据结构中的栈模拟递归的过程

非递归实现思路:每次将区间的左右区间压栈,当栈不为空时,取栈顶两个元素分别作为一趟排序的区间的左右端点进行排序

📖Note:

如果先定义right,则需要区间左端点begin要先入栈,区间右端点end后入栈,这样才能保证right被区间右端点赋值,left被区间左端点赋值

//快速排序的非递归实现
void QuickSortNonR(int* a, int begin, int end)
{ST st;StackInit(&st);StackPush(&st, begin);StackPush(&st, end);while (!StackEmpty(&st)){//区间右端点int right = StackTop(&st);StackPop(&st);//区间左端点int left = StackTop(&st);StackPop(&st);//区间划分,单趟排序int keyi = PartSort3(a, left, right);//右区间if (keyi + 1 < right){StackPush(&st, keyi + 1);StackPush(&st, right);}//左区间if (left < keyi - 1){StackPush(&st, left);StackPush(&st, keyi - 1);}}StackDestroy(&st);
}

7.归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的典型应用。将已经有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序,若将两个有序表合并成一个有序表,称为二路归并。

二路归并排序的步骤如下图:

由如下动态图,可以更好的理解二路归并排序的过程

归并排序的递归实现类似于二叉树的后续遍历,采用二叉树的后续遍历框架,递归结束的条件是begin>=end;区间的划分为 [begin, mid] 和 [mid+1, end],mid为中间位置

归并排序的时间复杂度和空间复杂度:

归并排序需要额外开辟一个长度为N的数组,因此其空间复杂度为O(N)

归并排序的时间复杂度为O(N*logN)

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (end + begin) / 2;_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);// 两两归并成有序序列:取小的尾插// [begin, mid] [mid+1, end]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//左区间中还有数据while (begin1 <= end1){tmp[i++] = a[begin1++];}//右区间中还有数据while (begin2 <= end2){tmp[i++] = a[begin2++];}// 拷贝回原数组:归并哪部分就拷贝哪部分回去memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

8.计数排序(非比较排序)

计数排序又称为鸽巢原理,是对哈希定址法的直接应用

计数排序的操作步骤:

  1. 统计相同元素出现的次数
  2. 根据统计的结果将序列回收到原来的序列中

📖Note:

计数排序中的相对映射

1.统计个数,得到个数记录数组C

2.将数组C转换成C[i]中存放的是值小于等于i的数据的个数

3.为A数组从前向后的每个元素找到对应的B中的位置,每次从A中复制一个元素到B中,C中相应的计数减一

4.当A中的所有数据都复制到B之后,B中存放的就是有序的数据

总结:计数排序在数据范围集中时,效率很高,但其适用范围有限

void CountSort(int* a, int n)
{int max = a[0], min = a[0];for (int i = 1; i < n; i++){if (a[i] > max){max = a[i];}if (a[i] < min){min = a[i];}}int range = max - min + 1;//统计计数int* countA = (int*)malloc(sizeof(int) * range);if (countA == NULL){perror("malloc fail");return;}memset(countA, 0, sizeof(int) * range);for (int i = 0; i < n; i++){countA[a[i] - min]++;//映射的相对位置}//排序int j = 0;for (int i = 0; i < range; i++){while (countA[i]--){a[j] = i + min;j++;}}
}

9.补充:基数排序

基数排序也属于非比较排序

基数排序的操作步骤:

  1. 分发数据
  2. 回收数据

多关键字排序有两种方式:MSD(最高位优先)和LSD(最低位优先)

基数排序的步骤如下图:

10.总结:排序算法的复杂度及稳定性分析

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,a[i] = a[j],且a[i] 在 a[j]之前,而在排序后的序列中,a[i] 仍在 a[j]之前,则称这种排序算法是稳定的,否则称之为不稳定的

1️⃣直接插入排序:稳定

关键码相同则不调整,继续向后排序

2️⃣希尔排序:不稳定

预排序时,相同的数据可能分到不同的组,不能保证稳定性

3️⃣选择排序:不稳定

4️⃣堆排序不稳定

当一个堆为单值时,向下调整会影响该值的稳定性,因此堆排序不稳定

5️⃣冒泡排序:稳定

关键码相同则不调整,继续向后排序

6️⃣快速排序:不稳定

7️⃣归并排序:稳定

关键码相同则不调整,继续向后排序

时间复杂度与空间复杂度总结:

算法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(N^2)O(N)O(N^2)O(1)稳定
简单选择排序O(N^2)O(N^2)O(N^2)O(1)不稳定
直接插入排序O(N^2)O(N)O(N^2)O(1)稳定
希尔排序O(N*logN)~O(N^2)O(N^1.3)O(N^2)O(1)不稳定
堆排序O(N*logN)O(N*logN)O(N*logN)O(1)不稳定
归并排序O(N*logN)O(N*logN)O(N*logN)O(N)稳定
快速排序O(N*logN)O(N*logN)O(N^2)O(logN)~O(N)不稳定

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

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

相关文章

保送阿里云的云原生学习路线

近期好多人都有咨询学习云原生有什么资料。与其说提供资料不如先说一说应该如何学习云原生。 Linux基础知识&#xff1a;云原生技术通常在Linux环境中运行&#xff0c;因此建议首先掌握Linux的基础知识&#xff0c;包括命令行操作、文件系统、权限管理等。 容器化技术&#x…

CentOS将磁盘剩余空间分配到已有分区

CentOS将磁盘剩余空间分配到已有分区 引growpartresize2fs 引 手里有台云服务&#xff0c;之前磁盘只有60G&#xff0c;在执行SQL语句时报错No space left on device. 通过df -h查看磁盘占用情况&#xff0c;确实所剩无几了 通过云服务后端控制台升级了下配置&#xff0c;将…

【管理篇 / 升级】❀ 13. FortiOS 7.4固件升级新规则 ❀ FortiGate 防火墙

【简介】飞塔防火墙的固件升级一直是所有厂家中最好的。只要有注册官方帐号&#xff0c;有注册设备&#xff0c;并且只要有一台设备在服务期内&#xff0c;即可下载所有型号的所有版本的固件。即使其它设备服务期已过&#xff0c;也可以通过固件文件手动升级&#xff0c;避免防…

STM32之OLED显示

一、模块介绍 1、常见的显示设备 LED、数码管、点阵、LCD屏(1602/12864)、OLED屏(消费电子) 2、OLED屏的概述 OLED&#xff0c;即有机发光二极管&#xff08;Organic Light-Emitting Diode&#xff09;&#xff0c;又称为有机电激光显示&#xff08;Organic Electroluminesenc…

SAP PI/PO 运行ESR报错:无法验证证书,将不执行该应用程序

java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: OCSP 运行ESR报错 解决方案&#xff1a; 1. 打开控制面板&#xff0c;找到JAVA 这个时候就可以正常打开ESR了

如何在CentOS 7 中搭建Python 3.0 环境

1、下载 通过https://www.python.org/ftp/python/下载Python安装包&#xff0c;这里下载Python-3.10.9.tgz&#xff1b; 2、上传 借助MobaXterm等工具将Python安装包上传至/opt目录&#xff1b; 3、解压 将JDK压缩文件解压至/opt目录&#xff1a;tar -xvf /opt/Python-3.1…

2024年第二届“华数杯”国际大学生数学建模竞赛 (A题 MCM)| 废水扩散分析 |数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看华数杯的A题&#xff01; 完整内容可以在文章末…

机器学习算法 - 马尔可夫链

马尔可夫链&#xff08;Markov Chain&#xff09;可以说是机器学习和人工智能的基石&#xff0c;在强化学习、自然语言处理、金融领域、天气预测、语音识别方面都有着极其广泛的应用 > The future is independent of the past given the present 未来独立于过去&#xff…

java SECS管理系统 将逐步推出 SECS 客户端(Passive) 管理系统 SECS快速开发平台 springboot secs开发平台

SECS管理系统 这是一套SECS客户端(Passive)&#xff0c;可以直接连接PLC设备,支持Modbus、三菱MC、欧姆龙Fine、OPC-UA、西门子S7设备等通信。 企业已经有了EAP软件&#xff0c;但是设备没有SECS通信功能&#xff0c;这时候可以使用这套框架&#xff0c;直接连接设备&#xff…

GEE使用

【GEE】Google Earth Engine&#xff08;GEE&#xff09;注册详细教程&无需教育邮箱-CSDN博客 数据下载代码 // Map the function over 3 months of data and take the median. // Load Landsat-8 surface reflectance data.var landsat8 ee.ImageCollection("LAND…

使用composer生成的DMG和PKG格式软件包有何区别

在使用Composer从包源构建软件包时候&#xff0c;有两种不同类型的包&#xff1a;PKG和DMG。你知道两者之间的区别吗? 以及如何选取吗&#xff1f; 每种格式都有各自的优势具体取决于软件包的预期用途以及用于部署软件包的工具。下面我们来了解一下PKG和DMG格式的区别和用途。…

Python密码本连接wifi

有时候我们会忘记自己的Wi-Fi密码&#xff0c;或者需要连接某个Wi-Fi网络以满足合法需求。本文将介绍如何使用Python编程语言编写一个简单的连接Wi-Fi的程序。 一、密码本准备 在进行wifi猜测时&#xff0c;其实就是列出各种可能的密码&#xff0c;用来尝试去访问目标wifi&…

Vue + JS + tauri 开发一个简单的PC端桌面应用程序

Vue JS tauri 开发一个简单的PC端桌面应用程序 文章目录 Vue JS tauri 开发一个简单的PC端桌面应用程序1. 环境准备1.1 安装 Microsoft Visual Studio C 生成工具[^2]1.2 安装 Rust[^3] 2. 使用 vite 打包工具创建一个 vue 应用2.1 使用Vite创建前端Vue项目2.2 更改Vite打包…

计算机毕业设计 基于Java的美食信息推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

vi/vim 编辑器 --基本命令

1 vi/vim编辑器介绍 vi 是visual interface 的简称&#xff0c;是Linux中最经典的文本编辑器 vim是vi的加强版。兼容了vi的所有指令&#xff0c;不仅能编辑文本&#xff0c;而且具有shell程序编辑的功能&#xff0c;可以通过不同颜色的字体辨别语法的正确性&#xff0c;极大…

轻松识别Midjourney等AI生成图片,开源GenImage

AIGC时代&#xff0c;人人都可以使用Midjourney、Stable Diffusion等AI产品生成高质量图片&#xff0c;其逼真程度肉眼难以区分真假。这种虚假照片有时会对社会产生不良影响&#xff0c;例如&#xff0c;生成公众人物不雅图片用于散播谣言&#xff1b;合成虚假图片用于金融欺诈…

支持华为GaussDB数据库的免费开源ERP:人力资源管理解决方案概述

开源智造所推出的Odoo SuperPeople数字化解决方案将HR和薪资数据与财务、项目规划、预算和采购流程连接起来&#xff0c;消除了多套系统给企业带来的信息孤岛问题。 ——复星集团 人力资源中心 高经理 一种更具吸引力、更有洞察力的人员管理方式 什么是开源智造Odoo的人力资源…

【Vue】后端返回文件流,前端预览文件

let date;request({url: this.$route.query.url,method: get,responseType: blob,}).then(resp > {date respthis.path window.URL.createObjectURL(new Blob([resp], {type: "application/pdf"}))}).catch((e) > {//旧版本浏览器下的blob创建对象window.Blo…

中产医学产康AI智慧云发布会暨中产学院2024年度盛典圆满成功

【向光而行】中国医学产康AI智慧云服务平台发布会暨中产学院2024年度盛典 前言 开新局&#xff0c;迈新步&#xff0c;谋新篇&#xff0c;创新绩。中产医学产康AI智慧云服务平台发布会 暨中产学院2024年度盛典于2024年1月10日在郑州.涵唐酒店成功举办。本次年会以“【向光而行…

跟着cherno手搓游戏引擎【7】Input轮询

在引擎程序中任何时间&#xff0c;任何位置都能知道按键是否按下、鼠标的位置等等信息。 与事件系统的区别&#xff1a;事件系统是在按下时调用并传递按键状态&#xff1b;轮询是每时每刻都能获取按键状态 创建基类&#xff1a; YOTO/Input.h&#xff1a;名如其意 #pragma …