【数据结构之排序算法】

数据结构学习笔记---010

  • 数据结构之排序算法
    • 1、排序的基本概念及其运用
      • 1.1、常见排序算法的实现
    • 2、插入排序的实现
      • 2.1、直接插入排序
        • 2.1.1、直接插入排序的实现
          • 2.1.1.1、直接插入排序InsertSort.h
          • 2.1.1.2、直接插入排序InsertSort.c
          • 2.1.1.3、直接插入排序main.c
        • 2.1.2、直接插入排序的小结
      • 2.2、希尔排序
        • 2.2.1、希尔排序的实现
          • 2.2.1.1、希尔排序ShellSort.h
          • 2.2.1.2、希尔排序ShellSort.c
          • 2.2.1.3、希尔排序main.c
        • 2.2.2、希尔排序的小结
    • 3、选择排序的实现
      • 3.1、直接选择排序
        • 3.1.1、直接选择排序的实现
          • 3.1.1.1、直接选择排序SelectSort.h
          • 3.1.1.2、直接选择排序SelectSort.c
          • 3.1.1.3、直接选择排序main.c
        • 3.1.2、直接选择排序的小结
      • 3.2、堆排序
        • 3.2.1、堆排序的实现
          • 3.2.1.1、堆排序HeapSort.h
          • 3.2.1.2、堆排序HeapSort.c
          • 3.2.1.3、堆排序main.c
        • 3.2.2、堆排序的小结
    • 4、交换排序的实现
      • 4.1、冒泡排序
        • 4.1.1、冒泡排序的实现
          • 4.1.1.1、冒泡排序BubbleSort.h
          • 4.1.1.2、冒泡排序BubbleSort.c
          • 4.1.1.3、冒泡排序main.c
        • 4.1.2、冒泡排序的小结
      • 4.2、快速排序
        • 4.2.1、快速排序的实现
          • 4.2.1.1、快速排序QuickSort.h
          • 4.2.1.2、快速排序QuickSort.c
          • 4.2.1.3、快速排序main.c
        • 4.2.2、快速排序的小结
    • 5、计数和归并排序的实现
      • 5.1、计数排序
        • 5.1.1、计数排序的实现
          • 5.1.1.1、计数排序CountSort.h
          • 5.1.1.2、计数排序CountSort.c
          • 5.1.1.3、计数排序main.c
        • 5.1.2、计数排序的小结
      • 5.2、归并排序
        • 5.2.1、归并排序的实现
          • 5.2.1.1、归并排序MergeSort.h
          • 5.2.1.2、归并排序MergeSort.c
          • 5.2.1.3、归并排序main.h
        • 5.2.2、归并排序的小结
    • 6、排序算法复杂度及稳定性分析
      • 6.1、归并排序AllSort.h
      • 6.2、归并排序AllSort.c
      • 6.3、归并排序main.c
    • 7、排序算法总结

数据结构之排序算法

前言:
前篇学习了 数据结构的二叉树的知识,那么这篇继续学习关于数据结构的排序算法知识。

/知识点汇总/

1、排序的基本概念及其运用

