深入浅出C语言——排序

news2024/10/6 10:40:29

文章目录

  • 排序的概念
  • 常见的排序算法
    • 冒泡排序
    • 选择排序
    • 插入排序
    • 希尔排序
    • 堆排序
    • 快速排序
      • hoare版本
      • 挖坑法
      • 前后指针版本
      • 快速排序的非递归形式
    • 归并排序
      • 递归版本
      • 非递归版本
    • 计数排序
    • 排序算法复杂度及稳定性分析


排序的概念

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

常见的排序算法

冒泡排序

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这步做完后,最后的元素会是最大的数。针对所有的元素重复以上的步骤(除了最后一个)即可完成排序。

在这里插入图片描述

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void BubbleSort(int* a, int n)
{
	assert(a);
	//外层循环走n-1次,因为最后一趟不需要比较
	for (int i = 0; i < n - 1; i++)
	{
		int flag = 0;
		//每次外层循环能确定一个最大的数在正确的位置,所以只需要比较n-i次
		for (int j = 1; j < n - i; j++)
		{
			//比较相邻的两个元素,如果他们的顺序错误就把他们交换过来
			if (a[j-1] > a[j])
			{
				Swap(&a[j - 1], &a[j]);
				flag = 1;
			}			
		}
		//如果走了一趟都没有交换说明已经有序
		if (flag == 0)
			break;
	}
}

选择排序

每一次从待排序的数据元素中选出最小和最大的一个元素,存放在序列的起始位置,在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素 。因为可能把相同的元素换到不同的位置,所以选择排序也是不稳定的。

在这里插入图片描述

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void SelectSort(int* a, int n)
{
	assert(a);

	int begin = 0, end = n - 1;
	while (begin < end)//迭代的过程中,n为奇数个数会相遇,偶数个会错过
	{
		int mini = begin;
		int maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			//找最小元素
			if (a[i] < a[mini])
				mini = i;
			//找最大元素
			if (a[i] > a[maxi])
				maxi = i;
		}
		//开头和最大交换
		Swap(&a[begin], &a[mini]);

		// 如果begin和maxi重叠,那么上一步中就把max换走了,那么max就到了min的位置
		if (begin == maxi)
		{
			maxi = mini;
		}
		//结尾和最小交换
		Swap(&a[end], &a[maxi]);
        //迭代
		++begin;
		--end;
	}
}

插入排序

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为
止,得到一个新的有序序列。即:当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与 array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZZcm0S0R-1683821857206)(D:\Desktop\insertionSort.gif)]

每一次从待排序的数据元素中选出最小和最大的一个元素,存放在序列的起始位置,在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素 。最好的情况是接近有序;最坏的情况是逆序。

void InsertSort(int* a, int n)
{
	assert(a);
	//外层循环n-1次
	for (int i = 0; i < n - 1; ++i)
	{
		// [0,end]有序,把end+1位置的值插入,保持有序
		int end = i;		  //每次都要对end重新赋值
		int tmp = a[end + 1];  //tmp保存end+1位置的元素
		while (end >= 0)
		{
			//如果不满足条件就把前面的元素后移,直到数组起始位置
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		//到这里就满足条件了,证明end前面的元素就是tmp应该存放的位置
		a[end + 1] = tmp;
	}
}

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:把待排序文件中所有记录分组,所有距离为Gap的分在同一组内,并对每一组内的记录进行排序。待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。 预排序的时候,相同的数据可能分到了不同的组,所系希尔排序是不稳定的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O13k2s4q-1683821857206)(D:\Desktop\042e746dcbcb46e0a6d749af778324c1.gif)]

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化
  2. 当 gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,就变为插入排序了,这样整体而言,可以达到优化的效果。
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//gap = gap / 2;
		for (int i = 0; i < n - gap; ++i) //注意循环条件,当i=n-gap的时候,a[end+gap]越界
		{
			//类似插入排序,只不过把1换为了gap
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap; //这里不是--,而是-=gap
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

堆排序

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qRVAsobU-1683821857206)(D:\Desktop\heapSort.gif)]

