数据结构八大常见的排序

数据结构八大常见的排序

  • 常见排序算法分类
  • 1.插入排序
  • 2.希尔排序(缩小增量排序)
  • 3.选择排序
  • 4.堆排序
  • 5.冒泡排序
  • 6.快速排序
  • 7.归并排序
  • 归并排序非递归的实现
  • 8.计数排序

常见排序算法分类

在这里插入图片描述

1.插入排序

基本思想:把待排序的数组按大小逐个插入到一个已经排好序的有序序列中,直到所有的数据插入完为止,得到一个新的有序序列 。
动图解析:
在这里插入图片描述

代码实现思路:
定义一个临时变量tmp来遍历待排数组,定义一个end作为有序数组的最后一个下标,然后end遍历有序数组。在排序中有以下几种情况:
1.最初开始的时候第一趟排序tmp直接从第二个数据开始遍历即可。
在这里插入图片描述
2.tmp<a[end]
在这里插入图片描述
3.tmp>a[end]
在这里插入图片描述
代码实现:

void InsertSort(int* a, int n)//插入排序 最好为O(N) O(N2)
{
	//思路:把数组中一个一个的插入,然后进行比较,把它们放到自己的位置
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;//end为控制数组的下标,使他放在temp的前一个
		int temp = a[i];
		//一趟插入一个数组,如果temp比a[end]小就放在a[end]左边,如果temp比a[end]大的话就放在,a[end]的右边
		while (end >= 0)
		{
			if (temp < a[end])//如果temp小于a[end],那么tempd肯定插入到a[end],之前,那么就让a[end]向后移动一位
				//接下来就拿temp和前面的比较,看是否小于a[end]前面的数
			{
				a[end + 1] = a[end];
				end--;
			}
			else {
				break;//如果temp>a[end]的话就跳出循环,将temp放在a[end]后面,插入的精髓就在于此temp永远在a[end]的后面
			}
		}
		a[end + 1] = temp;//这种写法temp大于或小于a[end]两种情况都符合,temp放在左(小)右(大)边都符合
	}
}

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

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

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

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap,把待排序文件中所有数据分成个gap组,所有距离为gap的数据分在同一组内,并对每一组内的数据进行排序,单趟排序的思路和直接插入排序一样。然后取重复上述分组和排序的工作。当到达gap=1时,所有数据在同一组内排序,gap=1是也就相当于直接插入排序。
在这里插入图片描述

代码实现:

