数据结构-排序

 这篇文章主要记录各种排序算法的思想及实现代码,最后对各种算法的性能进行了对比。

目录

排序的概念及其运用

排序的概念

排序运用

常见的排序算法

常见排序算法的实现

插入排序

基本思想

直接插入排序

希尔排序

选择排序

基本思想

直接选择排序

堆排序

 交换排序

基本思想

冒泡排序

快速排序

归并排序

基本思想

非递归实现

计数排序

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


排序的概念及其运用

排序的概念

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

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

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

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

排序运用

常见的排序算法

// 排序实现的接口
// 插入排序
void InsertSort(int* a, int n);
// 希尔排序
void ShellSort(int* a, int n);
// 选择排序
void SelectSort(int* a, int n);
// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);
// 冒泡排序
void BubbleSort(int* a, int n)
// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right);
// 快速排序挖坑法
int PartSort2(int* a, int left, int right);
// 快速排序前后指针法
int PartSort3(int* a, int left, int right);
void QuickSort(int* a, int left, int right);
// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
// 归并排序递归实现
void MergeSort(int* a, int n)
// 归并排序非递归实现
void MergeSortNonR(int* a, int n)
// 计数排序
void CountSort(int* a, int n)
// 测试排序的性能对比
void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand();a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();SelectSort(a3, N);int end3 = clock();int begin4 = clock();HeapSort(a4, N);int end4 = clock();int begin5 = clock();QuickSort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();MergeSort(a6, N);int end6 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);
}

常见排序算法的实现

插入排序

基本思想

直接插入排序是一种简单的插入排序法,其基本思想是:将待排序的序列按照值大小逐个插入到一个有序序列的合适位置中,直到这个待排序的序列被插完,就可以得到一个新的有序序列

实际上,我们在玩扑克牌时,也是利用直接插入排序的思想:

直接插入排序

当插入第i(i>=1)个元素时,前面的a[0]、a[1]、...a[i-1]已经排好序,让a[i]依次与a[i-1]、a[i-2]、...进行比较,找到插入位置将a[i]插入即可,原来位置上的元素依次后移。

其代码实现如下:

void InsertSort(int* a, int n)
{int i = 0;for (i = 0; i < n-1; i++){//[0,end] end+1int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}

在这段代码中,把[0,end]看成有序序列,将end+1插入到这个有序序列中。tmp用来保存待插入的值(end+1),因为在查到插入位置时,需要从有序序列最后一个元素开始向后挪,会把end+1处的值覆盖掉。当退出while(end >=0)循环时,有两种可能:第一,因为tmp>=a[end],此时找到了该插入的位置,即end+1;第二,因为end<0退出循环,这是由于原有序序列所有值都比待插入元素大,待插入元素需要插入到首元素位置处,首元素位置0,由于此时end=-1,待插入位置也end+1。因此,不管哪种情况,都需要将tmp插入到end+1位置处。

希尔排序

希尔排序其实是直接插入排序的plus版,主要分为两步:1.预排序,得到一个接近有序的序列;2.在对这个接近有序的序列进行直接插入排序。这样貌似看起来很麻烦,效率好像也不高,但是希尔排序的效率真的会让人眼前一亮

预排序

我们以上图序列为例,设定gap=3,即9、6、4、1为一组,8、6、3、0为一组,7、5、2为一组,分别对每个分组进行插入排序

在预排序时:

gap越大,大的值更快调到后面,小的值可以更快调到前面,但是越不接近有序。

gap越小,调的越慢,但是越接近有序。如果gap==1就是直接插入排序。

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

这段代码中,j==0是在为红色这一组插入排序,j==1是在为蓝色这一组插入排序,j==2是在为绿色这一组插入排序。这是在一组一组排,先排好红色一组,再排蓝色一组,最后排绿色一组。

然而,这段代码嵌套三层循环,是否可以精简一下呢?答案是肯定的!

改进如下:

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

改进在于for循环的递增改为i++,而不是i+=gap,在这种情况下,变成了多组并排。

直接插入排序

在进行完预排序后,已经使序列接近有序,再在这个基础上直接插入排序效率就会高很多。

在实际用希尔排序时,我们并不清楚序列到底有多少个数,因此gap不能为定值,下面是希尔排序的实现代码:

void ShellSort(int* a, int n)
{int i = 0;int gap = n;while (gap > 1){//gap = gap / 2;gap = gap / 3 + 1;for (i = 0; i < n - gap; i++){//[0,end] end+1int end = i;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

当gap>1时是预排序,目的是让数组接近有序;当gap==1时是直接插入排序,目的是让数组有序。

值得一提的是,希尔排序的时间复杂度不好计算,有人在大量实验基础上,给出希尔排序的时间复杂度大约为O(N^1.3)

选择排序

基本思想

每一次从待排序的数据元素中选出最小和最大的一个元素,分别存放在序列的起始位置和末尾位置,直到全部待排序的数据元素排完 。

直接选择排序

在元素集合a[0]--a[n-1]中遍历找出最大的和最小的元素下标,将最小元素和起始位置元素交换,将最大元素和末尾位置元素交换,然后在剩下的a[1]--a[n-2]中,重复上述步骤,直到集合中只剩下1个或者0个元素。直接选择排序的时间复杂度为O(N^2)。为下面是直接选择排序的代码实现:

void SelectSort(int* a, int n)
{int begin = 0;int end = n - 1;int i = 0;while (begin < end){int maxi = begin;int mini = begin;for (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]);if (begin == maxi)maxi = mini;Swap(&a[end], &a[maxi]);end--;begin++;}}

其中,

if (begin == maxi)maxi = mini;

这两行代码的作用是,如果序列中第一个元素是最大元素所在位置,而Swap(&a[begin], &a[mini]);将最小元素交换到了起始位置,那么找到的maxi不再是最大元素所在位置,需要调整maxi,因为最大元素被交换到了mini的位置,因此,让maxi=mini,这样再次让maxi为最大元素位置。

堆排序

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

堆排序在博主之前的文章介绍过-->堆排序

 交换排序

基本思想

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序

冒泡排序的主要思想是两两相邻的元素进行比较,这在博主之前的文章也详细介绍过:冒泡排序

快速排序

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

快速排序思想和二叉树前序遍历思想很相似,也是使用递归方式实现。

快速排序有几个版本:

1.【hoare版本

将最左边key作为基准值,先从右边开始找小(小于基准值的元素,并记录其下标),找到后再从左边找大(大于基准值的元素,并记录其下标),然后交换这两个元素,继续重复上述步骤,直到L和R相遇,相遇处的值一定小于基准值key,将相遇处的值和key交换,交换后,6在“中间”,6左边的值均小于6,6右边的值均大于6,也就是说,6排在了其该在的位置,接下来就要继续排6左边和6右边,使用递归实现即可。

实现代码如下:

int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;//begin end midi三个数选中位数if (a[begin] > a[midi]){if (a[midi] > a[end]){return midi;}else if (a[begin] > a[end]){return end;}elsereturn begin;}else{if (a[begin] > a[end]){return begin;}else if (a[midi] < a[end]){return midi;}elsereturn end;}
}
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin;int right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;//[begin,keyi-1] keyi [keyi+1,end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}

将相遇处的值和key交换,这默认相遇位置的值比key小,那么为什么相遇位置的值比key小因为右边先走!换言之,如果左边先走,那么相遇位置的值不一定比key小。具体原因是:

相遇有两种情况:

1.R遇L:R一直走,直到遇到L,相遇位置是L,L位置比key小。

2.L遇R:R先走,找到小的停下来,L找到,没有找到,遇到R停下来了,相遇位置是R,比key小。

因此,相遇位置的值一定比key小。

值得注意的是,上段代码有这样几行:

int midi = GetMidi(a, begin, end);
Swap(&a[midi], &a[begin]);

这几行代码的含义是,如果序列本身就是有序的,那么当使用快速排序时,会开辟序列元素个数个函数栈帧,导致栈溢出;当加上这几行代码后,序列最左边的值就不是最小的,因此只会开辟logN个栈帧,几乎不会导致栈溢出。

那么我们再来看另一个问题,叫做小区间优化,我们知道,快速排序是利用递归,利用了二叉树的思想,那么二叉树的最后三层占了总结点的87.5%,同样地,在快速排序时,为了让最后几层有序,递归了很多次,付出了很大代价,那有没有其他解决方案?有的!在最后几层递归时,我们可以采用其他排序,推荐插入排序(适应性强)。

 实现代码如下:

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;//小区间优化if (end -  begin + 1 < 10){InsertSort(a + begin, end - begin + 1);}else{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin;int right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;//[begin,keyi-1] keyi [keyi+1,end]QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);}
}

虽然小区间优化听起来很牛,但是在Release状态下,会有一些优化效果,但是可能不明显,因为此状态下对建立递归栈帧已经优化到足够小了。

2.【挖坑法

在上面的hoare方法实现中,需要注意到很多问题,一不小心就会出错,那么,针对这些问题,有人提出对hoare方法的改进--挖坑法

挖坑法的思想和hoare法很相近:

 把左边第一个值临时保存到key中,形成坑位,然后从右边end开始找小于key的值,当找到以后,把小值放到坑位中,然后小值的位置就形成了新的坑位,

 接着从左边begin开始找大于key的值,当找到以后,把大值放到刚才的坑位中,进而形成新的坑位,继续重复以上步骤,直到begin和end相遇,相遇时肯定在坑位上,然后把key放到begin和end相遇位置,此时,左边都是小于key的,右边都是大于key的,然后左边和右边分别作为新的序列递归。

实现代码如下:

int PartSort2(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int key = a[begin];int holei = begin;while (begin < end){//右边找小,填到左边的坑while (begin < end && a[end] >= key){end--;}a[holei] = a[end];holei = end;//左边找大,填到右边的坑while (begin < end && a[begin] <= key){begin++;}a[holei] = a[begin];holei = begin;}a[holei] = key;return holei;
}void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int keyi = PartSort2(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

挖坑法相对于hoare写起来更不容易犯错,也更好理解,因此,在没有要求的情况下,推荐使用挖坑法!

3.【前后指针版本

这种方法和前面两种思路上会有很大不同,但是相对hoare来说,也是容易理解,也不容易犯错,因此,这种方法也很推荐

这种方法是思想是,创建前后两个指针prev和cur,初始时,prev指针指向序列开头,cur指针指向prev指针的后一个位置,

1.当cur遇到比key大的值,++cur

2.当cur遇到比key小的值,++prev,交换prev和cur位置的值,++cur

直到cur>end结束。

实现代码如下:

int PartSort3(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int keyi = begin;int cur = begin + 1;int prev = begin;while (cur <= end){//cur找小//注释掉的这种情况可能存在自己和自己交换的情况/*if (a[cur] < a[keyi]){prev++;Swap(&a[prev], &a[keyi]);}*/if (a[cur] < a[keyi] && prev++ != cur)Swap(&a[prev], &a[cur]);cur++;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int keyi = PartSort3(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

以上三种方式是使用递归来实现快速排序,那么,可以使用非递归实现快速排序吗?可以!使用非递归形式实现快速排序,不是递归,胜似递归

但是,要需要借助栈

先把0  9压栈,再出栈,形成两部分0  4和6  9,再将0  4和6  9压栈,...,当栈为空时就结束,如图所示。

实现代码如下:

void QuickSortNonR(int* a, int begin, int end)
{ST s;STInit(&s);STPush(&s, end);STPush(&s, begin);while (!STEmpty(&s)){int left = STTop(&s);STPop(&s);int right = STTop(&s);STPop(&s);int keyi = PartSort3(a, left, right);//[left ,keyi-1] keyi [keyi+1,right]if (left < keyi - 1){STPush(&s, keyi - 1);STPush(&s, left);}if (keyi+1 < right){STPush(&s, right);STPush(&s, keyi+1);}}
}

归并排序

基本思想

基本思想:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

 如果两个子序列是有序的,那么为了使将这个两个子序列合并成一个有序序列,只需要取这两个子序列中小的值尾插即可,最终将排序好的序列拷贝到原数组。

实现代码如下:

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (begin + end) / 2;//[begin,mid][mid+1,end]_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, sizeof(int) * (end - begin + 1));
}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);
}

归并的缺点在于需要O(N)的空间复杂度,时间复杂度为O(N*logN)。

非递归实现

在非递归实现时,每个元素都是有序的,一个和一个有序归成两个,两个和两个有序归成四个,四个和四个有序归成八个。

实现代码如下:

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){int i = 0;printf("gap:%d->", gap);for (i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//[begin1,end1][begin2,end2]归并printf("[%2d,%2d][%2d,%2d] ", begin1, end1, begin2, end2);//边界的处理if (end1 >= n || begin2>=n){break;}if (end2 >= n){end2 = n - 1;}int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));}printf("\n");gap *= 2;}free(tmp);
}

在上面的代码中,设置一个gap,表示归并的每组数据有几个,刚开始gap=1,表示归并的每组数据有一个,之后gap=2,表示归并的每组数据有两个,最后gap=4,表示归并的每组数据有四个。

在上面的这段代码中,有这样几句:

//边界的处理
if (end1 >= n || begin2>=n)
{break;
}
if (end2 >= n)
{end2 = n - 1;
}

这几句代码是在处理最后归并的一组越界的情况,如果end1>=n,那么begin1和其之后不越界的元素肯定是有序的,直接break跳出;如果begin2>=n,那么直接break跳出,此时的begin1和end1之间也已经是有序的;如果end2>=n,那么begin1-end1和begin2和其之后不越界的元素需要归并,但是end2要改为n-1。

接下来,我们了解一下内排序外排序

内排序,就是在内存中进行排序;外排序就是在硬盘中进行排序。上图中的几种排序都是内排序,但是归并排序同时也是外排序。

假设我们有100亿个整数,大约40G大小的文件,那么对这40G文件如何排序呢?利用归并排序,有这样一种思路:每次读1G数据到内存,然后快速排序,写到一个小文件中,最后得到40个1G的小文件,然后对这40个文件两两归并,得到20个文件,依次类推,最终得到一个大文件,这个文件是有序的。

因此,在实际中,往往在硬盘中排序时,才会用到归并排序。

计数排序

这是一种非比较排序,即不需要比较元素大小,只需要统计相同元素出现次数。

例如,遍历一遍数组a,a中1出现了3次,那么count中下标为1的位置就是3,2出现了1次,那么count中下标为2的位置就是1,...,9出现了1次,那么count中下标为9的位置就是1,

for(int i=0;i<n;i++)
{count[a[i]]++;
}

然后根据count中记录的结果,1位置是3,那么写3次1到a,2位置是1,那么写1次2到a,3位置是2,那么写2次3到a,...,得到下面的结果

特点:效率极高,时间复杂度O(aN+countN(范围)),空间复杂度O(countN(范围))

局限性:1.不适合分散的数据,更适合集中数据2.不适合浮点数、字符串、结构体数据排序,只适合整数。

那么,现在有这样一个问题,假设我们要排的数据是1000 1999 1888...(在1000-1999之间),那么我们仍需要开辟0-999这么多的空间,很浪费,如下图

那么,我们变通一下,做一个相对映射:

也就是说,1000在0位置处,1005在5位置处,极大节约了空间。

代码实现如下:

void CountSort(int* a, int n)
{int min = a[0], max = a[0];for (int i = 0; i < n; i++){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if (count == NULL){perror("calloc fail");return;}//统计次数for (int i = 0; i < n; i++){count[a[i] - min]++;}//排序int i = 0;for (int j = 0; j < range; j++){while (count[j]--){a[i++] = j + min;}}}

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

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

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

相关文章

C++设计模式-- 2.代理模式 和 外观模式

文章目录 代理模式外观模式角色和职责代码演示一&#xff1a;代码演示二&#xff1a;外观模式适用场景 代理模式 代理模式的定义&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xff0c;一个对象不适合 或不能直接引用另一个对象&#xff0c;而代…

git推送前HOOK pre-push判断版本号增加再推送

我写了一个C程序&#xff0c;然后每次编译后&#xff0c;记不得当时的版本是哪个源代码&#xff0c;对于core dump复现调试很麻烦。 为此我想了一个办法&#xff0c;首先在include/git_version.hpp 创建一个版本的文件 /** Description:* Author: jiangsheng* Date: 2024-01-…

数据结构与算法:快速排序

数据结构与算法&#xff1a;快速排序 快速排序荷兰国旗问题霍尔版本递归优化小区间优化 PartSort优化三数取中 挖坑法前后指针法 非递归法 快速排序 荷兰国旗问题 想要理解快速排序&#xff0c;就先理解这个问题&#xff1a; [LeetCode75.颜色分类] 荷兰国旗是由红白蓝三色组…

10.云原生之在线开发调试

云原生专栏大纲 文章目录 vscode-server介绍VSCode Server 和云开发结合vscode-server安装code-server安装插件在线安装插件离线安装插件安装中文插件 配置开发环境在容器中安装开放环境Dockerfile制作镜像 git拉取项目 vscode-server介绍 VSCode Server&#xff08;Visual S…

动态内存面试的经典题目

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

js includes方法的使用

JavaScript中的数组includes()方法用于判断数组中是否包含指定的元素&#xff0c;并返回一个布尔值。 includes()方法的语法如下&#xff1a; array.includes(searchElement[, fromIndex]) 其中&#xff0c;array是要进行搜索的数组&#xff0c;searchElement是要搜索的元素…

网络安全中的“三高一弱”和“两高一弱”是什么?

大家在一些网络安全检查中&#xff0c;可能经常会遇到“三高一弱”这个说法。那么&#xff0c;三高一弱指的是什么呢&#xff1f; 三高&#xff1a;高危漏洞、高危端口、高风险外连 一弱&#xff1a;弱口令 一共是4个网络安全风险&#xff0c;其中的“高危漏洞、高危端口、弱…

电脑怎么录制屏幕?看这一篇就够了

在数字时代&#xff0c;电脑屏幕录制已经成为人们日常生活中一个越来越重要的工具。无论是录制在线课程、游戏精彩时刻&#xff0c;还是远程会议、软件演示&#xff0c;屏幕录制都可以帮助我们更好地保存和分享这些信息。可是您知道电脑怎么录制屏幕吗&#xff1f;本文将介绍两…

GATHER_DATABASE_STATS 的常见问题及 权限 ANALYZE ANY

​​​​​​光有执行DBMS_STATS的权限还是不够的 DBMS_STATS.GATHER_DATABASE_STATS (ESTIMATE_PERCENT > 30); * ERROR at line 1: ORA-20000: Insufficient privileges to analyze an object in Database ORA-06512: at "SYS.DBMS_STATS", line 13323 ORA-0651…

旧电脑追加内存条

内存条基本知识 DDR4 2666 DDR&#xff08;Double Data Rate&#xff09;双倍速率 4 第四代 2666 内存主频2666MHz 内存时序 内存的延迟时间 传输带宽 MB/s 内存和CPU之间的传输速度 针脚数 数字-PIn 288-PIN就是288个针脚 选购内存条 …

【国产mcu填坑篇】华大单片机(小华半导体)一、SPI的DMA应用(发送主机)HC32L136

最近需要用华大的hc32l136的硬件SPIDMA传输&#xff0c;瞎写很久没调好&#xff0c;看参考手册&#xff0c;瞎碰一天搞通了。。。 先说下我之前犯的错误&#xff0c;也是最宝贵的经验&#xff0c;供参考 没多看参考手册直接写&#xff08;即使有点烂仍然提供了最高的参考价值。…

iis配置asp网站

1.安装IIS的ASP win7和win10都是一样的 下安装IIS时ASP一般被默认不选中的状态&#xff0c;因此需要打开IIS检查功能视图栏中是否存在ASP选项&#xff0c;若没有则需要从控制面板->程序和 功能->打开或关闭Windows功能->Internet信息服务->万维网服务->应用程序…

数环通更新动态|新增连接器抖店自建、叮当OKR、千易ERP、货拉拉

更新快速预览 新增连接器4个 抖店自建 叮当OKR 千易ERP 货拉拉 应用更新2个 百度统计&#xff08;2&#xff09; 旺店通&#xff08;1&#xff09; 应用连接器 新增连接器 1.抖店自建 抖店是抖音官方打造的电商商家实现一站式经营平台&#xff0c;为商家提供全链路服务&#xf…

Selenium定位元素的方法css和xpath的区别

selenium是一种自动化测试工具&#xff0c;它可以通过不同的定位方式来识别网页上的元素&#xff0c;如id、name、class、tag、link text、partial link text、css和xpath。 css和xpath是两种常用的定位方式&#xff0c;它们都可以通过元素的属性或者层级关系来定位元素&#…

MacOS环境下Kali Linux安装及使用指导

Kali Linux是一个开源的、基于Debian的Linux发行版&#xff0c;面向各种信息安全任务&#xff0c;如渗透测试、安全研究、计算机取证和逆向工程&#xff0c;是最先进的渗透测试发行版&#xff0c;它的前身是BackTrack。 1. 我们为什么要用Kali Linux 由于Kali Linux具有以下特…

【脑筋急转弯系列】乒乓球称重问题

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

SpringBoot+Vue实现一个系统登录功能

1. 创建一个Spring Boot项目&#xff0c;添加Web和Security依赖。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>2. 创建一个User实体类&#xff0c;用…

vue render函数的使用场景

render函数使用和详解_render函数如何使用-CSDN博客 上面链接写的很详细&#xff0c;有不懂的地方可以留言互相交流哈&#xff01;~~~~~~

蓝桥杯简介

蓝桥杯全国软件和信息技术专业人才大赛是由工业和信息化部人才交流中心举办的全国性IT学科赛事。这个比赛在全国范围内有着广泛的影响力&#xff0c;参赛人数已经累计超过40万人&#xff0c;涵盖了全国1200余所高校。在高校教育教学改革和创新人才培养方面&#xff0c;蓝桥杯大…

FDTD2018a安装问题记录

FDTD2018a安装问题记录 目录问题解决方案 目录 问题 解决方案 电脑名字如果是中文改成英文