记录:在排序问题中,通常将数据元素称为记录。
排序:所谓的排序,就是使一串记录,按照其中某个关键字的大小,递增或递减的排序起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = t[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍然在r[j]之前,则称这种排序算法是稳定的。否则称为不稳定的。
正序、逆序:若待排序的记录序列已排好序,称此纪录序列为正序;若待排序列记录的排序顺序与排好序的顺序正好相反,称此纪录序列为逆序或反序。
:在排序过程中,将待排序的记录序列扫描一遍称为一趟。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

运用
生活中各种排序的需求场景。 比如,学校成绩排名,物品的销量排名等。

1.1、常见排序算法的实现

常见排序算法的实现分类
1.插入排序:直接插入排序,希尔排序
2.选择排序:选择排序,堆排序
3.交换排序:冒泡排序,快速排序
4.计数和归并排序:计数排序、归并排序

2、插入排序的实现

插入排序:已知一组数据,插入数据使其逐次有序。
插入排序是一种简单的插入排序法其基本思想
待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

2.1、直接插入排序

直接插入排序是指插入排序中最简单的排序方法,类似于玩纸牌时整理手中纸牌的过程,其基本思想是依次将待排序序列中的每一个记录插入已知排好的序列中,直到全部都排好序。
存在以下两个关键性问题:
1.如何构造初识的有序序列?
2.如何查找待插入的有序序列?

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

2.1.1、直接插入排序的实现

直接插入排序的实现:普遍采用类似于分治的思想,先控制好单趟排序。先局部再整体。
声明和定义分离
直接插入排序InsertSort.h
直接插入排序InsertSort.c
直接插入排序main.c

2.1.1.1、直接插入排序InsertSort.h

放置头文件,函数声明,注释信息。后面的头文件同理,不多赘述。

#include  <stdio.h>
//直接插入排序
void InsertSort(int* a, int n);
//打印数据元素
void PrintArray(int* a, int n);
2.1.1.2、直接插入排序InsertSort.c

根据分治思想,所以先思考编写单趟的思路,直接插入的算法思想就是将一个新元素,在已经有序的序列中,去进行比较再插入的操作。但是我们一开始并不知道要插入的序列是否有序,那么不妨假设 [0,end] 闭区间为有序的序列,继续将end+1作为要插入的新元素。所以,不管我们一开始的初识序列是否有序,能确定的是只有第一个元素那么肯定有序,然后再根据假设的区间进行比较插入操作,再后移插入之后的元素即可,所以单趟的思路就是如此。
简述核心就是,将end+1的元素与end的元素比较,大就直接插入在end后面,有序序列范围就是[0,end+1];否则,小就将end右移,范围[0,end].单趟的比较插入就是这样。多趟就是在前面的基础上,逐步n次(n为问题的规模)循环即可。
为了方便理解,画出单趟的图形演示,如下图所示
在这里插入图片描述

边界考虑:当end+1下标的新元素,比有序序列的首元素都小,那么就得考虑边界问题,避免非法访问等问题。
在这里插入图片描述
单趟插入代码实现

//单趟
void InsertSortByOnce(int* a, int len)
{
   
	//假设有序序列:[0,end]
	//新元素下标:end+1
	int end;
	int tmp = a[end + 1];//保存新元素值至变量tmp
	//边界
	while (end >= 0)
	{
   
		//比较end下标的元素的大小
		if (tmp < a[end])
		{
   
			a[end + 1] = a[end];//后移
			--end;//更新继续比较的下标
		}
		else //tmp >= a[end]
		{
   
			break;
		}
	}
	//尾插,同时解决end移到了 end = -1的情况三。
	a[end + 1] = tmp;
}

多趟插入代码实现
多趟只需要把单趟执行循环衔接起来即可。end的初始值与序列的数据个数相关,所以end = i;其次,多趟循环的边界,因为下标从0开始那么应该是在n-1结束,防止越界,又因为参数是传入的数组长度。

#include "InsertSort.h"

//直接插入排序 -- O(N^2)
void InsertSort(int* a, int n)
{
   
	for (int i = 0; i < n-1; i++)//结束位置n-1,满足防止越界
	{
   
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
   
			if (tmp < a[end])
			{
   
				a[end + 1] = a[end];
				--end;
			}
			else
			{
   
				break;
			}
		}
		//同时解决end移到了 end = -1的情况。
		a[end + 1] = tmp;
	}
}

//打印数据元素
void PrintArray(int* a, int n)
{
   
	for (int i = 0; i < n; i++)
	{
   
		printf("%d ", a[i]);
	}
	printf("\n");
}
2.1.1.3、直接插入排序main.c
#include "InsertSort.h"

int main()
{
   
	int arr[] = {
    3,1,4,6,2,8,5,0,7,10 ,6};
	InsertSort(arr, sizeof(arr) / sizeof(arr[0]));
	PrintArray(arr, sizeof(arr) / sizeof(arr[0]));
	return 0;
}

测试运行结果
在这里插入图片描述

