【C/C++】排序讲解,C语言实现各种排序

news2024/11/18 5:53:21

这篇文章会从思路到实现到分析时间空间复杂度,一次性搞懂各种排序

有帮助的话点个赞收藏一下不迷路啊

 如果对时间空间复杂度还不熟悉的请去看

时间 空间复杂度

本文章不会讲堆排序,这部分内容会马上单写一篇博客介绍,和堆的和一些更复杂的问题写在一起(写完我会来贴链接~)

目录

1.插入排序

1.1直接插入排序

1.2希尔排序

2.选择排序

2.1直接选择排序

2.2堆排序

3.交换排序

3.1冒泡排序

3.2快速排序

4.归并排序


在介绍下面的代码之前,先进行一些准备工作,里面包括一些测试方法和性能比对

创建项目之后,把头文件(Sort.h),源文件(Sort.c),测试文件(test,c)都写好

 性能测试主要就是生成随机数然后用更多的测试样例测试运行时间

//性能测试
void Toptest()
{
	srand(time(0));
	const int N = 10000;
	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);
    int* a7 = (int*)malloc(sizeof(int) * N);
	if (a1 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}
	if (a2 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}if (a3 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}if (a4 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}if (a5 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}if (a6 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}if (a7 == NULL)
	{
		perror("malloc a fail");
		exit(-1);
	}

	for (int i = 0; i < N; i++)
	{
		a1[i] = rand()+i; //如果想生成连续的数字,直接写ai[i]=i;
		a2[i] = a1[i]; 
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[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();
	Bubblesort(a5, N);
	int end5 = clock();

	int begin6 = clock();
	Quicksort(a6, N);
	int end6 = clock();

    int begin7 = clock();
	Mergesort(a7, N);
	int end7 = 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("Bubblesort:%d \n", end5 - begin5);
	printf("Quicksort:%d \n", end6 - begin6);
    printf("Mergesort:%d \n",end7-begin7);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
    free(a7);
}

测试函数就是简单的自己开辟一块数组,然后再写一个打印函数

void Printf(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

头文件一定都要写在.h文件里

1.插入排序

 排序肯定不需要多介绍,插入排序就像是打牌的时候边摸排,边理牌,我们想把每个新摸到的手牌都按照顺序或者逆序放到之前的手牌里,这就是插入排序

1.1直接插入排序

直接插入就是用代码实现上面说过的过程,现在有一个数组a里面保存一些乱序数据,我们要做的就是从第二个元素开始,每一个元素都插入排序一遍

即,数组里第一个元素看成是有序的,后面每一个元素都使用插入排序

把要插入的元素保存在tmp,往前找比tmp里面元素更小的,如果比tmp保存的元素大,就往后挪动

void Insertsort(int*a ,int n) //a是一个数组指针,n是数据元素个数
{
	for (int i = 0; i < n - 1; i++)
	{
		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 + 1] = tmp;
	}
}

性能分析:

时间复杂度:O(N^2)

空间复杂度:O(1)  没有额外开口空间

稳定性(假设数组里面有两个5,排序之后是否还能保证两个5的相对位置):好(能保证两个5的相对位置)


1.2希尔排序

希尔排序分成两步,第一步通过gap,把间隔为gap的数据都排序好,最后再把所有的数据进行插入排序

这两步简记为

1.预排序(gap>1)

2.直接插入排序(gap == 1)

gap的选择很重要,直接关乎到排序的性能

先来看一下希尔排序应该怎么写

//希尔排序,如果是i++就叫gap并排
void Shellsort(int *a,int n)
{
	int gap = 3;
	for (int j = gap; j > 0; j--)
	{
		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;
		}
	}
}

细心的小伙伴就会发现其实就是把直接插入排序的1换成gap

事实证明gap=2或者gap=3都是相对最好的,3用的更普遍一些

或者还可以写成更灵活的(gap越来越小代表排序的区间越来越细化)

//gap>1预排序,gap == 1直接排序
void Shellsort2(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap /= 2; //或者gap=gap/3+1;保证一定最后是1
		for (int j = gap; j > 0; j--)
		{
			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;
			}
		}
	}
}

希尔的时间复杂度是很复杂的,一眼看过去就不是我们之前见过的金典模型

直接给出结论,在数据结构的权威大佬严蔚敏《数据结构(C语言版)》有写、

时间复杂度是O(N^1.3)

空间复杂度毫无质疑:O(1)