void ShellSort(int* a, int n)//希尔排序
{
	int gap = n;//gap在这里是一个不断变化的值
		while (gap > 1)
	
		{
			gap = gap / 3 + 1;//保证gap最后一次为1,当gap等于1的时候相当于简单插入排序,就可以直接排序出来了
			for (int i = 0; i < n - gap; i++)
			{
				int end = i;
				int temp = a[end + gap];
				//将数组分为gap组,当gap较大的时候,也就是第一循环gap为n/3+1,将数组分成gap(大约n/3)组,那么一组就有3个数据,
				// 最差的情况while循环也只需要1+2次,外循环n,所以gap较大的时候的时间复杂度为O(N)
				//当gap较小的时候,即gap为1的时候,这时候就是插入排序,但是因为gap越小数组就越接近有序,
				//所以这时候的时间复杂度为O(N)
				//所以希尔排序的时间复杂度的量级为 logN*N   大约为n^1.3
	
				while (end >= 0)
				{
					if (temp < a[end])
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = temp;
			}
		}
}

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
    会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的
    希尔排序的时间复杂度都不固定:
    在这里插入图片描述
    在这里插入图片描述
  4. 稳定性:不稳定

3.选择排序

基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据排完 。我们实现的是单趟排序中选出最大值和最小值,分别放在数组的最右边和最左边。
动图实现的是将数组中最小的放在最左边:
在这里插入图片描述
代码实现:

void SelectSort(int* a, int n)//选择排序(升序)   最坏为O(N2) 最好O(N2)
{
	int left = 0;
	int right = n - 1;
	while (left < right)
	{
		//一趟选出一个最大和最小的值然后放在数组的两边
		int mini = left;//最小数的下标
		int maxi = left;//最大数的下标
		for (int i = left+1; i <= right; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}
		Swap(&a[left], &a[mini]);//最小的放在左边
		if (maxi == left)//如果刚好最大值再最左边,则会导致最大值交换到mini下标的位置
		{
			maxi = mini;
		}
		Swap(&a[right], &a[maxi]);
		left++;
		right--;

	}
}

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

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

4.堆排序

升序堆排序的一般思路就是将给定的一组数据放在堆的数据结构当中去,然后进行不断被的取堆顶元素放在数组当中,不断地pop(删除)。但是这种方法太麻烦了,自己还要手写一个堆的数据结构以及一些接口函数,还要创建数组等,显然不是最优解。
接上文的写的两种调整方式,向上调整和向下调整。 详细见数据结构二叉树顺序结构——堆的实现
思路:
①可以用向上调整或者向下对原数组进行调整,也就是建一个大堆(排升序大堆后面讲为啥)
②接下来利用堆删除的原理,将堆顶的数据和数组最后一个交换(也就是将堆顶和堆尾的数据进行交换),然后就相当于最大的数放在了最后一个位置,所以最后一个位置的数据确定了,接下来对剩下的数据进行向下调整,再重复以上操作。
在这里插入图片描述

ps:排升序建大堆而不是小堆的原因,反证思路来看,若建小堆的话,最小的数据在第一个,第一个数据确定了,但是剩下的数据很暖再重新调整成为一个新的小堆的数据结构,所以排升序建小堆很难实现。

向上调整和向下调整都可以完成建堆的操作,但是他们的时间复杂度有所不同,接下来讲一下他们的取舍。
向上调整建堆(时间复杂度:O(N * logN) )

for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
在这里插入图片描述

向下调整建堆(时间复杂度:O(N) )

for (int i = (n - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);//a是堆的数组,n是防止数组越界的数组数据个数,i是开始向下调整的位置
	}

向下调整建堆得思路:从第一个非叶子结点开始从数组得后面向前面一个一个进行调整
在这里插入图片描述

复杂度证明:
在这里插入图片描述

看到这里很容易发现向下调整方法建堆得时间复杂度更加合适
代码实现:


void AdjustDown(int* a, int parent, int n)//向下调整
{
	int child = parent * 2 + 1;
	while (child < n)//这里child会先越界到达临界点,所以不用判断parent
	{
		//默认child是左右孩子中较大的值
		if (child + 1 < n && a[child] < a[child + 1])
		{
			child = child + 1;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;//符合堆的性质则跳出循环
		}
	}
}

// 对数组进行堆排序
void HeapSort(int* a, int n)
{
	//思路:向上调整方法建堆建堆
	//>这里排升序要建大堆,因为建小堆的话,接下来排升序第一个数据好处理,
	//但是剩下的数据重新排成堆的话关系全都乱了
	//所以这时候建大堆,和删除的思路一样,首先交换堆顶和堆尾,这时候最大的数据放到了最后一个位置
	//然后将前面n-1个数据看成新的堆进行向下调整,然后再找次大的数放在倒数第二的位置
	
	//建堆有两种方式 向上调整建堆和向下调整建堆,时间复杂度分别为O(N*logN)和O(N)
	
	//向上调整建堆 建堆时间复杂度 O(N*logN)
	/*for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}*/

	//向下调整建堆  时间复杂度为O(N)
	//向下调整的条件是调整节点的左右子树都为大堆或者小堆
	//所以从下边开始进行向下调整(大堆),这时候大的数会慢慢上浮,最先调整的位置不能是叶子节点,那样会重复很多操作
	//应该从最后一个父亲节点开始进行向下调整 最后一个父亲节点的下标为 (n-1)/2,然后按数组下标的顺序递减进行调整
	for (int i = (n - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);//a是堆的数组,n是防止数组越界的数组数据个数,i是开始向下调整的位置
	}
	while (--n)  //排序的时间复杂度为O(N*logN)
	{
		//此处为--n的原因:一共有n个数据,循环一次确定一个数据的位置,循环n-1次之后就可以确定n-1个数据的位置,
		// 前面已经确定了n-1个位置,最后一个数据的位置也已经确定,所以当n=0的时候的那一次循环可以不需要进行就可以
		// 
		//此时n已经自减1,所以此时n就为堆尾数据,且n为下一个新堆的数据个数,所以后面向下调整直接传参n
		Swap(&a[0], &a[n]);//交换堆顶和堆尾的数据
		AdjustDown(a, n, 0);//n为新堆的数据个数
	}

	//总结:堆排序的时间复杂度为O(N*logN),因为堆排序有两个步骤①建堆②排序
	//建堆向上调整建堆的时间复杂度为O(N*logN),向下调整建堆的时间复杂度为O(N),相对较快,
	//但是排序的时间复杂度都为O(N*logN),所以决定了堆排序的时间复杂度为O(N*logN)。
}

堆排序的特性总结:

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

5.冒泡排序

基本思路:两两数据相比,大的元素向后移,也就是前一个数据大于后一个数据就交换,一趟能保证最大的数据放在数组的最右边。
在这里插入图片描述
代码实现:

void BubbleSort(int* a, int n)//冒泡排序   最坏O(N2)  最好O(N)
{
	//这里起始位置如果i=0;那么就是a[i]和a[i+1]进行比较,i作为下标最后的位置落在n-2的位置,和n-1去比较
	//这里起始位置如果i=1;那么就是a[i-1]和a[i]进行比较,i作为下标最后的位置落在n-1的0位置,和n-1-1去比较
	while (n)
	{
		int i = 0;
		bool exchange = false;
		//这里定义一个记号,记录一下单趟循环中是否有交换,若单趟循环中没有交换就证明,已经有序了
		//不需要接下来的排序
		for (i = 1; i < n; i++)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = true;
			}
		}
		if (exchange == false)
		{
			break;
		}
		n--;
	}
}