2.1.2、直接插入排序的小结

1.元素集合越接近有序,直接插入排序算法的实践效率越高;
2.时间复杂度为:O(n^2) 最好情况是:O(N) --> 默认顺序恰好有序
3.稳定性:稳定

2.2、希尔排序

希尔排序就是对直接插入排序的改进。希尔排序通过引入“增量”概念,将整个待排序的记录序列分割成若干子序列分别进行直接插入排序,从而减少数据移动的次数,提高排序效率。
基本思想:先确定一个增量gap,是将整个待排序记录序列分割为若干个gap长度的子序列,在子序列内分别进行直接进行插入排序重复上述分组和排序的工作,待整个序列基本有序,gap=1时再对全部记录进行一次直接插入排序
存在两个关键性问题;
1.应如何分割待排序记录,才能保证整个序列逐步向基本有序发展?
2.子序列内如何进行直接插入排序?

2.2.1、希尔排序的实现

希尔排序的实现:普遍采用于分治的思想,先控制好单趟排序。即先局部再整体。
声明和定义分离
直接插入排序ShellSort.h
直接插入排序ShellSort.c
直接插入排序main.c

两个步骤
步骤1.预排序(使接近有序) – 引入gap增量划分为若干组,再分别每组进行插入排序;
gap取得越大,待排序序列中大的值更快调到后面,小的值更快调到前面,但越不接近于有序;
gap取得越小调得越慢,但是越接近有序;
当gap == 1 时就是直接插入排序了。
步骤2.预排序后,最后统一进行,直接插入排序。

那么如何决定gap的大小呢?
首先肯定不是定值,也可以发现gap常常随着问题规模而变化;
所以总结出来一般有两种方法:关键在于始终要保证gap最后都能化为1。
根据数的奇偶性:gap /= 2(gap>1) 或者gap /= 3 + 1

2.2.1.1、希尔排序ShellSort.h
#include  <stdio.h>
//希尔排序
void ShellSort(int* a, int n);
//打印数据元素
void PrintArray(int* a, int n);
2.2.1.2、希尔排序ShellSort.c

根据分治思想,首先得思考单趟的排序处理,而且希尔的主要核心思想就是上述的两个步骤,即根据gap执行预排序,当gap=1时,再执行直接插入排序。
那么单趟的思路:首先确定一个gap值(这里以gap=3为演示),然后将待排序的序列按照gap为间距进行分组,再然后将每组分别进行直接插入排序(注意:并没有改变每组元素各个下标,只在各自组内下标位置间进行插入排序),最后在根据组数进行边界考虑,分析是否越界的情况。边界恰好是每组越界前的元素下标是每组的最后一个元素,所以得出结论,边界控制为n-gap

为了方便理解,画出单趟的图形演示,如下图所示
在这里插入图片描述

单趟希尔代码实现