稳定性:不好,因为一样的数据可能被分到不同的组(每组间距是gap)


2.选择排序

思想:把数组头(begin)尾(end)数据看成就是最大和最小的,从中间遍历,如果有比a[begin]还小的数据直接交换(最好单独写一个交换函数,分装一下),同理有比a[end]还大的数据直接和a[end]交换

2.1选择排序

直接实现上面的思想就可以了

void Selectsort(int* a, int n) //时间复杂度是N^2
{
	int begin = 0;
	int end = n-1;
	while(begin<end)
	{
		for (int i = begin; i < end; i++)
	   {
		if (a[i] < a[begin])
		{
			Swap(&a[i], &a[begin]);
		}
		if (a[i] > a[end])
		{
			Swap(&a[i], &a[end]);
		}
	   }
		begin++;
		end--;
	}
	
}

性能分析:

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:不好

假设这样的情况

 最后两个5的顺序颠倒了所以是不稳定的

2.2堆排序


3.交换排序

基本思想:比较两个值然后进行交换

3.1冒泡排序

这个大家肯定最熟悉了

简单介绍一下 比较相邻的两个元素(按照顺序),如果大数在前就交换,否则比较下两个数字,直到没有数字需要比较

void bubblesort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = i; j < n - i; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:好(相等的元素相邻直接不需要交换就过去了)

3.2快速排序(快排)

大名鼎鼎的快排其实有很多的小细节和多种实现方法

叫做快排,但不是最快或者说不是方方面面最优的

1.Hoare的经典老办法

其实他的历史很悠久了,Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:

然后转化成子问题,这时候key就把整个数组分成了两段

[begin,key-1]  key  [key+1,end]  转化成子问题接着递归就好了

递归结束的条件就是begin>=end

void Quicksort(int* a, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	//减少递归调用的次数
	//可以进行区间优化,其实最后区间长度很小没必要用子问题,直接插入排序就行
	if ((end - begin + 1) < 5)
	{
		Insertsort(a, end - begin + 1);
	}
	else
	{
	int key = Part1Quicksort_Hoare(a, begin, end);
	Quicksort(a,begin,key-1);
	Quicksort(a ,key+1,end);
	}
}

写一个这样的函数,把不同的快排方法分装

int Part1Quicksort_Hoare(int*a,int begin,int  end)
{
	int mid = Getmid(a, begin, end);
	Swap(&a[begin], &a[mid]);
	int left = begin, right = end;
	int key = left;
	while (left < right)
	{
		while (a[right] > key && left < right)
		{
			right--;
		}
		while (a[left] < key && left < right)
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[key]);
	key = left;

	return key;
}

优化: 其实也都写在上面了,如果最后的区间分划到足够小就可以不用非得快排,可以用很简单的插入排序

if ((end - begin + 1) < 5)
	{
		Insertsort(a, end - begin + 1);
	}

优化:三数取中选key,保障key不是最值,排除有序数组的影响

//三数取中,保证key的数据不是最值,排除有序对快排的影响
int Getmid(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
		{
			return mid;
		}
		else
		{
			if (a[begin] < a[end])
				return end;
			else
				return begin;
		}
	}
	else
	{
		if (a[mid] > a[end])
			return mid;
		else
		{
			if (a[begin] > a[end])
				return end;
			else
				return begin;
		}
	}
}

2.挖坑法

把key的数据放在临时变量里面(用坑位保存)

right先走找小,走到比key小停止,把right的位置换成坑位,然后left找大,找到了还是和坑位交换,最后坑位就是right和left的碰头点,坑位和key交换相当于left和坑位交换,就是碰头点和坑位交换