6.快速排序

详细见数据结构——快速排序的三种方法和非递归实现快速排序

7.归并排序

基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
在这里插入图片描述
动图详解:
在这里插入图片描述
代码实现思路:

子规模:
将数组分为两部分,然后分别把这两部分数据排成有序部分,然后将两组有序数组分别进行遍历比较,较小的数据放在临时数组tmp中,其中一个数组遍历结束后将另一组未比较的数据直接赋值到临时数组tmp,最后用memcpy函数将临时数组tmp的值拷贝放到指定数组中;
递归:
先将给定数组不断递归,直到数组中只有一个数据,也就代表有序,不需要进行排序,然后返回。
代码实现:

void MergeSort(int* a, int n)//归并排序
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("tmp malloc failed\n");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

void _MergeSort(int* a, int left, int right, int* tmp)//归并排序的排序部分
{
	//思路:将数组分为左右两部分,并且每组内是有序的,然后再将有序的两组分别遍历相比较,
	//较小的放在临时数组中,一个数组遍历结束后,另一个数组的所有值直接赋值给临时数组
	//最后用memcpy将临时数组的值拷贝到原数组
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) / 2;
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	//递归到一组为一个值的时候开始比较
	_MergeSort(a, begin1, end1, tmp);
	_MergeSort(a, begin2, end2, tmp);
	//比较
	int i = begin1;//这里i的值必须是begin1,当递归到右半边的数据的时候,给tmp赋值的时候必须对应的也应该是右半边的空间
	while (begin1 <= end1 && begin2 <= end2)//两组数据都没遍历结束的时候都可以进循环比较
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	//将没有遍历完的数组赋值到临时数组tmp中
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	//这里memcpy中数组的起始地址要加上left的,因为我们每次只需要将递归中重新排序的那个组的数值拷贝到原来的数组即可
	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

归并排序非递归的实现

归并排序的思路就是将数组分为左右两个有序数组然后进行排序,通过不断递归到子规模数组中只有一个元素的时候开始排序,然后一步一步的返回。那么我们可以直接模拟递归的子规模然后一步一步的的返回,也就是定义一个gap,初始值为1,gap的意义就是将数组分为每组为gap个数组的组,然后进行排序:
在这里插入图片描述
每两组然后开始进行单趟归并排序,排序后为6,10,1,7,3,9,2,4。接下来将数组分为每组数组为2的数组,也就是gap=2:
在这里插入图片描述
接下来进行单趟归并,归并后数组为1,6,7,10,2,3,4,9,接下来gap=4:
在这里插入图片描述
然后进行单趟归并排序后为1,2,3,4,6,7,9,10。完成递归。当然看到这里肯定有疑问,如果数组元素个数不是2的n次方怎么办呢,肯定会越界访问,先不急一步一步来,先实现这种理想状态下的代码
代码实现