为什么排升序要建大堆

  排升序时,如果建小堆, 最小的数已经在堆顶的位置上,但是除了堆顶以外,剩下的数都不是有序的,如果需要找出剩下的数中最小的数,需要重新建堆这样排序的时间复杂度太大,还不如直接遍历排序。所以需要建立大堆堆顶是最大的数,然后同最后一个数交换,再把交换后的堆顶数向下调整,然后重复交换再向下调整新堆顶数,直到实现排序

代码实现

#include<stdio.h>
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDwon(int* a, int size, int parent)
{
	assert(a);
	// 默认左孩子小
	int child = parent * 2 + 1;
	// 到叶子就是孩子不存在,孩子不存在就是child>=parent
	while (child < size)
	{
		// 选出左右孩子中小/大的那个
		// 避免越界访问
		if (child + 1 < size && a[child + 1] > a[child])
		{
			child++;
		}
		// 孩子跟父亲比较
		if (a[child] > a[parent])
		{
			//交换
			Swap(&a[child], &a[parent]);
			//迭代
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
// 堆排序:
void HeapSort(int* a, int n)
{
	// 向下调整建大堆 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}

	// 依次调整大的数据放到堆尾
	// O(N*logN)  总时间复杂度O(N*logN)+O(N)——>O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		//选出次大的
		AdjustDwon(a, end, 0);
		--end;
	}
}

快速排序

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

hoare版本

选出一个key,一般为最左边的值。先让right先往左遍历寻找一个比key小的数字,找到后停下来,再让left向右遍历寻找一个比key大的数字,找到后把这两个数交换。以此类推,直到left和right相遇,再把这个相遇的位置的数字和key交换就完成了一趟快速排序。单趟排序完后能达到左边比key小,右边比key大。然后再把key的左边和右边作为一个局部的数组重复上面的步骤,直到每一个局部区间都是有序的,那么这个数组也就是有序的了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UJVewCwp-1683821857207)(D:\Desktop\20210515134431451.gif)]

void QuickSort(int* a, int begin, int end)
{
	// 区间不存在,或者只有一个值则不需要再处理
	if (begin >= end)
	{
		return; //递归出口,整个过程非常类似二叉树的前序遍历
	}

	int left = begin, right = end;
	int keyi = left;
	while (left < right) //当相遇或错过就结束
	{
		// 右边先走,找小
		while (left < right && a[right] >= a[keyi]) //避免越界
		{
			--right;
		}
		// 左边再走,找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}

	// 如果左边先走,并与右边相遇,此时不能保证右边的值小于key,而在最后交换的时候产生bug
	// 即:右边先走,右边停下来的位置能保证相遇的位置比key都小
	Swap(&a[keyi], &a[left]);
	//keyi = left;使左边比key小,右边比key大
	keyi = left;
	
	//递归
	// [begin, keyi-1] keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}

挖坑法

首先选取左边第一个为key值形成第一个坑。右边先走,找到小于key的值,填入左边的坑位置中,该位置形成新的坑。左边找大于key的值,再填入右边的坑位中,该位置再形成新的坑。直到左右相遇,一定相遇再坑的位置处。单趟排序完后能达到左边比key小,右边比key大。然后再把key的左边和右边作为一个局部的数组重复上面的步骤,直到每一个局部区间都是有序的,那么这个数组也就是有序的了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pz7gAp8O-1683821857207)(D:\Desktop\20210515183213169.gif)]