//挖坑法写快排
int Partsort2_Quicksort_Hole(int* a, int begin, int end)
{
	int mid = Getmid(a, begin, end);
	Swap(&a[begin], &a[mid]);
	int left = begin;
	int right = end;
	int hole = left;
	int key = a[hole];
	while (left < right)
	{
		while (a[right] > a[key] && left < right)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (a[left] < key && left < right)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = a[key];
	return key;
}

3.双“指针”  其实不是真正的指针,就是标注下标而已

cur表示当前位置的下标,把所有比key小的都先++prev再交换,然后cur往后走最后

还是一样的把所有比key小的都折腾到前面去了

//这样写双指针
int Partsort3_Quicksort_double(int* a, int begin, int end)
{
	int key = begin;
	int prev = begin;
	int cur = prev++;
	while (cur <= end)
	{
		if (a[cur] < a[key])
		{
			Swap(&a[cur], &a[++prev]);
		}
		cur++;
	}
	Swap(&a[prev], &a[key]);
	key = prev;
	return key;
}

上面都是在递归的大框架下写的,但是要注意递归的空间复杂度是深不可测的,栈帧的开辟要谨慎,可能一不注意就栈溢出了

所以我们还可以非递归的方式

借助栈这个数据结构

(如果对栈有点陌生先去看一下我之前的博客吧:栈)

因为说白了你的子问题递归就是区间来回在传递在改变,所以只要解决了区间的问题递归与否无所谓

所以我们选择用栈来保存区间

注意:

如果是[1,2] [3,4]  两个区间想先拿出[1,2]就要后入而且是入2 1

因为要时刻考虑到栈的结构特点:先入后出

//快排还可以用非递归的方法写、
//注意我们是因为要带着区间范围这个参数才选择用子问题
//但是区间还可以用栈保存
void QuickNonR(int* a, int begin, int end)
{
	ST st; //需要拉进来栈的代码
	StackInit(&st);
	StactPush(&st, begin);
	StackPush(&st, end);
	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPpop(&st);
		int key = Partsort2_Quicksort_Hole(a, left, right);
		//如果按照前面的思路我还是想先排前面的区间就要先把后面的区间入栈
		//【left,key-1】key【key+1,right】
		if (key + 1 < right)
		{
			StackPush(&st, key + 1);
			StackPush(&st, right);
		}
		if(left < key - 1)
		{
			StackPush(&st, left);
			StackPush(&st, key - 1);
		}

	}
	StackDestory(&st);
}


其实上面的三种递归方法和一种非递归方法都是用key把区间分划成两段式

但是如果我的数据重合度很高(一万个2和一个4)这时候快排就会无敌慢 

为了解决这个大漏洞,有个天才想出了三段式的放法

//快排有一个优化的版本,当数据很多都等于key的时候老版本就会性能下降
void Quicksort_New(int* a, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	if ((end - begin + 1) < 5)
	{
		Insertsort(a, end - begin + 1);
	}
	else
	{
		int mid = Getmid(a, begin, end);
		Swap(&a[begin], &a[mid]);
		int left = begin;
		int right = end;
		int cur = begin + 1;
		int key = a[left];
		while (cur <= right)
		{
			if(a[cur]<a[left])
			{
				Swap(&a[cur++],&a[left++]);
			}
			if(a[cur]==a[left])
			{
				cur++;
			}
			if (a[cur] > a[right])
			{
				Swap(&a[cur], &a[right--]);
			}
		}
		//[begin,left-1] [left , right] [right+1,end]  其中,中间区域都是和key相等的
		Quicksort_New(a, begin, left - 1);
		Quicksort_New(a, right + 1, end);
	}
}

性能分析(递归的):

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

空间及复杂度:O(logN)

参考二叉树的结构一下就知道为什么是这个空间复杂度了

稳定性:不好


4.归并排序

 稳稳的老大哥

还是和上面写快排一样可以分割出去子函数,方便以后有新方法之后补充

void Mergesort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("Mergesort malloc fail");
		exit(-1);
	}
	//和上面的快排一样还是需要子函数
	_Mergesort(a, 0, n-1, tmp);

	free(tmp);
	tmp = NULL;
}

 每次分到足够小之后就取小的尾插到tmp这块空间,然后足够小的几个数排完之后就拷贝回去即可

//归并排序,条件:两端有序区间,方法:取小的尾插
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);
	//此时两个大区间都有序了,可以进行归并
	//由于变量之间会相互影响,最好还是重新定义一下
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int i = begin;
	while (begin1<=end1 && begin2<=end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i] = a[begin1++];
		}
		else
		{
			tmp[i] = a[begin2++];
		}
		i++; 
	}
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));

}

修正:

因为限制条件只有begin1不会越界,其他end1,begin2,end2都可能越界


归并排序如果是把越界的地方修正成不存在的区间那是不是相当于在这次循环里根本什么都没动


就还是不越界那部分原封不动拷贝到tmp数组,然后memcpy拷贝回去了
 

当然非递归也是可以的