void MergeSortNonR(int* a, int n)//归并排序(非递归)
{
	int* tmp = (int*)malloc(sizeof(int) * n);//创建临时数组tmp用来存放归并后的数据然后最后再拷贝回去
	if (tmp == NULL)
	{
		perror("tmp malloc failed\n");
		return;
	}
	int gap = 1;
	while(gap<n)
	{
		int i = 0;
		int j = 0;//控制tmp数组
		for (i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
		
			while (begin1 <= end1 && begin2 <= end2)//两组数据都没遍历结束的时候都可以进循环比较
			{
				if (a[begin1] <= a[begin2])
				{
					//这两种写法一样不过我的编译器第一种会有点报错,有点强迫症哈哈
					//tmp[j++] = a[begin1++];
					*(tmp + j++) = a[begin1++];
				}
				else
				{
					*(tmp + j++) = a[begin2++];
				}
			}
			//将没有遍历完的数组赋值到临时数组tmp中
			while (begin1 <= end1)
			{
				*(tmp + j++) = a[begin1++];
			}
			while (begin2 <= end2)
			{
				*(tmp + j++) = a[begin2++];
			}
			//这里memcpy中数组的起始地址要加上i的,因为我们每次只需要将递归中重新排序的那个组的数值拷贝到原来的数组即可
			memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));
		}
		gap *= 2;
	}
	free(tmp);
}

运行截图:
在这里插入图片描述
这里发现我们的思路暂时是没问题的,但是我们将数组改为11个数据的时候,会发现有越界的情况。
在这里插入图片描述

在这里插入图片描述
这时候我们可以加几行代码,把每次进行单趟排序的去加打印出来。
在这里插入图片描述

在这里插入图片描述
这时候我们发现划线部分都是越界部分,我们分析一下,begin1,end1,begin2,end2,谁有可能越界,因为begin1=i,i<n,所以begin1不会越界,那么也就是end1,begin2,end2会越界,这时候对这三种情况分情况讨论即可。
在这里插入图片描述
修改后代码:

void MergeSortNonR(int* a, int n)//归并排序(非递归)
{
	int* tmp = (int*)malloc(sizeof(int) * n);//创建临时数组tmp用来存放归并后的数据然后最后再拷贝回去
	if (tmp == NULL)
	{
		perror("tmp malloc failed\n");
		return;
	}
	int gap = 1;
	while(gap<n)
	{
		int i = 0;
		int j = 0;//控制tmp数组
		for (i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			//printf("[%d,%d][%d,%d]  ", begin1, end1, begin2, end2);
			while (begin1 <= end1 && begin2 <= end2)//两组数据都没遍历结束的时候都可以进循环比较
			{
				if (a[begin1] <= a[begin2])
				{
					//这两种写法一样不过我的编译器第一种会有点报错,有点强迫症哈哈
					//tmp[j++] = a[begin1++];
					*(tmp + j++) = a[begin1++];
				}
				else
				{
					*(tmp + j++) = a[begin2++];
				}
			}
			//将没有遍历完的数组赋值到临时数组tmp中
			while (begin1 <= end1)
			{
				*(tmp + j++) = a[begin1++];
			}
			while (begin2 <= end2)
			{
				*(tmp + j++) = a[begin2++];
			}
			//这里memcpy中数组的起始地址要加上left的,因为我们每次只需要将递归中重新排序的那个组的数值拷贝到原来的数组即可
			memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));
		}
		gap *= 2;
		//printf("\n");
	}
	free(tmp);
}

测试截图:
在这里插入图片描述

归并排序的特性总结:

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

8.计数排序

