数据结构:排序

news2024/11/15 21:25:01

目录

 插入排序

插入排序

希尔排序

 选择排序

选择排序

堆排序

交换排序

冒泡排序

快速排序

递归实现:

●hoare版本

●三数取中+小区间法优化

●挖坑版本

●双指针版本

非递归

●用栈实现

●用队列实现

归并排序 

● 递归

●非递归

总结


来了朋友,等你很久了,是不是很孤独,没人陪你聊天。快一起学习!!!

前言

排序的运用场景非常广泛,在我们的日常生活中经常出现,例如:根据销量排名选择店家购物,期末成绩排名,全国高校排名等等。如下图:不同的排序方法都有自己的特点,接下来就一起感受排序的魅力吧!

 插入排序

插入排序

排序思想:把待排序的值,插入到一个已经排好序的有序序列中,得到一个新的有序序列。简单的理解就是,假设在你面前有一个身高从矮到高的队列,现在要将你插入到队列当中,插入后保持队列仍然是从小到大。

思路分析:

还是以站队为例,原队列中有n个人,那么我就是第n+1个。我和队尾的个子最高的比较一下身高,如果我比他高,那么直接排在队尾。反之,他来到n+1的位置,我继续和前面的同学进行比较,直至我不在低于前面的同学。一次插入就完成了!

关于边界问题,视频中最后库里和哈登比较完后,end的位置应该更新到下标为-1的位置,end+1也就是0的位置,最后前面没有人可以比较,库里插入到end+1的位置。视频中没有体现出来,希望不要被误导。

代码分析:

定义一个变量tmp记录end+1位置(新插入的数据)的值,如果tmp的值小于end位置的值,那么end的值就向后挪动,并且更新end的位置,下次tmp要继续和前面的值比较。循环结束的条件是end>=0,如果tmp找到了位置,直接跳出循环。如图:一次插入的过程

代码实现:

//插入排序
void InsertSort(int* arr, int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end>=0)
		{
			if (tmp < arr[end])
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

希尔排序

排序思想:

先选定一个整数(gap),把待排序数据分成gap组,所有距离为gap的数据分在同一组内,并对每一组内进行排序。gap按照特定的规则减少,重复上述分组和排序的工作。当到达gap=1时,完成排序。

希尔排序是对插入排序的优化。插入排序的长处是处理接近有序的数据,所以在插入排序的基础上用gap将数据分组处理。当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,也就是直接插入排序,这时数组已经接近有序了。

 代码实现:

//希尔排序
void HellSort(int* arr, int sz, int gad)
{
	while (gad > 1)
	{   //gad > 1 预排序
		//gad = 1, 插入排序
		gad = gad / 3 + 1;
			for (int i = 0; i < sz - gad; i++)
			{
				int end = i;
				int tmp = arr[end + gad];
				while (end >= 0)
				{
					if (tmp < arr[end])
					{
						arr[end + gad] = arr[end];
						end -= gad;
					}
					else
					{
						break;
					}
				}
				arr[end + gad] = tmp;
			}
	}
	
}

 选择排序

选择排序

排序思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置(或

者末尾),直到所有数据排完 。在排序思想的基础上做下优化,一次遍历找到数据中最大和最小的两个值。

 第一趟排序找到最大的和最小的,mini = 2,maxi = 4;大的值交换值队尾,小的值交换值队首。下次排序的区间减小。

第二趟找到次大的和次小的,mini = 3,maxi = 1;大的值交换值队尾,小的值交换值队首。下次排序的区间减小。

 第三趟,mini = 4,maxi =2。大的值交换值队尾,小的值交换值队首。

需要注意的就是,找到大小两个数据的下标后,maxi 可能和 begin相等,正如上图中的第二趟,最高的是科比,maxi和begin相等,这种情况需要特殊处理一下。

 这种情况下,第一次begig和mini的值交换后arr[begin] = 0,arr[mini] = 9。这时arr[end]和arr[maxi]交换的时候,又把两个值给换回去了。所以要判断下这种情况,maxi 等于 begin的时候 maxi的值改为mini的值。(处理方法不唯一,不要忘记这种情况就好)。

 代码实现:

交换后面也会用到,这里封装成一个函数。

//交换
void Swap(int* e1, int* e2)
{
	int tmp = *e1;
	*e1 = *e2;
	*e2 = tmp;
}
//选择排序
void SelectSort(int* arr, int sz)
{
	int begin = 0;
	int end = sz - 1;
	while (begin < end)
	{
		int mini = begin;
		int maxi = begin;
		for (int i = begin+1; i <= end; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}
		//交换
		Swap(&arr[begin],&arr[mini]);
		if (maxi == begin)
		{
			maxi = mini;
		}
		Swap(&arr[end],&arr[maxi]);
		end--;
		begin++;
	}
}

堆排序

堆排序在上篇文章中介绍过了,这里不在重复。

建堆的过程注意升序建大堆,降序建小堆。

代码实现:

//向下调整
void AdjustDown(int* arr,int parent,int sz)
{
	int chlid = parent * 2 + 1;
	while (chlid < sz)
	{
		if (chlid + 1 < sz && arr[chlid+1]> arr[chlid])
		{
			chlid = parent * 2 + 2;
		}
		if (arr[chlid] > arr[parent])
		{
			//交换
			Swap(&arr[chlid],&arr[parent]);
			//迭代
			parent = chlid;
			chlid = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//建堆算法
void HeapCreate(int* arr, int sz)
{
	for (int i = ((sz - 1) - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr,i,sz);
	}
}
//堆排序
void HeapSort(int* arr, int sz)
{
	//建堆
	HeapCreate(arr,sz);
	//PrintSort(arr, sz);
	//升序
	int end = sz - 1;
	while (end)
	{
		//交换
		Swap(&arr[0],&arr[end]);
		//向下调整
		AdjustDown(arr,0,end);
		end--;
	}
}

交换排序

冒泡排序

排序思想:根据序列中相邻两个值的比较结果来对换这两个值在序列中的位置。将较大的向序列的尾部移动,将较小的向序列的前部移动。(升序)

代码实现:

//冒泡排序
void BubbleSort(int* arr, int sz)
{
	//总趟数
	for (int i = 0; i < sz - 1; i++)
	{
		//一趟
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				//交换
				Swap(&arr[j],&arr[j+1]);
			}
		}
	}
}

快速排序

1960年Tony Hoare发布了快速排序算法(Quick Sort),这个算法也是当前世界上使用最广泛的算法之一。

排序思想:

选取一个基准值,将待排序集合分割成两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,这就划分成了子问题,左右子序列重复该过程,直到所有元素都排列在相应位置上

递归实现:

●hoare版本

分析:
取begin位置的值为基准值,left和right记录第一个数据和最后一个数据的位置。假设right先向左走,遇到小于基准值的数据停下来。接着left向右移动,找到比基准值大的数据停下来。left和right位置的值交换一下,重复该过程,直至left和right的位置相遇。
它们相遇的位置一定小于基准值。如果是right停止(right找到的值是比基准值小的),left停止,left停下来后要和right位置交换值,此时left位置的值也小于基准值。

画图分析:

初始状态

代码实现:

//快速排序
void QuickSort(int* arr, int begin, int end)
{
	
	if (begin >= end)
	{
		return;
	}

	int left = begin;
	int right = end;

	int key = left;
	while (left < right)
	{
		//右边先走,找小
		while (left<right && arr[right] >= arr[key])
		{
			right--;
		}
		while(left < right && arr[right] <= arr[key])
		{
			left++;
		}
		Swap(&arr[left],&arr[right]);
	}
	Swap(&arr[left],&arr[key]);
	key = left;

	QuickSort(arr,begin,key-1);
	QuickSort(arr,key+1,end);
}

三数取中+小区间法优化

三数取中:这项优化主要是针对基准值的选择,当基准值在整个数据中偏大或者偏小都会降低快排的性能,所以在选择基准值的时候,在三个数据中选择中间大的一个作为基准值,在一定程度上达到了优化

//三数取中
int GetMiddle(int* arr,int begin, int end)
{
	int mid = (begin + end) / 2;
	if (arr[begin] > arr[mid] )
	{
		if (arr[mid] >arr [end])
		{
			return mid;
		}
		else if (arr[end] > arr[begin])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
	else
	{
		if (arr[mid] < arr[end])
		{
			return mid;
		}
		else if (arr[end] < arr[begin])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}

小区间法优化:

这项优化是考虑到当数据的数量较少的时候,继续递归快排效率不是很高,所以当区间较小的时候,可以考虑用插入排序直接解决。

优化后的快排:

优化后
void QuickSort(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//小区间优化
	if ((end - begin + 1) < 15)
	{
		InsertSort(arr,end-begin+1);
	}
	else
	{
		//三数取中优化
		int mid = CetMiddle(arr, begin, end);
		Swap(&arr[begin], &arr[mid]);

		int left = begin;
		int right = end;

		int key = left;
		while (left < right)
		{
			//右边先走,找小
			while (left < right && arr[right] >= arr[key])
			{
				right--;
			}
			while (left < right && arr[left] <= arr[key])
			{
				left++;
			}
			Swap(&arr[left], &arr[right]);
		}
		Swap(&arr[left], &arr[key]);
		key = left;

		QuickSort(arr, begin, key - 1);
		QuickSort(arr, key + 1, end);
	}
}

挖坑版本

思路:保存基准值,基准值的位置变成坑位。假设right先向左走,找到小于基准值的数停下来,将right位置的值填入坑位,right位置变成新坑位。left向右找到大于基准值的数据停下来,left位置的值填入坑位,left位置变成新坑位。left和right相遇的地方填入基准值,坑位左边的小于基准值,右边大于基准值。

画图分析:

代码实现:

//挖坑法
void HoleSort(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int left = begin;
	int right = end;
	int key = arr[left];
	//坑位
	int hole = left;
	while (left < right)
	{
		//右边先走,找小
		while (left < right && arr[right] >= key)
		{
			right--;
		}
		Swap(&arr[hole],&arr[right]);
		//新坑位
		hole = right;
		//左边找大
		while (left < right && arr[left] <= key)
		{
			left++;
		}
		Swap(&arr[hole],&arr[left]);
		//新坑位
		hole = left;
	}
	//坑位的位置
	arr[hole] = key;
	//[begin,hore-1]hore[hore+1,end]
	HoleSort(arr, begin,hole-1);
	HoleSort(arr,hole+1,end);
}

双指针版本

思路:key记录基准值的位置,prev和cur一前一后。当cur的位置越界时,一趟排序结束。cur从左边开始向右走,当cur位置的值比基准值小的时候,和++prev位置的值交换,这样是保证prev位置和以前位置的值都是小于基准值的。重复此过程直至cur遍历完所有元素,这时prev位置的值和基准值交换。这样prev以前的值都是小于基准值的。

画图分析:

 

代码实现:

void DoublePointer(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	int cur = begin + 1;
	int prev = begin;
	int key = begin;

	while (cur <= end)
	{
		//cur的值比key小,保证prev之前的值全部是比基准值小的
		if (arr[cur] < arr[key] && ++prev != cur)
		{
			Swap(&arr[prev],&arr[cur]);
		}
		cur++;
	}
	//prev位置的值是小于key的
	Swap(&arr[prev],&arr[key]);
	//新区间【begin ,prev-1】【prev+1,end】
	DoublePointer(arr,begin,prev-1);
	DoublePointer(arr,prev+1,end);
}

非递归

用栈或者队列实现快排。将区间入栈,或者队列。通过获取栈顶或者堆顶得到要排序的区间,出栈,并完成一次排序。根据key的新位置,将两段新的区间入栈。

用栈实现

栈的部分接口:

typedef int StackDateType;

typedef struct StackNode
{
	StackDateType* arr;
	int size;
	int capacity;
}St;
void StackInit(St* st)
{
	assert(st);
	st->arr = NULL;
	st->capacity = 0;
	st->size = 0;
}
void StackDestory(St* st)
{
	assert(st);
	st->arr = NULL;
	st->capacity = st->size = 0;
}
void StackPush(St* st, StackDateType data)
{
	//扩容
	if (st->capacity == st->size)
	{
		int NewCapcity = st->capacity == 0 ? 4 : st->capacity * 2;
		StackDateType* tmp = (StackDateType*)realloc(st->arr,NewCapcity*sizeof(StackDateType));
			if (tmp == NULL)
			{
				perror("malloc faile:");
				exit(-1);
			}
		st->arr = tmp;
		st->capacity = NewCapcity;
	}
	st->arr[st->size] = data;
	st->size++;
}
void StackPop(St* st)
{
	assert(st);
	assert(st->size > 0);

	st->size--;
}
StackDateType StackTop(St* st)
{
	assert(st);
	return st->arr[st->size-1];
}
bool StackEmpty(St* st)
{
	assert(st);

	return st->size == 0;
}

排序逻辑代码:

int PartSort3(int* arr, int begin, int end)
{
	int mid = GetMiddle(arr, begin, end);
	Swap(&arr[begin], &arr[mid]);

	int prev = begin;
	int cur = begin + 1;
	int key = begin;

	while (cur <= end)
	{
		// 找到比key小的值时,跟++prev位置交换,小的往前翻,大的往后翻
		if (arr[cur] < arr[key] && ++prev != cur)
		{
			Swap(&arr[cur],&arr[prev]);
		}
		cur++;
	}
	//prev的值一定比key要小
	Swap(&arr[prev], &arr[key]);
	key = prev;
	return key;
}
//非递归用栈
void QuickSortNorR1(int* arr, 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 key =  PartSort3(arr, left, right);
		//[begin ,key-1] key [key+1,end]
 		//新的区间入栈
		if (key +1 < right)
		{
			StackPush(&st, key + 1);
			StackPush(&st, right);
		}
		if (left < key - 1)
		{
			StackPush(&st,left);
			StackPush(&st,key-1);
		}
	}
	StackDestory(&st);
}

用队列实现

//非递归用队列
void QuickSortNorR2(int* arr, int begin, int end)
{
	int Que[20] = {0};

	int head = begin;
	int tail = begin;
	//先进先出
	Que[tail++] = begin;
	Que[tail++] = end;
	while (head < tail)
	{
		//出队获取左右区间
		int left = Que[head++];
		int right = Que[head++];
		//一趟排序
		int key = PartSort3(arr, left, right);
		//[begin ,key-1] key [key+1,end]
		if (left < key - 1)
		{
			//入队
			Que[tail++] = left;
			Que[tail++] = key - 1;
		}
		if (key + 1 < right)
		{
			Que[tail++] = key+1;
			Que[tail++] = right;
		}
	}
}

归并排序 

排序思想:使每个子序列有序,再使子序列段间有序。

 ​​​​​​​

 递归

分析:归并的过程是将两组有序的序列归并成一组有序的序列,但是在实际问题中,我们要处理的数据大都是随机的,所以当将子序列分到最小,一组只有一个数据的时候,这时候就认为这个子序列是有序的。从一个数据开始,两两归并。归并成一组有两个元素的有序序列,在继续归并,直至序列有序。

代码实现:

void _MergeSort(int* arr, int* tmp, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	int mid = (begin + end) / 2;
	//[begin,mid] [mid+1,end]
	_MergeSort(arr,tmp,begin,mid);
	_MergeSort(arr,tmp,mid+1,end);

	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] > arr[begin2])
		{
			tmp[i++] = arr[begin2++];
		}
		else
		{
			tmp[i++] = arr[begin1++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	//拷贝
	memcpy(arr+begin,tmp+begin,sizeof(int)*(end-begin+1));
}
//归并排序,递归
void MergeSort(int* arr, int len)
{
	int* tmp = (int*)calloc(len,sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc faile:");
		exit(-1);
	}
	_MergeSort(arr,tmp,0,len-1);

	free(tmp);
	tmp = NULL;
}

非递归

越界的三种情况:

gap表示一组有几个元素。这里的边界需要注意一下,i += 2*gap的原因,就是跳过两组取处理后面的数据。最后gap*=2是在下次处理的时候,每组的元素个数翻倍。 

代码实现:

void MergeSortNorR(int* arr, int len)
{
	int* tmp = (int*)calloc(len, sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc fail:");
		exit(-1);
	}
	
	int gap = 1;
	while (gap < len)
	{
		for (int i = 0; i < len; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap,end2 = i+2*gap-1;
			int index = i;
			if (end1 >= len)
			{
				end1 = len - 1;
				//修正成不存在区间
				begin2 = len;
				end2 = len - 1;
			}
			else if (begin2 >= len)
			{
				begin2 = len;
				end2 = len - 1;
			}
			else if (end2 >= len)
			{
				end2 = len - 1;
			}
			while(begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] <= arr[begin2])
				{
					tmp[index++] = arr[begin1++];
				}
				else
				{
					tmp[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = arr[begin2++];
			}
		}
		memcpy(arr, tmp, sizeof(int)*len);
		gap *= 2;
	}

	free(tmp);
	tmp = NULL;
}

总结

 时间复杂度和空间复杂度大家都非常了解,稳定性指定是在排序的过程中相同的值在排序后是否可以保证相对顺序,如果可以则视该排序稳定,反之不稳定。

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

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

相关文章

slot的理解

首先&#xff0c;大概了解一下插槽&#xff1a; 插槽是什么 ![在这里插入图片描述](https://img-blog.csdnimg.cn/90b04660769e49c286ee2e1821d2a2bb.png 插槽&#xff1a;在HTML中 slot 元素 &#xff0c;作为 Web Components 技术套件的一部分&#xff0c;是Web组件内的一个占…

HashMap1.8也会发生死循环

在网上搜资料时候然后发现网上都说1.7版本的HashMap会发生死链也就是死循环&#xff0c;但是在HashMap中也会产生死循环&#xff0c;接下来直接看代码吧 代码 类名字我忘记改了这是我以前看park时候弄的但是这不重要 当你运行 public class parkAndUnpark {static Map<…

微服务守护神-Sentinel-降级规则

引言 书接上篇 微服务守护神-Sentinel-流控规则 &#xff0c;上面介绍了Sentinel流控规则&#xff0c;本篇继续来Sentinel的降级规则。 降级规则 那啥为降级呢&#xff1f;降级可以理解为下降等次&#xff0c;比如&#xff1a;你从广州到北京&#xff0c;有钱时&#xff0c;…

Kafka的认证

Kafka支持基于SSL和基于SASL的安全认证机制。 基于SSL的认证主要是指Broker和客户端的双路认证。即客户端认证broker的证书&#xff0c;broker也认证客户端的证书。 Kafka还支持通过SASL做客户端认证。SASL是提供认证和数据安全服务的框架。Kafka支持的SASL机制有5种&#xff…

Docker容器化技术入门(一)Docker简介

Docker容器化技术入门&#xff08;一&#xff09;Docker简介前言&#xff08;一&#xff09;Docker简介1 Docker是什么&#xff1f;1.1 Docker的出现1.2 Docker的理念1.3 一句话2 容器与虚拟机比较2.1 容器发展简史2.2 传统虚拟机技术2.3 容器虚拟化技术2.4 对比3 Docker能干什…

华硕编程竞赛11月JAVA专场 D题飞机大战 题解

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

org.eclipse.e4.core.di.InjectionException: Unable to proces

eclipse RCP 项目在eclipse 插件程序中正常运行&#xff0c;导出eclipse product后运行报错。路径&#xff1a;【项目名称】–>workspace–>.metadata–>.log 报错信息如下&#xff1a; !SESSION 2022-12-08 17:19:22.227 ------------------------------------------…

MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究...

全文链接&#xff1a;http://tecdat.cn/?p30766本文就将采用改进Fuzzy C-means算法对基于用户特征的微博数据进行聚类分析。去年&#xff0c;我们为一位客户进行了短暂的咨询工作&#xff0c;他正在构建一个主要基于微博用户特征聚类研究的分析应用程序&#xff08;点击文末“…

从零开始带你实现一套自己的CI/CD(二)Jenkins+Sonar Qube

目录一、简介二、Sonar环境搭建2.1 下载Docker镜像2.2 使用docker-compose部署三、Sonar Qube基本使用3.1 Maven实现代码检测3.2 Sonar-scanner实现代码检测四、Jenkins集成Sonar Qube4.1 下载插件4.2 SonarQube生成Token4.3 Jenkins配置Sonar Token信息4.4 Jenkins全局工具配置…

word如何设置页码?一分钟教你设置Word文档的页码!

你知道Word怎么设置页码吗&#xff1f;可能你还找不到页码在何处。相信有很多人也会遇到这些类似的问题。如何在Word文档里面的任意一页设置页码呢&#xff1f;word如何设置页码&#xff1f;现在小编就把设置页码的过程和截图分享出来&#xff0c;本文仅供参考使用。有需要的朋…

算法刷题打卡第40天:打家劫舍

打家劫舍 难度&#xff1a;中等 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。…

全解一款六面体结构化网格划分利器-NUMECA IGG

作者 |卢工FunRun 仿真秀优秀讲师 导读&#xff1a;前不久&#xff0c;VIP群有人提问&#xff1a;“老师&#xff0c;NUMECA如何计算带蜗壳叶轮机呢”&#xff1f;笔者使用NUMECA FINE/Turbo&#xff08;以下简称Turbo&#xff09;软件解决叶轮机械气动性能仿真计算已有三年多&…

nRF Connect的使用

一、工具简介 nRF Connect是NORDIC开发的一款低功耗蓝牙测试APP&#xff0c;仅支持安卓。可以扫描和探索低功耗蓝牙设备并与它们通信。 蓝牙通信的核心是向硬件发送数据和接收硬件传回来的数据。 二、准备项 Android手机 蓝牙硬件 三、使用简介 1、进入界面 &#xff08;1&…

某科技公司防火墙配置与管理

目录 杭州继保南瑞电子科技有限公司… 1 公司简介…2需求分析… 错误!未定义书签。公司网络拓扑图…4IP 地址规划 …4设备选型…5技术介绍…6 6.1 DMZ …6 6.2 VPN …6 6.3 NAT …6 6.4 ACL …7项目实施…7 7.1 DMZ 区域配置及结果测试 …7 7.1.1 防火墙基本配置…8 7.1.2 内网…

粉笔通过上市聆讯:上半年营收14.5亿 腾讯经纬高瓴是股东

雷递网 雷建平 12月7日职业教育平台粉笔科技今日通过聆讯&#xff0c;准备在港交所上市。花旗、中金&#xff08;香港&#xff09;和美银证券为其联席保荐人。粉笔科技此次募资用途为丰富课程内容、扩大学员群体、加强内容及技术开发能力等。上半年营收14.51亿粉笔科技成立于20…

# spring-security(一)

一、权限管理简介 1、什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现对用户访问系统的控制&#xff0c;按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 ​ 权限管理…

学习笔记-3-SVM-9-Twin SVM

Outline 1. Motivation 2. Geometry 3. Algebraic manipulation 4. Observations ------------------------------ 1. Motivation Twin SVM的基本出发点是做二分类时&#xff0c;为什么我们只用1个分割超平面&#xff0c;为什么不能用两个呢&#xff1f; 这里是想用两个…

CVT变速器中壳体吊机设计

目 录 1 绪论 1 1.1 课题的研究目的和意义 1 1.2 课题研究的内容 1 2 CVT变速器中壳体吊机总体设计 3 2.1 性能参数 3 2.2 确定主要工作机构和金属结构的形式 4 2.2.1 确定主要工作机构形式 4 2.2.2 金属结构选型 11 2.3 载荷的计算 13 2.3.1 自重载荷 13 2.3.2 起升载荷 14 2.…

HTTP常见状态码

网上都有状态码的说明但是有些不全所以我特此在这里整理一下&#xff0c;这个图来自小林大佬的图 1xx 100 表示客户还需要继续发送请求 101 客户要求服务器根据请求转换HTTP协议版本号 2xx 200 成功 201 提示知道新文件的URL 202 接受和处理、但处理未完成 203 返…

Redis缓存优化、本地锁及分布式锁的入门使用思想实现

Redis缓存优化、本地锁及分布式锁的入门使用思想实现 1、依赖启动器引入 <!-- redis --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency><!…