基础排序算法【快速排序+优化版本+非递归版本】

news2024/11/29 13:28:27

基础排序算法【快速排序+优化版本+非递归版本】💯💯💯

  • ⏰【快速排序】
    • ◽1.hoare法
    • ◽2.挖坑法
    • ◽3.前后指针法
    • ◽4.特性总结
  • ⏰【优化版本】
    • ◽1.随机选key
    • ◽2.三路取中
    • ◽3.小区间优化
  • ⏰【非递归版本】
  • ⏰【测试效率】
    • 排序OJ(可使用各种排序跑这个OJ)

在这里插入图片描述 请添加图片描述

⏰【快速排序】

快速排序是Hoare提出的一种二叉树结构的交换排序方法
> 基本思想:

任取待排序元素序列中的某个元素作为基准值key,按照该基准值将待排序列分成两个子序列,左子序列都比key小,右基准值都比key大。然后左右子序列重复上面的操作,直到所有元素都排列到相应的位置上为止。

而为什么说它是一种二叉树结构的交换排序方法呢?
是因为它的排序框架和二叉树的前序遍历规则十分相似。

进行一趟排序的效果是什么?

将小于key的值都放到key的左边,大于key的值都放到key的右边。

key将序列分成两个左右子序列,再对左右子序列重复上面的操作,就可以实现排序。而这一操作可以由递归实现。对左右子序列递归,直到没有子序列为止。

【快排基本框架】
void QuickSort(int *a, int left, int right)
{
	if (left>=right)
		return;

	// 按照基准值key对a数组的 [left, right)区间中的元素进行划分
	int keyi = partion(a, left, right);

	// 划分成功后以key为边界形成了左右两部分 [left, keyi-1] 和 [keyi+1, right)
	// 递归排[left, keyi-1]左子序列
	QuickSort(a, left, keyi-1);

	// 递归排[keyi+1+1, right)右子序列
	QuickSort(keyi, keyi + 1, right);
}

我们发现它与二叉树的前序遍历规则非常像,前序遍历是先访问根节点,然后再到左子树,右子树。
而这里类似,先将key排序好,然后再到左子序列,右子序列中去。所以我们在写递归框架时,可以想象二叉树前序遍历规则即可快速写出来。

◽1.hoare法

> 如何找key?

key的选值没有规定必须选哪一个,通常序列的最左边或者最右边作为key。

> 单趟排序:
单趟排序的效果也就是让key到正确的位置上去,key左边都比它小,右边都比它大。那如何做到这样呢?

  • 1.左边作key,右边先走。

  • 2.右边找比key小的值,左边找比key大的值。

  • 3.当两边都找到了就交换。

  • 4.当左边和右边相遇,将key和相遇点交换
    在这里插入图片描述

在这里插入图片描述

void Quicksort1(int*a,int left,int right)//需要区间范围
 {       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值与left和right相遇位置交换
	Swap(&a[keyi], &a[left]);
}

> 细节1

在找小找大的过程中,要注意lleft和right的范围,虽然在外面已经控制left小于right,但找小和找大的过程不受该限制,所以还需要判断一下。

> 细节2

在找小和找大的过程中,当遇到与key大小一样的值,该怎么办,当遇到与key一样的值时,想一下,这样的值在key的左边和右边都不影响结果,所以直接跳过即可,所以在找小找大时遇到相等的也跳过去。

> 问题1:为什么左边作key右边先走

> 问题2:为什么最后相遇点比key小

单趟排序写好了接下来就是写总趟了。
其实单趟写完后,剩下的就简单多了,单趟将key放入正确的位置,并且将序列分割成两个区间,在左区间中再选取个key,放入正确位置后,又分割成两个区间,在去左子序列中分割,直到最后子序列无法分割,递归就往回返。当左区间有序后再去分割右区间,右区间选key后再分割,跟左区间操作一致。
在这里插入图片描述

void Quicksort1(int*a,int left,int right)//需要区间范围
{
	//递归结束条件--子序列无法再分割
	if (left >= right)
		return;

	int keyi = left;
	int begin = left;//递归需要用到左右区间,需要改动区间的值,这里我们保存左右区间的值。
	int end = right;
	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值与left和right相遇位置交换
	Swap(&a[keyi], &a[left]);
	keyi = left;//要将最后keyi的位置更新下
	//begin   keyi-1 key keyi+1   end
	Quicksort1(a, begin, keyi - 1);//递归左区间
	Quicksort1(a, keyi + 1, end);//递归右区间
}