void QuickSort(int* a, int begin, int end)
{
	// 区间不存在,或者只有一个值则不需要再处理
	if (begin >= end)
	{
		return; //递归出口,整个过程非常类似二叉树的前序遍历
	}
	int key = PartSort(a,begin,end);
	//递归
	// [begin, keyi-1] keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}
// 挖坑法
int PartSort(int* a, int begin, int end)
{
	int key = a[begin];
	int piti = begin;
	while (begin < end)
	{
		// 右边找小,填到左边的坑里面去。这个位置形成新的坑
		while (begin < end && a[end] >= key)
		{
			--end;
		}
		a[piti] = a[end];
		piti = end;
		// 左边找大,填到右边的坑里面去。这个位置形成新的坑
		while (begin < end && a[begin] <= key)
		{
			++begin;
		}
		a[piti] = a[begin];
		piti = begin;
	}
	a[piti] = key;
	return piti;
}

前后指针版本

先选定左边第一个为基准值key,同时设定left位置为prev,prev的后一个位置为cur,从a[cur]开始和key比较,如果a[cur]比key小,就先将prev++,再让a[cur]和a[prev]交换,然后cur++,如果a[cur]比key大,那么就不改变prev,也不用交换,只对cur++,以此类推,直到cur>right就结束,最后将a[prev]和a[cur]交换就完成了一趟快排。

在这里插入图片描述

void QuickSort(int* a, int begin, int end)
{
	// 区间不存在,或者只有一个值则不需要再处理
	if (begin >= end)
	{
		return; //递归出口,整个过程非常类似二叉树的前序遍历
	}
    // 当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差
    // 此时可以使用插排而不是快排
    // 还可以减少大量的递归次数,防止栈溢出
    if (end - begin > 10)
	{
		int keyi = PartSort3(a, begin, end);
		// [begin, keyi-1] keyi [keyi+1, end]
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
	else
	{
		InsertSort(a+begin, end - begin + 1);
	}
}
// 前后指针法
int PartSort(int* a, int begin, int end)
{
	int prev = begin;
	int cur = begin + 1;
	int keyi = begin;
	// 加入三数取中的优化,因为选取的key值会影响快排的效率
	int midi = GetMidIndex(a, begin, end);
	Swap(&a[keyi], &a[midi]);
    
	while (cur <= end)//begin和end是闭区间,大于end才结束
	{ 
		// cur位置的之小于keyi位置值
		if (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev], &a[cur]);
		++cur; 
	}

	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}

快速排序的非递归形式

// 递归大问题,极端场景下面,如果深度太深,会出现栈溢出
// 1、直接改循环 -- 比如斐波那契数列、归并排序
// 2、用数据结构栈模拟递归过程
// 栈里面的区间都会拿出来,单趟排序分割,子区间再入
void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st, end);
	StackPush(&st, begin);

	while (!StackEmpty(&st)) //栈为空就不用入了
	{
        // 先出左再出右
		int left = StackTop(&st);
		StackPop(&st);
        
		int right = StackTop(&st);
		StackPop(&st);
        
		int keyi = PartSort(a, left, right);
		// [left, keyi-1] keyi [keyi+1, right]
     
         // 先入左子区间和右子区间都无所谓
		if (keyi + 1 < right)
		{
             // 先入右再入左
			StackPush(&st, right);
			StackPush(&st,keyi + 1);
		}

		if (left < keyi - 1)
		{
             // 先入右再入左
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
	}

	StackDestroy(&st);
}

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UR8Eh03u-1683821857208)(D:\Desktop\5a101d5bcdaf4ce2826741f3e691bb7b.gif)]


递归版本

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;
    // i表示区间的最开始
	int i = begin1;
	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, (end - begin + 1)*sizeof(int));
}

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);
}

非递归版本

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越界,则可以不归并了
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			int m = end2 - begin1 + 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)* m);
		}
		gap *= 2;
	}
	free(tmp);
}

计数排序

计数排序是对哈希直接定址法的变形应用,先统计相同元素出现次数,再根据统计的结果将序列回收到原来的序列中。计数排序的缺点是:如果是浮点数、字符串无法解决,另外如果数据范围大,空间复杂度很高。计数排序只适合数据范围集中且重复数据量很大的情况。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6KvbmRm-1683821857208)(D:\Desktop\countingSort.gif)]

void CountSort(int* a, int n)
{
	int min = a[0], max = a[0];
	for (int i = 1; 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*)malloc(sizeof(int)*range);
	if (count == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	memset(count, 0, sizeof(int)*range);

	// 统计次数
	for (int i = 0; i < n; ++i)
	{
        // 相对于最小值储存出现的次数
		count[a[i] - min]++;
	}

	// 按照出现的次数写回原数组
	int j = 0;
	for (int i = 0; i < range; ++i)
	{
		// 出现几次就会回写几个i+min
		while (count[i]--)
		{
			a[j++] = i + min;
		}
	}
}

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

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

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mmWB7plc-1683821857209)(C:\Users\11794\AppData\Roaming\Typora\typora-user-images\image-20230512000528315.png)]


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

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