计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
实现思路:
1.开辟一个数组CountA
首先遍历一遍数组找出数组中的最大值MAX,最小值MIN,然后开辟一个RANGE大小的新的数组CountA,其中RANGE=MAX-MIN+1。CountA数组每个元素分别用来记录待排数组中对应数据的出现次数。
2.统计次数
在这里插入图片描述
在以上例子中可以发现待排数组的最小值为1,最大值为9,RANGE=9,所以开辟9个空间,将CountA计数数组初始化为0,然后开始遍历待排数组,待排数组中1出现了一次,2出现了2次,3出现了1次,4出现了0次,5出现了2次,6出现了1次,7出现了0次,8出现了0次,9出现了1次。
这里计数数组CountA中第一个数据也就是下标0的位置映射的是待排数组中的MIN,下标1映射的就是MIN+1,···,最后一个数据RANGE-1映射的就是最大值MAX。
3.赋值到原数组
直接按照顺序将原数组的数据覆盖就好。
代码实现:

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\n");
		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;
		}
	}

	free(countA);
}

总结:基数排序适合范围集中,且范围不大的整型数组排序。不适合范围分散过着非整形的排序,如字符串、浮点数等

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

结构体与位段的定义以及在内存中的存储

目录 结构体的声明 完全声明 不完全声明 结构体变量的定义和初始化 结构体的嵌套 结构体成员的直接访问和间接访问 结构体的自引用 typedef对结构体类型重命名 结构体内存对齐 对齐规则 练习 为什么存在内存对齐 修改默认对齐数 结构体传参 结构体实现位段 位段…

【Linux】认识线程池 AND 手撕线程池(正常版)

文章目录 0.回顾进程池1.计算机层面的池化技术2.线程池预备知识2.1介绍线程池2.2设计线程池的意义是什么&#xff1f;2.3其他知识 3.回顾C类与对象3.1cpp什么情况下成员函数必须是静态的&#xff1f;3.1可变参数列表3.2格式化输出函数3.3预定义符号 4.图解线程池运作原理4.0完整…

创意艺术信息图表绘制方法(六边形图)

创意艺术信息图表绘制方法&#xff08;六边形图&#xff09; 在网络科技发展进步的当下&#xff0c;原来一些传统的统计图表都有了进一步的创新。以前企业的PPT都依赖微软的各应用软件来制作图表&#xff0c;现时企业的PPT展示的图表应用不再满足于Excle&#xff0c;Word等的图…

设计一个动物声音“模拟器”,希望模拟器可以模拟许多动物的叫声。

设计一个动物声音“模拟器”&#xff0c;希望模拟器可以模拟许多动物的叫声。要求如下&#xff1a; &#xff08;1&#xff09;编写接口Animal Animal接口有2个抽象方法cry()和getAnimaName()&#xff0c;即要求实现该接口的各种具体动物类给出自己的叫声和种类名称。 &…

【Linux系列】tree和find命令

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

腾讯云TDSQL(MySQL版)和(PostgreSQL版)TCP认证介绍

腾讯云TDSQL&#xff08;MySQL版&#xff09;TCP认证介绍 数据库交运维高级工程师-腾讯云TDSQL&#xff08;MySQL版&#xff09;-课程体系-云贝教育&#xff08;yunbee.net&#xff09; 培训概述 数据库交付运维高级工程师-腾讯云TDSQL&#xff08;MySQL版&#xff09;培训&…

PetaLinux使用简介

1 概述 PetaLinux 工具提供在 Xilinx 处理系统上定制、构建和调配嵌入式 Linux 解决方案所需的所有组件。该解决方案旨在提升设计生产力&#xff0c;可与 Xilinx 硬件设计工具配合使用&#xff0c;以简化针对 Versal、Zynq™ UltraScale™ MPSoC、Zynq™ 7000 SoC、和 MicroBl…

基于ssm的家庭食谱管理系统(java项目+文档+源码)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的家庭食谱管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 家庭食谱管理系统的主要使用者…

前端 - 基础 表单标签 - 表单元素 input - type 属性 ( 单选按钮和复选按钮 )

input 标签 type 属性 &#xff0c;上一篇讲了 输入框 和 密码框 这节看看 单选按钮 和 复选 按钮 目录 单选按钮 &#xff1a; 复选按钮 # 看上图就可以看到 单选按钮 -- radio 和 复选 按钮 -- checkbox 单选按钮 &#xff1a; 所谓单选按钮就是 有时…