◽2.挖坑法

> 单趟排序:
挖坑法,本质上也是选出一个key,最后让key的左边都小于key,key的右边都大于key。

  • 1.先将key值保存下来,那key原先的位置上就形成一个坑

  • 2.右边找小,找到后将小的放入坑内,自己位置上形成坑

  • 3.左边找大,找到后将大的放入坑内,自己位置上形成坑

  • 4.最后左右相遇,将key的值放入坑内。

挖坑法其实和霍尔的方法目的是一样的,都是将小的甩到左边,大的甩到右边。只是方法不同。

在这里插入图片描述

在这里插入图片描述

    int key = a[left];
	int hole = begin;
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] >= key)
			--right;
		//找到之后将它甩到坑里,自己形成新的坑
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
			++left;
		//找到之后将它甩到坑里,自己形成新的坑
		a[hole] = a[left];
		hole = left;
	}
	//最后left和right相遇,将key插入到坑里
	a[hole] = key;

单趟排序完后的结果也是让key的左边都比key小,key的右边都比key大。
而接下来的总趟跟霍尔的一样,都是递归左右子序列即可。

//2.挖坑法
void Quicksort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	//将key的值保留
	int key = a[left];
	int begin = left;
	int end = right;
	int hole = begin;
	//三数取中,不是取数组中间的数,而是取三个数中中间的数--begin mid end
	int midi = Midi(a, begin, end);
	Swap(&key, &a[midi]);
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] >= key)
			--right;
		//找到之后将它甩到坑里,自己形成新的坑
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
			++left;
		//找到之后将它甩到坑里,自己形成新的坑
		a[hole] = a[left];
		hole = left;
	}
	//最后left和right相遇,将key插入到坑里
	a[hole] = key;
	Quicksort2(a, begin, hole - 1);//递归左子序列
	Quicksort2(a, hole + 1, end);//递归右子序列

}

◽3.前后指针法

第三种方法:前后指针法,顾名思义有两个指针cur和prev,该思路与霍尔和挖坑法截然不同。
它是将大的数据往右"推",将大的数据翻滚到右边去,小的数据交换到左边去。

> 单趟排序:

  • 1.刚开始prev指向起始位置(最左边作key),cur指向prev后面的位置

  • 2.cur用来找小,当cur往后找小时,找到之后,prev++,再将prev指向的元素和cur交换,cur再++。

  • 3.当cur遍历完序列后,将key直接和prev指向的数据交换即可。
    在这里插入图片描述

在这里插入图片描述

void Quicksort3(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyi = left;//最左边作key
	int cur = left + 1;//cur指向prev的后面
	int prev = left;//prev指向序列起始位置
	//cur用来找小,当cur找到小时,prev++,prev的值与cur的值交换,cur继续往后走
	while (cur <= right)//当cur遍历完序列才可以结束
	{
		if (a[cur] <= a[keyi] && ++prev != cur)//如果cur找到比key小的数,那cur和prev直接的就是紧挨着,prev++后就会跟cur一样那指向的值也是一样,那就没有必要交换。当cur没到到比key小的数时,prev和cur之间就会出现距离。
		{
			Swap(&a[cur], &a[prev]);
		}
		++cur;//不管cur找没找到比key小的数,cur都要走的
	}
	Swap(&a[prev], &a[keyi]);//最后再将prev指向的数与key交换,这个数一定是比key要小的。
	 keyi = prev;//交换完后需要更新key的位置。以便递归使用。
}

当一趟结束之后,将key排在合适的位置,就将序列分成两个子序列了,那再对两个子序列做相同的操作即可,用递归分治思想。