相关文章

vue3 nextTick()应用

在Vue3中&#xff0c;可以使用nextTick函数来延迟执行某些操作&#xff0c;这些操作会在下一次DOM更新周期之后执行。这个函数通常用于在数据更新后&#xff0c;等待DOM更新之后执行一些操作&#xff0c;比如获取DOM元素的尺寸、位置等。 nextTick() 例如&#xff0c;以下一个…

C语言—与坤(机)对弈

目录 设计思路 游戏运行效果 函数的声明 头文件game.h 游戏主体(源文件) 1.game.c 2.test.c 各文件的阐述 各部分的设计心得 1.打印菜单 2.初始化棋盘 3.打印棋盘 棋盘最终效果 1.打印数据 2.打印分割线 如何扩展为10 x 10 棋盘&#xff1f; 4.玩家下棋、电脑下…

mkv文件怎么转换成mp4?来看看这四种转换方式吧!

mkv文件怎么转换成mp4&#xff1f;在很多小伙伴看到&#xff0c;图片就是图片&#xff0c;音频就是音频&#xff0c;视频就是视频&#xff0c;它们展现给人们的效果都差不多。但实际上&#xff0c;不管是图片、视频还是音频文件&#xff0c;其中一个重要的区别在于它们的文件格…

照片尺寸怎么调整大小?三个方法,高效、快捷、安全!

照片尺寸怎么调整大小&#xff1f;照片是我们在日常生活和办公中经常会使用的文件类型之一。在制作各种文件、讲义、PPT、视频等内容时&#xff0c;图片都会成为重要的一部分。不同的图片格式和大小各有特点&#xff0c;有些图片虽然比较大但画质清晰&#xff0c;有些则方便传输…

选择排序,直接插入排序

目录 一、选择排序 1.基本思想 2.直接选择排序的流程 3.实验 二、直接插入排序法 1.基本思想 2.直接插入排序法的流程 3.实验 三、反向排序 1.实验 一、选择排序 与冒泡排序相比&#xff0c;直接选择排序的交换次数更少&#xff0c;所以速度会更快。 1.基本思想…

centos linux 配置私有网段并联网

文章目录 1. 创建虚拟机2. 虚拟机 A 配置网络3. 虚拟机 B 分配静态地址4. 测试 1. 创建虚拟机 vcenter 创建两台虚拟机A 、B&#xff0c;如何创建虚拟机请参考这里 虚拟机 A 具备两个网络接口&#xff0c;外网接口为 ens192 ip&#xff1a;192.168.22.6/20&#xff0c;网关为…

Golang tracing:与 OpenTelemetry、jaeger实现 跨服务 全链路追踪

使用 OpenTelemetry 链路追踪说明 工作中常常会遇到需要查看服务调用关系,比如用户请求了一个接口接口会调用其他grpc,http接口,或者内部的方法这样的调用链路,如果出现了问题,我们需要快速的定位问题,这时候就需要一个工具来帮助我们查看调用链路OpenTelemetry就是这样一个工…

Sui改进提案(SIPs)及其审核流程

SIPs提供了一个清晰透明的流程&#xff0c;使社区可以对Sui网络提出改进建议。 Sui基金会致力于打造一个开放协作的生态&#xff0c;因此推出了Sui改进提案&#xff08;SIPs&#xff0c;Sui Improvement Proposals&#xff09;&#xff0c;这是一个记录社区为去中心化未来的发…

如何利用API做好电商,接口如何凋用关键字

一.随着互联网的快速发展&#xff0c;电子商务成为了众多企业的首选模式&#xff0c;而开放API则成为了电商业务中不可或缺的部分。API&#xff08;Application Programming Interface&#xff09;&#xff0c;即应用程序接口&#xff0c;是软件系统不同组件之间交互的约定。电…

登录校验2.0