Can‘t connect to server on ‘localhost‘ (10061)

问题&#xff1a;电脑关机重启后&#xff0c;连接不上mysql了&#xff0c;报错信息如下&#xff1a;2002 - Cant connect to server on localhost (10061)解决办法&#xff1a;很大的原因是mysql服务没有启动&#xff0c;需要你重启一下mysql&#xff1a; 以管理员的身份运行cm…

R使用netmeta程序包实现二分类数据的频率学网状meta分析

该研究检索了Cochrane对照试验中心注册&#xff0c;CINAHL&#xff0c;Embase&#xff0c;LILACS数据库&#xff0c;MEDLINE&#xff0c;MEDLINEIn-Process&#xff0c;PsycINFO&#xff0c;监管机构网站&#xff0c;以及从一开始就发布和未发表的双盲随机对照试验的国际注册20…

重构智能防丢产品,苹果Find My技术引领市场发展

目前市场上最主要的防丢技术是蓝牙防丢和GPS防丢&#xff0c;蓝牙防丢是通过感应防丢器与绑定手机的距离来实现防丢的。一般防丢会默认设置一个最远安全距离&#xff0c;超过这个安全距离后&#xff0c;与手机蓝牙信号断开&#xff0c;触发防丢报警&#xff0c;用户根据防丢报警…

百度贝塞尔曲线证码识别代码

一、前言 百度出了如图所示的验证码&#xff0c;需要拖动滑块&#xff0c;与如图所示的曲线轨迹进行重合。经过不断研究&#xff0c;终于解决了这个问题。我把识别代码分享给大家。 下面是使用selenium进行验证的&#xff0c;这样可以看到轨迹滑动的过程&#xff0c;如果需要…

Leetcode 234. 回文链表

心路历程&#xff1a; 一开始想到用栈&#xff0c;但是发现还是得到中点后才开始判断&#xff0c;时间空间没什么区别&#xff0c;还不如直接获取数组后正逆对比&#xff1b; 看了网上的O&#xff08;1&#xff09;空间复杂度方法&#xff0c;意思是按照奇数偶数判断完之后&am…

保护JavaScript代码安全性:探究JScrambler、JShaman、JSFack等常用加密混淆工具

摘要 本篇技术博客将介绍五款常用且好用的在线JavaScript加密混淆工具&#xff0c;包括 jscrambler、JShaman、jsfack、freejsobfuscator 和 jjencode。通过对这些工具的功能及使用方法进行详细解析&#xff0c;帮助开发人员更好地保护和加密其 JavaScript 代码&#xff0c;提…

142.环形链表

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整…

Spring Boot集成JPA快速入门demo

1.JPA介绍 JPA (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象/关联映射工具来管理 Java 应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和整合 ORM 技术&#xff0c;结束现在 Hibernate&#xff0c;TopLink&am…

保护前端代码安全:探索JScrambler、JSFack、IpaGuard等五款JavaScript加密工具

摘要 本篇技术博客将介绍五款常用且好用的在线JavaScript加密混淆工具&#xff0c;包括 jscrambler、JShaman、jsfack、freejsobfuscator 和 jjencode。通过对这些工具的功能及使用方法进行详细解析&#xff0c;帮助开发人员更好地保护和加密其 JavaScript 代码&#xff0c;提…

websocketpp上手笔记-Windows安装

WebSocketpp是什么 最近手上有一个c项目&#xff0c;需要用websocket从服务器端收内容。于是网上找了圈&#xff0c;发现WebSocketpp库可以做websocket的客户端。 WebSocketpp也叫WebSocket&#xff0c;github地址是&#xff1a;https://github.com/zaphoyd/websocketpp&…

KMP字符串匹配算法

本文用于记录个人算法竞赛学习&#xff0c;仅供参考 目录 一.KMP 二.next数组&#xff08;前缀表&#xff09; 三.具体实现模板 四.题解 先来看一个问题 28. 找出字符串中第一个匹配项的下标 - 力扣&#xff08;LeetCode&#xff09; 对于这个问题&#xff0c;一般暴力做法…