void Quicksort3(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyi = left;
	int cur = left + 1;
	int prev = left;
	//cur用来找小,当cur找到小时,prev++,prev的值与cur的值交换,cur继续往后走
	while (cur <= right)
	{
		if (a[cur] <= a[keyi] && ++prev!= cur)
		{
			Swap(&a[cur], &a[prev]);
		}
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	 keyi = prev;//更新key的位置
	Quicksort3(a, begin, keyi - 1);//递归左子序列
	Quicksort3(a, keyi + 1,end);//递归右子序列
}

> 注意:
prev与cur之间位置上有两种关系,要么prev紧跟着cur。
要么prev和cur之间隔着一段数据,该数据就是比prev大的数据。
当prev紧挨着cur的时,prev++就跳到cur的位置上去了,那prev的值和cur的值交换就没有意义了,因为它们代表的是同一个值。也就是如果一开始cur找小找到了,那么prev和cur就是紧挨着,当一旦找到一个比key大的数时,它们之间就会出现距离,不再紧挨着了。

◽4.特性总结

1.快速排序整体比其他排序都要好,所以才敢叫快排
2.时间复杂度O(N*logN)
在这里插入图片描述
3.空间复杂度:O(logN)
4.稳定性:不稳定。

⏰【优化版本】

想一想快排的时间复杂度是多少呢?如果我们从最坏情况讨论的话,当序列完全有序和完全逆序情况下,快速排序效率最低。
在这里插入图片描述
虽然从最后的情况下来看快速排序的时间复杂度为O(N^2)但是它是可以抢救的,它可以通过某种方法来提高效率,从而从O(N ^2)变成O(N*logN)。
在这里插入图片描述

◽1.随机选key

在这里插入图片描述

	//随机选key
	int randi = rand() % (right - left) + begin;
	Swap(&a[keyi], &a[randi]);

在单趟排序中:

void Quicksort1(int*a,int left,int right)//需要区间范围
{
	//递归结束条件
	if (left >= right)
		return;

	int keyi = left;
	int begin = left;
	int end = right;
	//随机选key
	int randi = rand() % (right - left) + begin;
	Swap(&a[keyi], &a[randi]);
   
	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值与left和right相遇位置交换
	Swap(&a[keyi], &a[left]);
	keyi = left;

随机选key的意义,本来最左边可能是最小值或者最大值,有可能会遇到有序情况和完全逆序情况,但是随机选key大大降低了这种可能,从而提高效率。

◽2.三路取中

三路哪三路呢?
左下标,右下标,和中间下标。
取中是什么意思?
我们要取的是三个下标中所代表的值是中间值的那个数。
找到之后,就将该中间值与最左边的key交换。那么key的值就不可能为最小值或者最大值。
这样就完全确定key不是最小值了,不像随机选key还有一丢丢的可能。
所以三路取中更科学更有可用性。

怎么取出三个数中的中中间值呢?就比较即可。

int Midi(int* a, int left, int right)
{
	int mid = (left + right) / 2;//获得中间下标
	if (a[left] > a[mid])
	{
		if (a[mid] > a[right])//a[left]>a[mid]>a[great]
		{
			return mid;//mid的是中间值下标
		}
		else//a[left]>a[mid]且a[right]>a[mid] 左右两个值都大于中间的,那左右两个值再比较得中间
		{
			if (a[left] > a[right])
			{
				return right;//right是两个数中小的,也就是三个数中中间数
			}
			else
			{
				return left;//否则left就是中间数的下标
			}
		}
	}
	else//a[left]<a[mid]
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else//a[left]<a[mid]且a[right]<a[mid]
		{
			if (a[left] > a[right])
			{
				return left;
			}
			else
			{
				return right;
			}
		}
	}
}

最后返回的都是三个数中中间值。

利用三路取中单趟排序:

void Quicksort1(int*a,int left,int right)//需要区间范围
{
	//递归结束条件
	if (left >= right)
		return;

	int keyi = left;
	//三数取中,不是取数组中间的数,而是取三个数中中间的数--begin mid end
	int midi=Midi(a, left, right);
	Swap(&a[keyi], &a[midi]);//将中间值与key交换,key就永远不可能为最小值或最大值了。
	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值与left和right相遇位置交换
	Swap(&a[keyi], &a[left]);
	keyi = left;

◽3.小区间优化

我们想一颗满二叉树它最后一层节点的个数是多少?
如果这颗数的深度为h,那么最后一层的节点数就是2^(h-1)而这么多节点,这么多节点需要大量的栈帧,而且最后几层的节点数是总课数节点的一半还多。而且最后一层要递归很多次才可以到达,这样的递归的缺点就显现出来了。所以有些人就在想当递归到一定程度就不再递归,用另一种方式进行排序。
也就是当区间不断的缩小,缩小到一定范围,用直接插入排序的效果要比递归好,这样也可以做到优化效果。

因为对于数据量小的,虽然区间多,但插入排序嘎嘎快,对于快速排序这些区间需要递归很多次才可以完成所以使用这样的小区间优化来提高效率。

//3.前后指针方法
void Quicksort3(int* a, int left, int right)
{
	if (left >= right)
		return;
	//三数取中,不是取数组中间的数,而是取三个数中中间的数--begin mid end
	int midi = Midi(a, left, right);
	Swap(&a[left], &a[midi]);
	int keyi = left;
	int cur = left + 1;
	int prev = left;
	
	//cur用来找小,当cur找到小时,prev++,prev的值与cur的值交换,cur继续往后走
	while (cur <= right)
	{
		if (a[cur] <= a[keyi] && a[++prev] != a[cur])
		{
			Swap(&a[cur], &a[prev]);
		}
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	 keyi = prev;
	 //小区间优化--当区间小时,直接使用插入排序
	 if(right-left+1>10)//当区间大于10时,仍然进行递归
	 {
	     Quicksort3(a, left, keyi - 1);
	     Quicksort3(a, keyi + 1,right);
	}
	else
	{
    	 InsertSort(a+left,right-left+1);//注意这里是将a+left传过去,因为不一定就是左区间,还可能是右区间
	}
}

⏰【非递归版本】

递归真正的问题是什么?

  • 1.效率不行,但现阶段效率方面都差不多

  • 2.深度太深,系统跑不过去会造成栈溢出。

因为递归是要消耗空间的,递归一次就需要建立一个栈帧,最后栈区域的内存不够了,就会造成栈溢出。
所以我们需要掌握这样的能力:将递归改成非递归。
递归改成非递归有两种思路:

  • 1.直接改成循环—如斐波那契数列

  • 2.使用(栈)辅助来改循环。

这里我们将会用栈来辅助将递归改成循环。
> 基本思路:
我们要思考排序递归函数里到底存放的是什么,每次递归什么发生了改变?对!
其实本质就是区间
每次递归函数区间都会被分割,缩小。所以我们在栈里存放的也就是区间通过栈里的区间对序列进行分割,不再使用递归分治的方法分割区间。

  • 1.首先将最初的序列左右区间入栈,要想使用左区间需要先将右区间入栈,然后左区间再入栈(栈的特点)。

  • 2.然后就开始使用栈来辅助修改循环。当栈不为空栈时,就将栈里区间出栈,一次出两个值,也就是左右区间。当左右区间出栈后,我们就可以根据左右区间对序列进行单趟排序选出key,key就将序列分成两个子序列,而这两个子序列又代表着两个区间,再将它们入栈,注意要先将右子序列区间入栈再将左序列区间入栈,然后再出栈重复上面的操作。

  • 3.当出栈的区间拿来分割子序列时,子序列无法再分割了没有区间,就不用入栈。

void QuickSortNoN(int* a, int left, int right)
{
	Stack st;
	StackInit(&st);//初始化栈
	//首先将序列的左右区间入栈
	StackPush(&st, right);
	StackPush(&st, left);
	//开始使用栈辅助修改循环
	while (!StackEmpty(&st))
	{
		//当栈不为空时,将区间出栈--出两个元素
		int begin = StackTop(&st);
		int end = StackTop(&st);
		//将区间出来后,就利用改区间对序列进行单趟排序
	
		int midi = Midi(a, left, right);
		Swap(&a[left], &a[midi]);
		int keyi = left;
		int cur = left + 1;
		int prev = left;
		//cur用来找小,当cur找到小时,prev++,prev的值与cur的值交换,cur继续往后走
		while (cur <= right)
		{
			if (a[cur] <= a[keyi] && a[++prev] != a[cur])
			{
				Swap(&a[cur], &a[prev]);
			}
			++cur;
		}
		Swap(&a[prev], &a[keyi]);
		keyi = prev;
		//[left   keyi-1]  [keyi+1  right] 
	 //单趟排序完后选出key ,而key又将序列分成两个子序列,相当于两个区间,将这两个区间入栈,注意先将右区间入栈,再入左区间
	//还有要注意的是,当子序列无法再分割时,无法产生区间时就不入栈

		if (left < keyi - 1)
		{
			StackPush(&st, right);
			STackPush(&st, keyi + 1);
		}
		if (keyi + 1 < right)
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
	}
	//当栈里没有区间时序列就已经排序好了。
}

⏰【测试效率】

接下来就是测试快排的效率如何了。
我们可以拿希尔排序进行比较,也可以拿快排自己的三种方法进行比较。

void QuickTest1()
{
	srand(time(0));
	const int N = 1000000;
	int* a2 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a2[i] = rand();
	}
	int begin1 = clock();
	Quicksort1(a2, 0, N - 1);
	int end1 = clock();
	printf("霍尔-Quicksort:%d\n", end1 - begin1);
	free(a2);
}
void QuickTest2()
{
	srand(time(0));
	const int N = 1000000;
	int* a2 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a2[i] = rand();
	}
	int begin1 = clock();
	Quicksort2(a2, 0, N - 1);
	int end1 = clock();
	printf("挖坑法-Quicksort:%d\n", end1 - begin1);
	free(a2);
}
void QuickTest3()
{
	srand(time(0));
	const int N = 1000000;
	int* a2 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a2[i] = rand();
	}
	int begin1 = clock();
	Quicksort3(a2, 0, N - 1);
	int end1 = clock();
	printf("前后指针-Quicksort:%d\n", end1 - begin1);
	free(a2);
}
void ShellsortTest()
{
	srand(time(0));
	const int N = 1000000;
	int* a2 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a2[i] = rand();
	}
	int begin1 = clock();
	Shellsort(a2, N);
	int end1 = clock();
	printf("希尔Shellsort:%d\n", end1 - begin1);
	free(a2);
}
int main()
{
	QuickTest1();
	QuickTest2();
	QuickTest3();
	ShellsortTest();
	return 0;

}

