当前位置: 首页 > news >正文

详解八大排序算法

文章目录

  • 前言
  • 排序算法
    • 插入排序
      • 直接插入排序:
      • 希尔排序(缩小增量排序)
    • 选择排序
      • 直接选择排序
      • 堆排序
    • 交换排序
      • 冒泡排序
      • 快速排序
        • hoare版本
        • 挖坑法
        • 前后指针版本
        • 快速排序的非递归
        • 快速排序总结
    • 归并排序
      • 归并排序的非递归实现:
    • 计数排序
  • 排序算法复杂度及稳定性分析
  • 总结

前言

本篇将讲述常用的排序算法,包括直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序,使用动图解释,并且画图阐明了递归的写法,同时实现了与非递归的写法复杂度和稳定性分析等等,看完绝对不亏!!!

🕺作者: 迷茫的启明星

😘欢迎关注:👍点赞🙌收藏✍️留言

🏇家人们,码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

持续更新中~

排序算法

排序的概念及其运用

排序的概念

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

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

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

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

全文以升序为例,降序类似

常见排序:

在这里插入图片描述

插入排序

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

实际中我们玩扑克牌时,就用了插入排序的思想

在这里插入图片描述

直接插入排序:

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

其实就是将前面i个数视为一个有序数组,即使是一个也是有序数组,第i+1的数和前一个数比较,如果比它小就不变,比它大就让大的数往后移,一直到遇到比它小的数为止。这里指的是升序。

在这里插入图片描述

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

代码实现:

void InsertSort(int* a, int n)
{for (int i = 0; i < n-1; ++i) {int end=i;int x=a[i+1];while(a[end]>x&&end>=0){a[end+1]=a[end];end--;}a[end+1]=x;}
}

希尔排序(缩小增量排序)

  • 希尔排序法又称缩小增量法。

  • 希尔排序法的基本思想是:先选定一个整数gap,把待排序文件中所有记录分成个组,

    所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。

    然后,gap不断取小,重复上述分组和排序的工作。
    当到达=1时,所有记录在统一组内排好序。

在这里插入图片描述

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。

  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。

    当gap == 1时,数组已经接近有序的了,再让gap等于1以后,

    就是前面未优化的直接插入排序方式排序一次,这样就会很快。

    这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定

《数据结构(C语言版)》— 严蔚敏

在这里插入图片描述

《数据结构-用面相对象方法与C++描述》— 殷人昆

在这里插入图片描述

  1. 稳定性:不稳定

代码实现:

void ShellSort(int* a, int n)
{int gap=n;while(gap>1){//gap=gap/2;//shell的方式gap=gap/3+1;//knuth的方式for (int i = 0; i < n-gap; i++) {//多次预排序+最后一次gap==1直接插入排序//预排序使得数组更加有序,后面直接排序便可加快效率int end=i;int x=a[i+gap];while(a[end]>x&&end>=0){a[end+gap]=a[end];end-=gap;}a[end+gap]=x;}}
}

选择排序

基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,

存放在序列的起始位置,直到全部待排序的数据元素排完 。

直接选择排序

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

  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换

  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,

    直到集合剩余1个元素

也就是说,每次遍历都选出最大或者最小的值放到一端,这样到最后就是有序的了。

在这里插入图片描述

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

代码实现:

void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){int mini = begin, maxi = begin;for (int i = begin; i <= end; ++i){if (a[i] < a[mini])mini = i;if (a[i] > a[maxi])maxi = i;}Swap(&a[begin], &a[mini]);// begin == maxi时,最大被换走了,修正一下maxi的位置if (begin == maxi)maxi = mini;Swap(&a[end], &a[maxi]);++begin;--end;}
}

这个排序整体来说是最差的排序方式,因为无论什么情况都是时间复杂度O(N2)

堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

前面我们讲述二叉树的时候就已经讲过这个排序方式了

链接:http://t.csdn.cn/LcTKm

在这里插入图片描述

直接选择排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

skills:

在这里建堆最好使用向下调整来建堆,如果使用向上调整来建堆,

建堆的时间复杂度就要O(N*logN),而向下调整时间复杂度为O(N),

这些都在http://t.csdn.cn/LcTKm讲过了。

交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置

交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序