登录校验2.0 Filter Filter详解 过滤器Filter在使用中的一些细节&#xff0c;主要介绍一下3个方面的细节&#xff1a; 过滤器的执行流程过滤器的拦截路径配置过滤器链 执行流程 过滤器当中我们拦截到了请求之后&#xff0c;如果希望继续访问后面的web资源&#xff0c;就要…

内容编排与Kubernetes

第一节 内容编排与Kubernetes 为什么要用k8s 集群环境容器部署的困境&#xff0c;假设我们有数十台服务器。分别部署Nginx&#xff0c;redis&#xff0c;mysql&#xff0c;业务服务。如何合理的分配这些资源。这里就需要用到容器编排 容器编排 在实际集群环境下&#xff0…

线性结构-队列

队列是一种先进先出First In Fisrt Out,FIFO的线性表。 与一般的数组和链表不同&#xff0c;队列要求所有的数据只能从一端进入&#xff0c;从另一端离开。 输入进入的一端叫队尾rear&#xff0c;数据离开的一端叫队头front。 数据只能从队尾进入队列&#xff0c;从队头离开队…

VSCODE配置ROS编译环境

目录 一、安装插件 二、环境配置 2.1初始化工作空间 2.2配置VSCode 2.2.1创建功能包 2.2.2配置 c_cpp_properties.json 2.2.3配置 task.json 2.2.4配置 CMakeLists.txt 三、运行程序 3.1编译程序 3.2启动ros master 3.3执行可执行文件 用VSCode编辑ROS程序时&#xf…

linux 内核内存管理

物理内存 相关数据结构 page&#xff08;页&#xff09; Linux 内核内存管理的实现以 page 数据结构为核心&#xff0c;其他的内存管理设施都基于 page 数据结构&#xff0c;如 VMA 管理、缺页中断、RMAP、页面分配与回收等。page 数据结构定义在 include/linux/mm_types.h …

使用 Lambda 函数将 CloudWatch Log 中的日志归档到 S3 桶中

作者&#xff1a;SRE运维博客 博客地址&#xff1a;https://www.cnsre.cn/ 文章地址&#xff1a;https://www.cnsre.cn/posts/221205544069/ 相关话题&#xff1a;https://www.cnsre.cn/tags/aws/ 躺了好久&#xff0c;诈尸了。因为换了工作&#xff0c;所以比较忙一直没有时间…

解决APP抓包问题「网络安全」

1.前言 在日常渗透过程中我们经常会遇到瓶颈无处下手&#xff0c;这个时候如果攻击者从APP进行突破&#xff0c;往往会有很多惊喜。但是目前市场上的APP都会为防止别人恶意盗取和恶意篡改进行一些保护措施&#xff0c;比如模拟器检测、root检测、APK加固、代码混淆、代码反调试…

挖出api接口的重要性

作为一名软件开发者&#xff0c;API是我们工作中不可或缺的一部分。无论是将不同系统连接起来&#xff0c;还是构建多组件应用程序&#xff0c;API都是我们的核心工具之一。在本文中&#xff0c;我们将深入讨论API的技术细节和实际应用。 一.首先&#xff0c;我们来看看什么是…

怎么把mkv格式改成mp4?不妨试试这几种方法吧!

怎么把mkv格式改成mp4&#xff1f;mp4是一种多媒体封装格式&#xff0c;不过我们通常会将它说成是视频格式&#xff0c;它可以在一个文件中容纳无限数量的视频、音频、图片或字幕轨道&#xff0c;mp4格式也是被我们每个人所熟知&#xff0c;因为我们每个人几乎每天都会接触或者…

Spring入门教程

目录 一、Spring最基本的使用 1.创建Maven项目(不需要模板) 2.添加Spring框架支持 3.添加启动类(没啥可说的符合规范即可) 4.创建bean对象 5.将bean对象注册到Spring中 (a)先在resources文件夹中创建一个xml文件(注意:是test文件用了.xml后缀 不是直接创建一个xml文件) (…

[ChatGPT] 从 GPT-3.5 到 GPT-5 的进化之路 | ChatGPT和程序员 : 协作 or 取代

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐如果觉得文章写的不错&#xff0c;欢迎点个关注一键三连&#x1f609;有写的不好的地方也欢迎指正&#xff0c;一同进步&#x1f601;…