在这里插入图片描述

排序OJ(可使用各种排序跑这个OJ)

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

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

相关文章

CDH 6.3.2集群安装部署

文章目录CDH 6.3.2集群安装部署一 CDH概要1.1 CDH的背景1.2 CDH介绍二 环境准备2.1 安装部署环境2.2 部署规划2.3 服务器主机映射2.4 服务器免登录配置2.5 防火墙关闭2.6 关闭Selinux2.7 设置最大打开文件2.8 关闭最大透明页面压缩2.9 设置Swappiness空间2.10 安装JDK2.11 MySQ…

mysql中增删改成的练习

文章目录一、表的创建1.student表的数据2、课程表的数据course3、学生成绩表的数据二、操作序列1、查询计算机系cs的全体学生学号、姓名和性别2、检索选修了课程号为2的学生号和姓名3、检索至少选修了三门课以上的学生号4、检索选修了全部课程的学生5、在原表的基础上创建一个视…

java并发-线程池

java并发-线程池 线程池的介绍 Java线程池表示一组等待工作并多次重用的工作线程。在线程池的情况下&#xff0c;创建了一组固定大小的线程。服务提供者从线程池中拉出一个线程并分配一个作业。完成作业后&#xff0c;线程再次包含在线程池中。 使用线程池可以节省多线程应用…