在这里插入图片描述

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定
  • 简单来说:冒泡排序就是在[0,n-1]两两对比选出最大值放到a[n-1],

  • 再从[0,n-2]先选出最大值放到a[n-2],如此循环下去就排好序了。

  • 冒泡排序最好的情况是,数组本就是有序,此时经过一轮遍历后,

  • 没有发生交换,就可以判断是有序数组了,最好情况的时间复杂度为O(N),

  • 但是一般情况不会是这样,仍需要一个个选出最大值,时间复杂度为O(N2).

代码实现:

void BubbleSort(int* a, int n)
{int end = n;while (end > 0){int exchange = 0;for (int i = 1; i < end; ++i){if (a[i - 1] > a[i]){exchange = 1;Swap(&a[i - 1], &a[i]);}}--end;if (exchange == 0){break;}}
}

快速排序

  • 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法

  • 其基本思想为:任取待排序元素序列中的某元素作为基准值,

  • 按照该排序码将待排序集合分割成两子序列,

  • 左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,

  • 然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{if(right - left <= 1)return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int div = partion(array, left, right);// 划分成功后以div为边界形成了左右两部分 [left, div-1] 和 [div+1, right)// 递归排[left, div)QuickSort(array, left, div—1);// 递归排[div+1, right)QuickSort(array, div+1, right);
}

我们会发现,这很像二叉树的前序遍历,只不过要注意基准值的改变,以及边界的改变。

将其划分左右两部分的方式主要有两种:

hoare版本

在这里插入图片描述

思路:
取最左边的值为keyi,然后R往左走找到比a[keyi]小的值就停,

L往右走找到比a[keyi]大的值就停,然后交换两个值,

继续循环直到二者相遇值为x,把a[keyi]与a[x]交换,

自此keyi左边的值比a[keyi]小,keyi右边的值比a[keyi]大

代码实现:

int Partion1(int* a, int left, int right)
{int keyi=left;while(left<right){while(a[keyi]<=a[right]&&left<right)right--;while (a[keyi]>=a[left]&&left<right)left++;Swap(&a[left],&a[right]);}Swap(&a[keyi],&a[left]);return left;
}

挖坑法

在这里插入图片描述

思路:

与上面的思路相似,取最左边的值为keyi,

把a[keyi]备份,试想左边这个为坑

然后R往左走找到比a[keyi]小的值就把它放到坑中,

然后L往右走找到比a[keyi]大的值就把它放到坑中,

继续循环直到二者相遇值为x,把备份的a[keyi]放到坑中,

自此keyi左边的值比a[keyi]小,keyi右边的值比a[keyi]大

代码实现:

int Partion2(int* a, int left, int right)
{int key = a[left];int pivot = left;while (left < right){// 右边找小, 放到左边的坑里面while (left < right && a[right] >= key){--right;}a[pivot] = a[right];pivot = right;// 左边找大,放到右边的坑里面while (left < right && a[left] <= key){++left;}a[pivot] = a[left];pivot = left;}a[pivot] = key;return pivot;
}

前后指针版本

在这里插入图片描述

思路:

  • 设置最左值为key,再设置两个指针,一个在前为cur,一个在后为prev。

  • cur从第二个开始,prev从第一个开始,在前的cur开始往前寻找比key小的值。

  • 找到就停下来和prev+1位置的值交换,因为cur位置的值肯定比key大,

  • 而prev最初的位置是key,所以最后prev所在的位置的值必然是比key小的。

  • 停止的条件是什么呢?是cur指针超出了数组范围。

  • 就这样prev最后所在位置的值与key交换,这样就实现了key左边比key小,key右边比key大。

代码实现:

int Partion3(int* a, int left, int right)
{int keyi = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)//这个地方就比较巧妙,把++prev放在这里//为什么要判断++prev不等于cur呢?因为二者相同就没有交换的价值。{Swap(&a[cur], &a[prev]);}++cur;}Swap(&a[prev], &a[keyi]);return prev;
}

快速排序优化

  1. 三数取中法选key

三数指的是:a[0],a[(n-1)/2],a[n-1],由于a[key]的值是无法预料它的相对大小的,

当想要排升序时,如果是倒序有序的话,就是最坏的情况,

这时如果取首尾中间值为key则变成了最好的情况