//也可以用非递归的方式
void MergesortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("Mergesort malloc fail");
		exit(-1);
	}
	 //每组有rangeN个数据要进行归并
	for (int rangeN = 1; rangeN < n; rangeN *= 2)
	{
		for (int i = 0; i < n; i += rangeN * 2)
		{
			int begin1 = i;
			int end1 = i+rangeN-1;
			int begin2 = rangeN + i;
			int end2 = i+2*rangeN-1;
			//end1 begin2 end2 都越界
			//可以修正也可以直接break
			//修正出一段不存在的区间然后所有数据相当于没动
			//如果在当前的i,break出去相当于跳出了这个i的循环,这一段没拷贝但是其他正常的地方还是要拷贝的所以不能把memcpy写在循环的外面(整体拷贝)
			if (end1 >= n) 
			{
				end2 = n-1;
				end1 = n - 1;
				begin2 = n;
			}
			//或者直接break;
			/*if (end1 >= n)
			{
				break;
			}*/
			else if (begin2 >= n)
			{
				begin2 = n;
				end2 = n - 1;
			}
			else if(end2>=n)
			{
				end2 = n-1;
			}
			int j = i;
			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));

		}
	}

	free(tmp);
	tmp = NULL;
}

性能测试:

时间复杂度:O(NlogN)

空间复杂度:O(N)

稳定性:稳定


5.总结 

最后我们总结一下所有算法的性能


对你有帮助的话点个赞吧

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

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

相关文章

C++基础之核心3

C核心编程 本阶段主要针对C面向对象编程技术做详细讲解&#xff0c;探讨C中的核心和精髓。 1 内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的全局区&#xff1a;存放全局变量…

海格里斯HEGERLS标准解析|夹抱式四向穿梭车医用行业现代物流解决方案

众所周知&#xff0c;随着当前电商、医用、新零售等领域的快速发展&#xff0c;各大中小企业对于存储的要求越来越高&#xff0c;为让仓储货架、仓储设备、仓储配件等更具有行业的适配性&#xff0c;传统固定的穿梭车已不能满足对不同尺寸料箱的处理。为此&#xff0c;河北沃克…

二叉树题型

目录 二叉数遍历迭代法 1.1前序遍历 1.2中序遍历 1.3后续遍历 二叉树最小深度 二叉树所有路径 中序后序构造二叉树 验证二叉搜素树 二叉数遍历迭代法 1.1前序遍历 前序遍历顺序&#xff1a;根—左—右&#xff1b; 解法1&#xff1a;用栈来进行中间过程处理&#xf…

Babel和devServer | Webpack

文章目录Babel和devServerbabelbabel命令行使用babel-loaderVue源码的打包VSCode对SFC文件的支持vue-loaderdevServerBabel和devServer babel babel命令行使用 babel-loader Vue源码的打包 VSCode对SFC文件的支持 vue-loader devServer

一键可以轻松替换人物背景图,效果出乎意料的好(附 Python 代码)

最近发现 BackgroundMattingV2 项目的一些使用上的小缺陷&#xff0c;但是他却可以做到头发丝精细的抠图效果。我将项目稍微魔改了一下&#xff0c;让他在可以选择单一图片的基础上&#xff0c;可以把抠好的图片贴在自定义的背景图上&#xff0c;这样就可以让照片中的人物&…

使用Tomcat时出现Access Error: 404 -- Not Found的解决办法

当时出现这种情况很迷惑&#xff0c;错误提示如下 突然想到大二上学模电的时候安装过multisim&#xff0c;这个软件的某些不必要的服务占用了8080端口&#xff08;恼&#xff09;&#xff0c;出现这种情况应该是和Tomcat默认的端口冲突了。 于是就有了解决思路&#xff1a; …

stm32f407VET6 系统学习 day03 通用同步异步收发器

1.同步串行通信 同步通信发送端和接收端必须用共同的时钟源才能保持它们之间的准确同步。同步传输时&#xff0c;每个字 符没有起始位和停止位&#xff0c;它不是用起始位来标志字符的开始&#xff0c;而是用一串特定的二进制序列&#xff0c;称为 同步字符&#xff0c;去通知接…

[思维模式-14]:《复盘》-2- “知”篇 - 复盘之道

目录 前言&#xff1a; 一、U型学习法&#xff1a;复盘的学习机理 &#xff08;1&#xff09; 回顾、评估 &#xff08;2&#xff09;分析、反思 &#xff08;3&#xff09;萃取、提炼 &#xff08;4&#xff09;转化、应用 二、复盘与PDCA既有区别&#xff0c;也有联系…