//单趟思路实现
void ShellSortByOnce(int* a, int n)
{
   
	//单趟
	for (int j = 0; j < gap; j++)//gap组
	{
   
		//第一组,i=0
		//i = j,j巧妙决定了为第几组
		for (int i = j; i < n - gap; i += gap) //相当于全局替1为--> gap
		{
   
			int end = i;
			int tmp = a[end + gap];//每组各个元素间距gap
			while (end >= 0)
			{
   
				if (tmp < a[end])
				{
   
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
   
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

明白了单趟的思路,那么多趟就是循环嵌套,其中值得注意的是,这里单趟只是为了方便演示数据gap取的定值3,但是实际应用中,gap是非定值,所以关于gap具体怎么取?以及gap取多少合适?
书上说,关键在于始终要保证gap最后都能化为1。
所以,提供两种常用的方法:根据数的奇偶性:gap /= 2(gap>1) 或者gap /= 3 + 1

多趟希尔代码实现

//希尔排序
void ShellSort(int* a, int n)
{
   
	//int gap = 3;
	int gap = n;
	while (gap > 1)//直至gap等于1.执行最后一次直接插入排序,完成排序
	{
   
		//gap = gap / 2;
		gap = gap / 3 + 1;
		//单趟
		for (int j = 0; j < gap; j++)//gap组
		{
   
			//第一组
			//i = j,j巧妙决定了为第几组
			for (int i = j; i < n - gap; i += 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;
					}
					else
					{
   
						break;
					}
				}
				a[end + gap] = tmp;
			}
		}
	}
}

当然还有更加新奇的实现思路,巧妙地运用各组各元素和gap的关系,实现多组并排反复横跳进行比较交换数据的操作,具体可以看我下面画的演示图:
在这里插入图片描述

多组并排希尔代码实现

//希尔排序
//写法2:优化算法 -- 多组并排
void ShellSort(int* a, int n)
{
   
	//int gap = 3;
	int gap = n;
	while (gap > 1)
	{
   
		//gap = gap / 2;
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++) //i++决定了依次反复切换不同组进行插入排序
		{
   
			//单趟
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
   
				if (tmp < a[end])
				{
   
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
   
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}
//打印数据元素
void PrintArray(int* a, int n)
{
   
	for (int i = 0; i < n; i++)
	{
   
		printf("%d ", a[i]);
	}
	printf("\n");
}

可知,多组并排与各个组排的关键区别就在于 i++ 和 i += gap。

2.2.1.3、希尔排序main.c
#include "ShellSort.h"

int main()
{
   
	int arr[] = {
    3,1,4,6,2,8,5,0,7,10 ,6};
    ShellSort(arr, sizeof(arr) / sizeof(arr[0]));
	PrintArray(arr, sizeof(arr) / sizeof(arr[0]));
	return 0;
}

测试运行结果
在这里插入图片描述

2.2.2、希尔排序的小结

希尔排序的特性总结:
1.希尔排序就是对直接插入排序的改进。
2.当增量gap>1时,都是预排序,目的是让数组更接近于有序;当gap == 1时,数组已经接近于有序的了,这样就会很快。这样整体而言,可以达到优化效果。
3.希尔排序的时间复杂度不好确定(通过大量数据测试大约处于O(N^1.3),因为根据实际情况的增量并不相同,所以根据书上说的,希尔提出的最早方法是:d1= [n/2] d(i+1) = [di/2],且增量序列互质,显然最后一个增量必须是1。
4.稳定性:所以也就得出该排序算法并不稳定。

3、选择排序的实现

选择排序基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在起始位置,直到全部待排序的数据元素排完。

3.1、直接选择排序

直接选择排序(简单选择排序)是选择排序中最简单的排序方法;
其基本思想:是从第i趟排序在待排序r[i]~r[n]中选取最小或最大的数据元素;若它不是这组元素中的最后一个(或第一个)元素,则将它与这组元素中的最后一个(第一个)元素进行交换;并在剩余的数组集合中,重复直到剩余最后一个元素。
存在两个关键性问题:
1.如何在待排序中选出最小的记录?
2.如何确定待排序序列中的最小记录在有序序列中的位置?

3.1.1、直接选择排序的实现

直接插入排序的实现:根据直接选择的思想,得要明确对最大值或最小值的处理,以及比较和交换。
声明和定义分离
直接选择排序SelectSort.h
直接选择排序SelectSort.c
直接选择排序main.c

3.1.1.1、直接选择排序SelectSort.h
#include  <stdio.h>

//直接选择排序
void SelectSort(int* a, int n);
//打印数据元素
void PrintArray(int* a, int n);
3.1.1.2、直接选择排序SelectSort.c

首先,知道选择排序的思想,直接选择,最大值或最小值,然后分别放在最左边的位置或最右边的位置,那么既然如此就设计一个区间,以分治的思想一个区间一个区间的,完成每一个区间的最小值和最大值,再分别放入当前区间的最小位置和最大位置。依次类推完成排序即可。
为了方便理解,画出图形大致演示一下,如下图所示
在这里插入图片描述
直接选择排序代码实现

#include "SelectSort.h"

//直接选择排序   最好的情况:O(N^2)
static void Swap(int* p1, int* p2)
{
   
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void SelectSort(int* a, int n)
{
   
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
   
		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]);
		if (maxi == begin)
		{
   
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);
		++begin;
		--

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

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

相关文章

【深度学习:开源数据注释】开源数据注释完整指南

【深度学习&#xff1a;Automated Data Annotation】开源数据注释完整指南 什么是开源数据标注工具&#xff1f;您会使用开源标签工具做什么&#xff1f;主要的开源数据标注工具有哪些&#xff1f;CVATMONAI LabelLabelMeRIL-ContourSefexa 使用开源注释工具的优点和缺点是什么…

openEuler 22.03 LTS 上源码安装 PostgreSQL 15

安装PostgreSQL 15 1 安装必要的依赖 #yum install -y readline-devel zlib-devel gcc2、下载源码 # wget https://ftp.postgresql.org/pub/source/v15.6/postgresql-15.6.tar.gz # tar -xzvf postgresql-15.6.tar.gz3 配置 # cd postgresql-15.6/ # ./configure4 编译安装…

RK3399平台开发系列讲解(调试篇)死锁检测工具lockdep

🚀返回专栏总目录 文章目录 一、常见死锁场景二、lockdep使用方法三、lockdep技术原理3.1、锁类状态3.2、检查规则沉淀、分享、成长,让自己和他人都能有所收获!😄 📢介绍死锁检测工具lockdep。 资料 一、常见死锁场景 场景1:进程重复申请同一个锁,称为AA死锁。例如…

qt 开发 “控件之家“

本篇文章我们来描述一下Qt 控件 是qt中最基本 也是最难缠的 有种“小鬼难缠的感觉” qt常用控件大集合 Qt是一个广泛使用的跨平台应用程序框架&#xff0c;它提供了许多用于构建图形用户界面(GUI)的控件。以下是一些Qt中常用的控件&#xff1a; QPushButton&#xff1a;这是…

LV.23 D2 开发环境搭建及平台介绍 学习笔记

一、Keil MDK-ARM简介及安装 Keil MDK&#xff0c;也称MDK-ARM&#xff0c;Realview MDK &#xff08;Microcontroller Development Kit&#xff09;等。目前Keil MDK 由三家国内代理商提供技术支持和相关服务。 MDK-ARM软件为基于Cortex-M、Cortex-R4、ARM7、ARM9处理器设备…

基于requests框架实现接口自动化测试项目实战

requests库是一个常用的用于http请求的模块&#xff0c;它使用python语言编写&#xff0c;在当下python系列的接口自动化中应用广泛&#xff0c;本文将带领大家深入学习这个库&#xff0c;Python环境的安装就不在这里赘述了&#xff0c;我们直接开干。 01、requests的安装 wi…

深夜突发! OpenAI震撼发布了SORA文生视频模型,对职场人的影响可能跟你想的不一样

深夜突发! OpenAI震撼发布了SORA文生视频模型&#xff0c;对职场人的影响可能跟你想的不一样。 马上就要节后返工了&#xff0c;顾问老师也早已回到了温暖的广州。与一位同城的学员相聚在老广州的一个茶楼中&#xff0c;喝起了下午茶。面对各式的广式茶点&#xff0c;在淡淡的茶…

C++并发编程 -3.同步并发操作

本文介绍如何使用条件变量控制并发的同步操作、C 并发三剑客&#xff0c;函数式编程 一.条件变量 1.概念 C条件变量&#xff08;condition variable&#xff09;是一种多线程编程中常用的同步机制&#xff0c;用于线程间的通信和协调。它允许一个或多个线程等待某个条件的发生…

迷失在前端框架中的初级开发者,总觉得大厦要从二层开始建

知乎有人提问&#xff1a;现在是框架主导前端时代&#xff0c;还有必要学习Html&#xff0c;CSS和JavaScript吗&#xff1f;我看很愕然&#xff0c;框架可以节省力气&#xff0c;难道都可以替代前端基础了吗&#xff1f; 一、起因 因为贝格前端工场的主营业务就是前端开发&…

【中英双语】OpenAI Sora文本转视频模型的技术分析!全新的AI视频叙事时代即将到来!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

Shellcode免杀对抗(C/C++)

Shellcode C/C免杀&#xff0c;绕过360安全卫士、火绒安全、Defender C/C基于cs/msf的上线 首先是测试一下shellcode上线&#xff0c;主要是俩种方法 测试环境 攻击机&#xff1a;kali2023 靶机&#xff1a;win10 msf方法 首先是启动msf msfconsole 然后msf生成一个sh…

【C语言】linux内核tcp/ip协议代码

一、linux内核tcp/ip协议源码有哪些&#xff1f; Linux内核中实现TCP/IP协议栈的源代码主要位于内核源码树的net/ipv4和net/ipv6目录下&#xff0c;针对IPv4和IPv6协议。不单是TCP/IP协议&#xff0c;还包含了UDP以及其他相关的网络层协议实现。 这里是一些与TCP/IP协议栈相关…

云计算基础-存储虚拟化(深信服aSAN分布式存储)

什么是存储虚拟化 分布式存储是利用虚拟化技术 “池化”集群存储卷内通用X86服务器中的本地硬盘&#xff0c;实现服务器存储资源的统一整合、管理及调度&#xff0c;最终向上层提供NFS、ISCSI存储接口&#xff0c;供虚拟机根据自身的存储需求自由分配使用资源池中的存储空间。…

对账中心系统架构设计与实现的实践总结

随着数字化时代的到来&#xff0c;越来越多的企业开始使用对账中心系统来管理其财务交易。对于一个成功的对账中心系统&#xff0c;其架构设计和实现非常关键。本文将探讨对账中心系统架构设计与实现的重要性、关键原则和实施过程中需要考虑的要点&#xff0c;帮助企业构建强大…

模型训练 —— AI算法初识

一、背景 AI算法中模型训练的主要目的是为了让机器学习算法从给定的标注数据中学习规律、特征和模式&#xff0c;并通过调整模型内部参数&#xff0c;使模型能够对未见过的数据进行准确预测或决策。具体来说&#xff1a; 1. **拟合数据**&#xff1a;模型通过训练来识别输入数…

【Spring篇】 项目加盐加密处理

目录 1. MD5 加密算法 2. 加盐加密流程 3. Spring Security 实现加盐加密 1. 添加 Spring Security 框架 2. 关闭 Spring Security 认证 3.实现加盐加密 1. MD5 加密算法 MD5 是 Message Digest Algorithm 的缩写&#xff0c;译为信息摘要算法&#xff0c;它是 Java …

QGIS004:【08图层工具箱】-导出到电子表格、提取图层范围

摘要&#xff1a;QGIS图层工具箱常用工具有导出到电子表格、提取图层范围等选项&#xff0c;本文介绍各选项的基本操作。 实验数据&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ZK4_ShrQ5BsbyWfJ6fVW4A?pwdpiap 提取码&#xff1a;piap 一、导出到电子表格 工具…

【Web】CTFSHOW java刷题记录(全)

目录 web279 web280 web281 web282 web283 web284 web285 web286 web287 web288 ​web289 web290 web291 web292 web293 web294 web295 web296 web297 web298 web299 web300 web279 题目提示 url里告诉我们是S2-001 直接进行一个exp的搜 S2-001漏洞分析…

文件夹删不掉,显示在另一个文件中打开怎么办

问题&#xff1a; 一、想要删掉这个文件夹&#xff0c;却因为文件夹中的文件打开了删不掉&#xff0c;这里我因为做的测试&#xff0c;所以是知道打开了什么 二、一般情况下文件比较多时&#xff0c;是不知道打开了什么的&#xff0c;长这个样子 解决&#xff1a; 一、打开任…

JAVA面试题并发篇

1. 线程状态 要求 掌握 Java 线程六种状态 掌握 Java 线程状态转换 能理解五种状态与六种状态两种说法的区别 六种状态及转换 分别是 新建 当一个线程对象被创建&#xff0c;但还未调用 start 方法时处于新建状态 此时未与操作系统底层线程关联 可运行 调用了 start 方法…