代码实现:

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

这个函数可以放在上述所说的三种方法中,只需向其中加以下代码

int mini = GetMidIndex(a, left, right);
Swap(&a[mini], &a[left]);
  1. 递归到小的子区间时,可以考虑使用插入排序

递归可以想象成满二叉树的结构,越到后面需要递归的次数越多,这样

就使我们萌生一个想法,把后面几次的递归排序换成直接插入排序。

想法是好的,那么怎么实现呢?

这时候就要考虑最后是什么情况了,我们需要它数组递归到个数少的时

候就使用直接插入排序,代码如下:

void QuickSort(int* a, int left, int right)
{if (left >= right)return;// 小区间优化,当分割到小区间时,不再用递归分割思路让这段子区间有序// 对于递归快排,减少递归次数if (right - left + 1 < 10){InsertSort(a + left, right - left + 1);}//要注意这里插入排序的左值是数组开始的位置,//右值是需要排序的个数,参考上文讲述的插入排序函数的描述。else{int keyi = Partion3(a, left, right);// [left, keyi-1] keyi [keyi+1, right]QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}  
}

就这样,我们实现了快速排序的递归实现方法,最主要的是怎么确定key值,
并且使key左边和右边比key小或者大,最后使用递归的方式,
一层层缩小数组使其有序,最后整个数组有序,那么非递归怎么写呢?

快速排序的非递归

在这里我们需要用到一个数据结构----栈

栈的实现我们之前在这里讲过,这里直接使用它的代码就好

栈的实现链接:http://t.csdn.cn/cZzJh

怎么使用?

思路是这样的:

  • 首先将原数组的首末位置按照末先首后的顺序传入栈中

  • (栈的作用主要是记录边界),然后原数组按照上文讲过的让左边右边

  • 比key大或者小的方法把key放到合适的位置,然后把栈内的已经找到

  • key合适位置的边界删掉,之后把key+1和end的值传入栈,

  • begin和key-1的值传入栈,把这个边界里key放到合适的位置就删掉边界,

  • 继续传入key右边的边界和左边的边界,

  • 到了最后第一次的key前面的都有序了,

  • 这时开始key后面的边界开始按照前面所说的

  • 开始把key放到合适的位置,最后栈空了就结束了。

代码实现:

这里我们就只把所用到栈的函数拿出来,如果需要复现的可以到http://t.csdn.cn/cZzJh里把Stack.c和Stack.h文件复制放到源文件下。