solr 安装和使用

Solr是基于ApacheLucene构建的流行、快速、开源的企业搜索平台 Solr具有高度可靠性、可扩展性和容错性&#xff0c;提供分布式索引、复制和负载平衡查询、自动故障切换和恢复、集中配置等功能。Solr为许多世界上最大的互联网站点提供搜索和导航功能 环境准备 linux centos7 ja…

如何用iDesktop快速制作一幅研究区概况图

目录前言数据准备成果展示制作步骤前言 研究区概况图能直观展示研究区域的地理位置&#xff0c;在许多研究展示与论文撰写中必不可少。本文将以成都市为例&#xff0c;利用SuperMap桌面产品iDesktop快速制作一幅研究区概况图。 数据准备 四川省行政区划矢量数据&#xff08;…

【语音处理】LQ/QR噪声估计器研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

【Javascript】循环,函数,调用栈,闭包,递归

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录函数作用域例子调用栈可选参数闭包递归函数 形参不需要定义var&#xff0c;函数可以付给一个变量…

Android Qcom USB Driver学习(八)

该系列文章总目录链接与各部分简介&#xff1a; Android Qcom USB Driver学习(零) 因为要看usb charging的问题&#xff0c;所以需要补充一下battery的相关知识&#xff0c;算是入门吧 BAT SCH (1)VBATT_VSNS_P (2)BAT_THERM (3)I2C_SDA (4)I2C_SCL (5)VBATT_VSNS_M (1)BATT…

【问题分析】解决java中epoll依赖缺失问题

【问题分析】解决java中epoll依赖缺失问题一、前言二、问题描述三、问题分析四、解决方法五、总结一、前言 在学习使用lettuce框架实现UNIX域套接字unix domain socket连接redis时&#xff0c;遇到了一个问题&#xff0c;提示java.lang.IllegalStateException: A unix domain …

Java Stream后续来了,汇总一些项目开发中高频使用的 Stream操作

不过讲解这些操作时用的都是非常简单的例子&#xff0c;流操作的数据也都是简单类型的&#xff0c;主要的目的是让大家能更快速地理解 Stream 的各种操作应用在数据上后&#xff0c;都有什么效果。 在现实场景中实际做项目的时候&#xff0c;我们使用Stream操作的数据大多数情…

OpenCV颜色识别

颜色分辨 单个颜色识别 代码 import cv2 import numpy as npdef color(lower, upper, name):Img cv2.imread(image/origin/all.png) # 读入一幅图像kernel_3 np.ones((3, 3), np.uint8) # 3x3的卷积核if Img is not None: # 判断图片是否读入HSV cv2.cvtColor(Img, cv2…

maven中profiles使用详解,多环境开发配置文件(开发,测试,生产)+ pom中resources部分标签介绍

一.maven中profiles使用详解&#xff08;仅供参考&#xff09; 使用的场景 常常遇到一些项目中多环境切换的问题。比如在开发过程中用到开发环境&#xff0c;在测试中使用测试环境&#xff0c;在生产中用生产环境的情况。springboot中提供了 spring.profile.active的方式来实…

以mariadb为例介绍如何使用systemctl命令集设置服务开机自启动

以mariadb为例介绍如何使用systemctl命令集设置服务开机自启动一、systemd简介二、systemctl命令集常用命令三、以mariadb自启动为例四、更多说明一、systemd简介 systemd即为system daemon,是linux下的一种init软件,由Lennart Poettering带头开发,并在LGPL 2.1及其后续版本许…

[思维模式-13]:《复盘》-1- “知”篇 - 认识复盘

目录 前言 一、什么是复盘 二、复盘的三个关键词 三、复盘&#xff0c;而非总结 四、复盘的优势与局限 五、复盘与行动学习、培训、绩效改进的区别与联系 六、关于复盘的几个常见误解 误解1&#xff1a;可否对他人之事进行复盘 误解2&#xff1a;“项目后评估”是复盘吗…

细粒度图像分类模型(含实战代码)

来源&#xff1a;投稿 作者&#xff1a;lsc 编辑&#xff1a;学姐 理论部分 01细粒度图片分类问题 1.1细粒度图片分类特点 可判别区域往往只是在图像中很小的一块区域内。 1.2细粒度图像分类数据集 1.3细粒度图像分类竞赛 1.4细粒度图像分类模型分类: (1)强监督模型: 需要…