Revit中如何绘制四面倾斜的屋顶?

一、Revit中如何绘制四面倾斜的屋顶&#xff1f; 如下图所示&#xff0c;像这种坡屋顶有两种方法进行绘制。具体操作如下。 第一种&#xff1a;定义坡度。 1、点击建筑选项卡中的屋顶按钮。 2、选择使用矩形工具。将偏移值修改为500。将屋顶迹线绘制出来。 3、选中全部屋顶迹…

信号浪涌保护器SPD选型方案

1.最大连续工作电压&#xff08;Uc&#xff09;&#xff1a;信号电路的最大电压必须小于Uc 最大连续工作电压Uc是最大工作信号电压&#xff0c;是电涌保护装置长期运行的信号电压或直流电压的最大有效值。这也是在额定漏电流条件下线与线之间或线与地之间的最大电压&#xff0…

UFT常用检查点的使用

录制时Flight自动打开。创建三个Action,分别命名为01_Login/02_New Order/03_Logout&#xff0c; 在Action里录制对应的Login/New Order/Logout操作。用飞机票预定系统订票&#xff0c;在02_New Order中设置起飞和到达城市参数化&#xff0c;用本地表数据&#xff08;至少2组数…

【排序算法(二)】选择排序(直接选择排序堆排序)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录1、直接选择排序1.1 …

腾讯云轻量应用服务器搭建网站教程(WordPress为例)

腾讯云轻量应用服务器搭建WordPress网站教程&#xff0c;先安装WordPress应用镜像&#xff0c;然后远程连接轻量应用服务器获取WP用户名和密码&#xff0c;域名DNS解析到轻量服务器IP地址&#xff0c;登陆WordPress后台管理全过程&#xff0c;腾讯云百科来详细说下腾讯云轻量服…

作用域、闭包的理解、GC算法