void QuickSortNonR(int* a, int left, int right)
{ST st;StackInit(&st);StackPush(&st, left);StackPush(&st, right);while (!StackEmpty(&st)){int end = StackTop(&st);StackPop(&st);int begin = StackTop(&st);StackPop(&st);int keyi = Partion3(a, begin, end);// [begin, keyi-1] keyi [keyi+1, end]if (keyi + 1 < end){StackPush(&st, keyi+1);StackPush(&st, end);}if (begin < keyi-1){StackPush(&st, begin);StackPush(&st, keyi-1);}}StackDestroy(&st);
}

快速排序总结

  1. 在大部分场景下,快速排序的性能都是比较好的,所以才叫快速排序

  2. 时间复杂度:O(N*logN)

    在这里插入图片描述

  3. 空间复杂度:O(logN)

  4. 稳定性:不稳定

归并排序

基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

将已有序的子序列合并,得到完全有序的序列;

即先使每个子序列有序,再使子序列段间有序。

若将两个有序表合并成一个有序表,称为二路归并。

归并排序核心步骤:

在这里插入图片描述

在这里插入图片描述

思路:

  • 开辟一个新数组存重新排序的结果,原数组不断分半到最后只剩一个元素,
  • 再开始归并,两数比较,小的放前面,大的放后面,这一块就有序了,
  • 随着数组变大,数变多,依旧如此,大的放前面,小的放后面,
  • 一个数组没有数了就把另一个数组没有放的值按顺序放在后面即可。

图示:

由于代码过长不方便图示,故中间比较大小的就省去了,仅示例部分,后面类似

在这里插入图片描述

代码实现:

void _MergeSort(int* a, int left, int right, int* tmp)
{if (left >= right){return;}int mid = (left + right) / 2;// [left, mid] [mid+1, right] 有序_MergeSort(a, left, mid, tmp);_MergeSort(a, mid + 1, right, tmp);int begin1 = left, end1 = mid;int begin2 = mid+1, end2 = right;int i = left;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++];}// tmp 数组拷贝回afor (int j = left; j <= right; ++j){a[j] = tmp[j];}
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);if (tmp == NULL){printf("malloc fail\n");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

这就是归并排序的递归实现,其实还有一种非递归方法,

但是并不推荐使用,主要是使用时容易出错,

数组边界容易出问题,

但是还是在这里讲一下:

归并排序的非递归实现:

  • 通过观察数组可以发现,我们递归方法中分割的最后一步,

  • 把数组分成一个个单独的元素,

  • 但是数组里面我们可以把它看作是单个的元素,

  • 直接就是分的最后一步,开始归并就好了,

  • 那么该怎么归并呢?

  • 这里我们就要设置一个变量gap来控制,一次是几个元素进行归并,

  • 这一步是最主要的难点,然后就是这几个数进行归并,

  • 到了后面数不够了怎么办?比如说现在是八个数,

  • 先一一归,然后两两归,最后四四归,这是理想情况,

  • 但如果是九个数呢?最后还多了一个数怎么办?

  • 多的数和后面的归就越界了,排序结果肯定就不对了,

  • 这里所说的就是边界控制问题了,

  • 基于我的代码循环情况,越界情况有两种:

    1. 前一个右边界就越界,或者后一个的左边界就越界,那就不用再继续了,说明数组比较完了
    2. 后一个右边界越界,那就把它右边界设置为原来整个数组的右边界即可

代码实现:

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);if (tmp == NULL){printf("malloc fail\n");exit(-1);}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){// [i,i+gap-1] [i+gap,i+2*gap-1]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;// 核心思想:end1、begin2、end2都有可能越界// end1越界 或者 begin2 越界都不需要归并if (end1 >= n || begin2 >= n){break;}// end2 越界,需要归并,修正end2if (end2 >= n){end2 = n- 1;}int index = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}// 把归并小区间拷贝回原数组for (int j = i; j <= end2; ++j){a[j] = tmp[j];}}gap *= 2;}free(tmp);tmp = NULL;
}

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

计数排序

计数排序,顾名思义使用计数的方式进行排序,它是哈希直接定址法的变形应用。

步骤:

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

图示:

在这里插入图片描述

这里有一个问题,一定要从0开始吗?

如果有一组数是这样的[1000,1111,1022,1073,1288],给它排序难道要浪费前面一千个空间来记录吗?

当然不需要,像这种集中在一定区域的数,就把它最小的数放在0那,其他的数就映射到x-min上,与从0开始不同的是,这是相对的映射,而那是绝对映射。我们怎么确定要开辟多大的空间来存放数据呢?循环一遍找出最大最小值即可1.

代码实现:

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* count = (int*)malloc(sizeof(int)*range);memset(count, 0, sizeof(int)*range);if (count == NULL){printf("malloc fail\n");exit(-1);}// 统计次数for (int i = 0; i < n; ++i){count[a[i] - min]++;}// 根据次数,进行排序int j = 0;for (int i = 0; i < range; ++i){while (count[i]--){a[j++] = i + min;}}
}

计数排序总结:

因为计数排序的适用性不强,故不在后面总结中提及,此处仅作涉猎

  1. 在数据比较集中时效率很高,但是适用范围和场景有限,

    如果范围较大或者是浮点数就不适用了。

  2. 时间复杂度:O(max(N,range))

  3. 空间复杂度:O(范围)

  4. 稳定性:稳定

排序算法复杂度及稳定性分析

在这里插入图片描述

在这里插入图片描述

算法复杂度分析前文已经讲述过了,总结参考上图。

稳定性是什么呢?

  • 假定在待排序的记录序列中,存在多个相同的关键字的记录,
  • 若经过排序,这些记录的相对次序保持不变,
  • 就称这种算法是稳定的,否则是不稳定的。

那么前面讲过的排序方式哪些是稳定的、哪些是不稳定的呢.

插入排序:

  • 直接插入排序(稳定)

  • 希尔排序不稳定)

    在相同的值预排时可能分到不同的组里导致相对位置改变

选择排序:

  • 选择排序(不稳定)

    如果有两个相同的数,一个在首位,这时如果最小的数在另一个的右边,那么相对位置将会改变

  • 堆排序(不稳定)

    排升序时,如果最小的数在末尾,而最大数在前,不止一个,那么将最大的数替换后, 最大数的相对1位置也会发生改变

交换排序:

  • 冒泡排序(稳定)

  • 快速排序(不稳定)

    有相同元素时相对位置可能会发生变化

归并排序:

  • 归并排序(稳定)

  • 计数排序(不稳定)

    归并时,如果相同会先归左边的数,这样会导致相对位置改变

总结

这一篇我们讲述了八大排序算法,包括直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序,并且进行了算法复杂度和稳定性的分析。可是知道理论和死板的重现是远远不够的,还需要刷题来巩固,主要掌握的是这种思想。

respect !

下篇见!
在这里插入图片描述

相关文章:

详解八大排序算法

文章目录前言排序算法插入排序直接插入排序:希尔排序(缩小增量排序)选择排序直接选择排序堆排序交换排序冒泡排序快速排序hoare版本挖坑法前后指针版本快速排序的非递归快速排序总结归并排序归并排序的非递归实现&#xff1a;计数排序排序算法复杂度及稳定性分析总结前言 本篇…...

python库streamlit学习笔记

什么是streamlit&#xff1f; Streamlit是一个免费的开源框架&#xff0c;用于快速构建和共享漂亮的机器学习和数据科学Web应用程序。它是一个基于Python的库&#xff0c;专为机器学习工程师设计。数据科学家或机器学习工程师不是网络开发人员&#xff0c;他们对花几周时间学习…...

C/C++开发,无可避免的内存管理(篇一)-约束好跳脱的内存

一、养成内存管理好习惯 1.1 养成动态对象创建、调用及释放好习惯 开发者手动接管内存分配时&#xff0c;必须处理这两个任务。分配原始内存时&#xff0c;必须在该内存中构造对象&#xff1b;在释放该内存之前&#xff0c;必须保证适当地撤销这些对象。如果你的项目是c项目&am…...

在React项目中引入字体文件并使用

一、背景 设计稿里某些文字所用的字体&#xff0c;系统默认不支持。 比如设计需要的这个字体&#xff1a;EmerlandRegular&#xff0c;即使在css里将文字字体设置为他们&#xff0c;实际效果也显示不出来。 二、现象及原因 1、样式 2、期待效果 3、实际效果 实际上是因为这个…...

STM32 CubeMX按键点灯

本文代码使用 HAL 库。 文章目录前言一、按键原理图二、CubeMX 创建工程三、代码讲解&#xff1a;1. GPIO的输入HAL库函数&#xff1a;2. 消抖&#xff1a;3. 详细代码四&#xff0c;实验现象&#xff1a;总结前言 我们继续讲解 stm32 f103&#xff0c;这篇文章将详细 为大家讲…...

2023链动2+1模式到底是什么?带你了解核心规则

2023链动21模式到底是什么&#xff1f;带你了解核心规则 2023-02-24 梦龙 大家好&#xff0c;我是你们熟悉而又陌生的好朋友梦龙&#xff0c;一个创业期的年轻人 传统的直销模式产品低价高卖&#xff0c;消费者难以接受。虽然直销省去了传统流通渠道的中间环节&#xff0c;但…...

【Java面试八股文宝典之基础篇】备战2023 查缺补漏 你越早准备 越早成功!!!——Day14

大家好&#xff0c;我是陶然同学&#xff0c;软件工程大三今年实习。认识我的朋友们知道&#xff0c;我是科班出身&#xff0c;学的还行&#xff0c;但是对面试掌握不够&#xff0c;所以我将用这100多天更新Java面试题&#x1f643;&#x1f643;。 不敢苟同&#xff0c;相信大…...

K8S篇-搭建kubenetes集群

安装环境 这里使用pve虚拟机搭建三台centos机器&#xff0c;搭建过程参考: Centos篇-Centos Minimal安装 此次安装硬件配置 CPU&#xff1a;2C 内存&#xff1a;2G 存储&#xff1a;64G 环境说明 操作系统&#xff1a;Centos 7.9 内核版本&#xff1a;6.2.0-1.el7.elrepo…...