1、全局作用域的变量是不会自动的的释放掉的 例如在 <script >let title "hello world"function coming(){alter(title) }</script> 只要不关闭网页或者浏览器&#xff0c;我们都可以在控制台调用 2、作用域环境我们可以理解为一块内存数据 3、函数作…

研报精选230416

目录 【行业230416国联证券】汽车行业4月周报&#xff1a;上海车展期待值高&#xff0c;政策加码一体化压铸【行业230416国联证券】农林牧渔行业周报&#xff1a;低猪价加深去化幅度&#xff0c;高标准农田建设推进【个股230416安信证券_万华化学】Q1业绩超预期&#xff0c;TDI…

靶机精讲之Prime

主机发现 192靶机 扫描靶机 进行对端口的服务和版本的扫描 保存信息 扫描UDP 一般都是http优先然后再shh vuln脚本扫描 web渗透 查看源码 目录爆破 dirb 翻刚才扫到的目录 dirb指定文件类型扫描 读取文件 保存 浏览链接 wfuzz模糊测试 专门扫PHP 只有两目录 浏览页面 模糊…

react2:children属性 - props进阶 - 生命周期

children属性&#xff1a;父想给子组件传递内容&#xff0c;都需要通过children占位 children属性&#xff1a;类似vue中的slot效果 props 类型验证 &#xff1a;现在都是 typescript 替代了 ref 放普通标签上可以获取dom节点 ref 放组件上获取组件实例&#xff0c;可以调用组件…

「C/C++」C/C++内存四大分区

博客主页&#xff1a;何曾参静谧的博客 文章专栏&#xff1a;「C/C」C/C学习 目录术语一、文本区&#xff08;Text Segment&#xff09;/ 代码区二、数据区&#xff08;Data Segment&#xff09;/ 全局区三、栈区&#xff08;Stack Segment&#xff09;四、堆区&#xff08;Hea…

Redis应用问题解决

16. Redis应用问题解决 16.1 缓存穿透 16.1.1 问题描述 key对应的数据在数据源并不存在&#xff0c;每次针对此key的请求从缓存获取不到&#xff0c;请求都会压到数据源&#xff0c;从而可能压垮数据源。比如用一个不存在的用户id获取用户信息&#xff0c;不论缓存还是数据库…

美元霸权的潜在风险——无锚货币,为什么都要刺激消费

第三章 美元体系的风险结构与定价 金句导读 结果平等 没有智慧&#xff0c;优胜劣汰 没有良心。竞争都不充分。 年轻人有出路&#xff0c;穷人有活路&#xff0c;先富起来的人才能有后路。 名词解释&#xff1a; 对冲风险&#xff1a;我是卖面包的的&#xff0c;我还囤很多面…

【持续更新篇】SLAM视觉特征点汇总+ORB特征点+VINS前端

Harris角点 opencv函数 cornerHarris提取输入图像的Harris角点 检测原理 检测思想&#xff1a;使用一个固定窗口在图像上进行任意方向的滑动&#xff0c;对比滑动前后的窗口中的像素灰度变化程度&#xff0c;如果存在任意方向上的滑动&#xff0c;都有较大灰度变化&#xf…

Jetpack Compose 实战 宝可梦图鉴

文章目录前言实现效果一、架构介绍二、一些的功能点的介绍加载图片并获取主色,再讲主色设置为背景一个进度缓慢增加的圆形进度条单Activity使用navigation跳转Compose可组合项返回时页面重组的问题hiltViewModel()主要参考项目总结前言 阅读本文需要一定compose基础&#xff0…

Python爬虫|全国补充耕地项目数量爬取与分析——多进程案例

一、使用的库 import requests from lxml import etree import time import random import re import openpyxl import openpyxl from pyecharts.charts import Bar, Pie from pyecharts import options as opts from multiprocessing.dummy import Pool 二、数据爬取思路 1…

手拉手Centos7安装配置Redis7

Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 Redis是一个NoSQL数据库&#xff0c;常用缓存(cache) Re…

Spark 安装及WordCount编写(Spark、Scala、java三种方法)

Spark 官网&#xff1a;Apache Spark™ - Unified Engine for large-scale data analytics Spark RDD介绍官网&#xff1a;https://spark.apache.org/docs/2.2.0/api/scala/index.html#org.apache.spark.rdd.RDD 下载好spark解压mv到软件目录 linux>mv spark-xxx-xxx /op…