文本生成图像简述4——扩散模型、自回归模型、生成对抗网络的对比调研

基于近年来图像处理和语言理解方面的技术突破&#xff0c;融合图像和文本处理的多模态任务获得了广泛的关注并取得了显著成功。 文本生成图像&#xff08;text-to-image&#xff09;是图像和文本处理的多模态任务的一项子任务&#xff0c;其根据给定文本生成符合描述的真实图像…...

财务共享建设,为什么需要电子影像系统?

某集团作为投资性集团公司&#xff0c;业务遍布全国20多个省市&#xff0c;控股公司200余家&#xff0c;业务范围涉及火电、供热、风电、天然气天然气、水务、铁路、港口、酒店、地产等20多个细分行业。 伴随着集团企业的快速发展&#xff0c;某集团在管理中面临“点多、面广、…...

「RISC-V Arch」SBI 规范解读(下)

第六章 定时器扩展&#xff08;EID #0x54494D45"TIME"&#xff09; 这个定时器扩展取代了遗留定时器扩展&#xff08;EID #0x00&#xff09;&#xff0c;并遵循 v0.2 中定义的调用规约。 6.1 函数&#xff1a;设置定时器&#xff08;FID #0&#xff09; struct sbi…...

Android framework socketpair

简述 在Linux中&#xff0c;socketpair函数可以用于创建一对相互连接的、通信域为AF_UNIX的套接字&#xff0c;其中一个套接字可用于读取&#xff0c;另一个套接字可用于写入。可以使用这对套接字在同一进程内进行进程间通信&#xff08;IPC&#xff09;。 以下是使用socketp…...

腾讯在海外游戏和短视频广告领域的新增长机会

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 腾讯(00700)的收入在过去几个季度一直在下降&#xff0c;部分原因是由于新冠疫情导致的经济放缓以及中国监管机构对大型科技公司的监管收紧导致游戏行业萎缩造成的。 然而&#xff0c;猛兽财经认为&#xff0c;这些不利因素…...

查找该学号学生的成绩。

从键盘输入某班学生某门课的成绩&#xff08;每班人数最多不超过40人&#xff09;&#xff0c;当输入为负值时&#xff0c;表示输入结束&#xff0c;试编程从键盘任意输入一个学号&#xff0c;查找该学号学生的成绩。**输入格式要求&#xff1a;"%ld"(学号) "%l…...

为Webpack5项目引入Buffer Polyfill

前言 最近在公司的一个项目中使用到了Webpack5&#xff0c; 然而在使用某个npm包的时候&#xff0c;出现了Buffer is not defined 这个问题&#xff0c;原因很明显了&#xff0c;因为浏览器运行时没有Buffer这个API&#xff0c;所以需要为浏览器引入Buffer Polyfill. Webpack5…...

【人工智能 AI 】您可以使用机器人流程自动化 (RPA) 实现自动化的 10 个业务流程:Robotic Process Automation (RPA)

摘:人类劳动正在被机器(例如在工业中)或计算机程序(适用于所有行业)所取代。 目录 10 processes you can robotise in your company您可以在公司中实现自动化的 10 个流程 Human employees or robotic workers?人类员工还是机器人工人? Robots take over headhunting…...

VMware ESXi 8.0b - 领先的裸机 Hypervisor (Dell HPE Custom Image update)

本站发布 Dell 和 HPE 定制版 ESXi 8.0b 镜像 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-8/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;www.sysin.org 产品简介 VMware ESXi&#xff1a;专门构建的裸机 Hyper…...

Java:SpringBoot 整合Spring-Retry实现错误重试

SpringBoot 整合Spring-Retry可以实现错误重试 目录引入依赖开启spring-retry使用重试注解Retryable 注解Backoff 注解测试参考引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactI…...

MyBatis学习笔记(二) —— 搭建MyBatis项目

2、搭建MyBatis 2.1、开发环境 IDE&#xff1a;idea 2019.2 构建工具&#xff1a;maven 3.5.4 MySQL版本&#xff1a;MySQL 8 MyBatis版本&#xff1a;MyBatis 3.5.7 MySQL不同版本的注意事项 1、驱动类 driver-class-name MySQL 5版本使用jdbc5驱动&#xff0c;驱动类使用…...

linux服务器上Docker中安装jenkins

前言 Jenkins是开源CI&CD软件领导者&#xff0c; 提供超过1000个插件来支持构建、部署、自动化&#xff0c; 满足任何项目的需要。 本文主要提供通过docker安装jenkins镜像&#xff0c;并配置nginx反向代理页面配置和使用。通过jenkins完成项目的自动部署。 我在安装之前…...

自考都有哪些科目?怎么搭配报考?

第一次自考科目搭配 先报理论课&#xff0c;熟悉学习和考试套路 参考搭配模式&#xff1a; 一、全报考公共课 公共课难度较低&#xff0c;通过率高&#xff0c;复习起来比较轻松。对于不确定考什么专业&#xff0c;后期想换专业的同学&#xff0c;考过公共课&#xff0c…...

HIVE --- 高级查询

目录 CTE和嵌套查询 嵌套查询 关联查询&#xff08;join&#xff09; MapJoin MapJoin操作在Map端完成 开启MapJoin操作 MAPJOIN不支持的操作 union 数据交换&#xff08;import/export&#xff09; 数据排序 order by sort by distribute by cluster by CTE和嵌…...

【手撕源码】vue2.x双向数据绑定原理

&#x1f431; 个人主页&#xff1a;不叫猫先生 &#x1f64b;‍♂️ 作者简介&#xff1a;前端领域新星创作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab;系列专栏&#xff1a;vue3从入门…...

Allegro如何显示层叠Options和Find操作界面

Allegro如何显示层叠Options和Find操作界面 Allegro常规有三大操作界面,层叠,Options和Find,如下图 软件第一次启动的时候,三大界面是关闭的,下面介绍如何把它们打开,具体操作步骤如下 点击菜单上的View点击Windows...

【数据结构】双向链表

目录 数据结构之双向链表&#xff1a;&#xff1a; List.h List.c 1.创建返回链表的头结点 2.双向链表初始化 3.双向链表打印 4.双向链表销毁 5.双向链表尾插 6.双向链表尾删 7.双向链表头插 8.双向链表头删 9.双向链表查找 10.双向链表在pos前插入 11.双向链表删除pos位置 12…...

Editor工具开发基础三:自定义组件菜单拓展 CustomEditor

一.创建脚本路径 创建脚本路径不再限制 一般写在自定义组件类的下边二.特性CustomEditor 定义主设计图面由自定义代码实现数组的编辑器。两个构造函数1.public CustomEditor(Type inspectedType);2.public CustomEditor(Type inspectedType, bool editorForChildClasses);参数意…...

拒绝摆烂!神仙网站Python自学,一路从入门闯到最后,边学边玩

前言给大家推荐3个边玩边学python的网站在刚接触编程&#xff0c;培养对其持续的兴趣是最最重要的事情辣&#xff01;&#xff01;&#xff01;因为前期需要大量的基础代码知识积累&#xff0c;这个过程对于不少人来说还是挺枯燥的&#xff0c;很有可能学到一半就放弃了&#x…...

Linux基础命令-locate快速查找文件

文章​​​​​​​目录 locate 命令介绍 语法格式 基本参数 参考实例 1&#xff09;查找1.txt相关的文件 2&#xff09;查找包含pass和txt都有的文件 3&#xff09;只匹配文件名&#xff0c;有路径的情况下不进行匹配 4&#xff09;匹配不区分大小写的文件 5&#…...

揭穿数据分析的六大谎言

目前许多企业在决策时仍沿用以往的个人经验&#xff0c;没有用数据说话&#xff0c;这在实际决策运行时会出现很多问题。在数据分析行业发展成熟的国家&#xff0c;90%的市场决策和经营决策都是通过数据分析研究确定的。用数据说话&#xff0c;重视定量分析&#xff0c;也逐渐成…...

LinkSLA智能运维技术派-Redis的监控

Redis是一个开源&#xff0c;内存存储的数据服务器&#xff0c;可用作数据库、高速缓存和消息队列代理等场景。 首先我们对内存进行监控&#xff0c;主要指标如下&#xff1a; - used_memory:使用内存 - used_memory_rss:从操作系统分配的内存 - mem_fragmentation_ratio:内…...