【排序】快速排序(C语言实现)

文章目录

  • 前言
  • 1. Hoare思想
  • 2. 挖坑法
  • 3. 前后指针法
  • 4. 三路划分
  • 5. 快速排序的一些小优化
    • 5.1 三数取中
      • 常规的三数取中
      • 伪随机的三数取中
    • 5.2 小区间优化
  • 6. 非递归版本的快排
  • 7. 快速排序的特性总结




前言


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

本章会重点讲解快速排序的四种思想:

  1. Hoare思想
  2. 挖坑法
  3. 前后指针法
  4. 三路划分

以及快速排序的一些小优化,和递归与非递归的实现方式。

阅读本章建议有一点二叉树的基础,如果对二叉树不太了解的可以看看:《初阶二叉树》这篇文章


1. Hoare思想


我们先看一下Hoare的思想是怎么样实现的:
请添加图片描述
这里来解释一下这个思想的意思:

  1. 首先我们将数组起始位置的数据定义成key
  2. 我们从右边开始往前找一个比key的数
  3. 找到比key小的数,我们在从左边开始找一个比key的数
  4. 做完上面两个步骤以后,当R与L相遇时,我们再将key放到R和L相遇的位置上,此刻key已经排到了它所应该在的位置(就是排好序以后的位置),我们介绍的这几种方法本质都是将key放到它所应该在的位置

注意⚠️:
在实现这个代码之前,我们要注意几个点:

  1. 如果是排升序的话,一定是要先从右边开始找,再从左边找,这样就能确保找到的数,一定是小于key的,具体原因如下:
    在这里插入图片描述
  2. 我们要注意如果数组中所有数都比key大,那么我们往右边找比key小的数的时候,控制不当就会导致越界;同理,如果数组中所有的数都比key要小,那么我们往左边找比key大的数的时候,控制不当也会导致越界。
  3. 我们在交换R和L的值的时候,可能会遇到R和L遇到等于key的数,如果不做处理的话,这样R和L就会一直交换,导致死循环。

说了那么多,我们一起来看看Hoare版的快排用代码是怎么实现的:

int PartSort_Hoare(int* a, int begin, int end)
{
	int left = begin;
	int right = end - 1;
	// 在这块将key定义成下标,
	// 因为我们在后面的过程中会涉及将key与我们锁定的位置的数交换位置,
	// 如果写成int key = a[left]就不方便后面的交换
	int key_i = left;

	while (left < right)
	{
		// 先走右边,再走左边
		while (left < right && a[right] >= a[key_i]) // 防止越界 && 找小
		{
			--right;
		}

		while (left < right && a[left] <= a[key_i]) // 防止越界 && 找大 
		{
			++left;
		}
		// 找到大的数和小的数,将它们交换位置
		Swap(&a[left], &a[right]);
	}
	// 出循环以后此刻left=right
	// 将在left位置和key_i位置的数交换一下位置
	Swap(&a[left], &a[key_i]);

	return left;
}

void Quick_Sort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int key = PartSort_Hoare(a, begin, end); 
	// 区间分为: [begin, key) key[key + 1, end), 这里要注意, 我们给的区间是左闭右开的
	Quick_Sort(a, begin, key);
	Quick_Sort(a, key + 1, end);

}

这里有一个细节不知道大家有没有发现
在这里插入图片描述
我在处理这两个地方的时候是先判断left是否小于right,这样处理的好处是有效的防止了我们对数组的越界访问,如果是后判断left是否小于right,那么在进行a[right] >= a[key_i]这步操作的时候,我们可能会进行越界访问数组的操作。


2. 挖坑法


我们先看一下挖坑法是怎么样实现的:
请添加图片描述

这里来解释一下这个思想的意思:

  1. 先将第一个数据存放在一个临时变量key中,然后形成一个坑位
  2. 右边开始找一个key小的数,将这个数放进坑位中,然后又将这个位置设置成坑位
  3. 左边开始找一个key大的数,将这个数放进坑位中,然后又将这个位置设置成坑位
  4. 当L和R相遇时,此时坑位一定是在L和R的交点处,我们将前面保存的临时变量key放入坑位中,此刻key已经排到了它所应该在的位置(就是排好序以后的位置)

我们来看一下挖坑法实现快排用代码是怎么实现的:

int PartSort_Hole(int* a, int begin, int end)
{
	int left = begin;
	int right = end - 1;
	// 这里不用像Hoare法那样设置成下标,
	// 因为我们在最后是直接将数据放入坑位中
	int key = a[begin];
	int hole = left; // 设置坑位
	while (left < right)
	{
		// 先从右边走
		while (left < right && a[right] >= key)  //  防止越界 && 找小
		{
			--right;
		}
		//找到小于key的数了,将该数放在hole中,再将该位置设置为hole
		a[hole] = a[right];
		hole = right;

		while (left < right && a[left] <= key)  // 防止越界 && 找大
		{
			++left;
		}
		//找到小于key的数了,将该数放在hole中,再将该位置设置为hole
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;

	return hole;
}

void Quick_Sort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int key = PartSort_Hole(a, begin, end);
	// 区间分为: [begin, key) key[key + 1, end), 这里要注意, 我们给的区间是左闭右开的
	Quick_Sort(a, begin, key);
	Quick_Sort(a, key + 1, end);
}


3. 前后指针法


我们先看一下前后指正法是怎么样实现的:
请添加图片描述
这里来解释一下这个思想的意思:

  1. 我们先定义两个指针prevcurkeyprevkey指向数组的起始位置,cur指向prev的下一个位置
  2. 我们先移动cur,找到比key小的数,就将cur位置的数据和 prev+1位置的数据交换位置,但这里出于考虑省略一些交换数据的消耗,我们可以判断prev+1位置的数据是否等于cur位置的数据,如过a[cur] < cur && a[prev+1] != a[cur],就将两个位置的数据进行交换
  3. 直到cur指针越界,我们这个过程就结束了,然后再将key的值放到prev的位置,此刻key已经排到了它所应该在的位置(就是排好序以后的位置)

我们来看一下前后指针实现快排用代码是怎么实现的:

int PartSort_PC_Point(int* a, int begin, int end)
{
	// 创建两个变量来指向数组
	int prev = begin;
	int cur = prev + 1;
	// 这里将key设置成
	int key_i = begin;

	while (cur < end)
	{
		if (a[cur] < a[key_i] && ++prev != cur) // 找比key小的值 && 防止与自身交换
		{
			Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	// 此时cur已经比right大了,将prev与key_i位置的数换位置
	Swap(&a[key_i], &a[prev]);
	return prev;
}

void Quick_Sort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	int key = PartSort_PC_Point(a, begin, end);
	// 区间分为: [begin, key) key[key + 1, end), 这里要注意, 我们给的区间是左闭右开的
	Quick_Sort(a, begin, key);
	Quick_Sort(a, key + 1, end);
}


4. 三路划分


三路划分这个思想用来处理大量重复数据的时候,效率特别高。

我们先看一下三路划分是怎样实现的:
请添加图片描述
这里来解释一下这个思想的意思:

  1. 我们先定义一个key等于数组起始位置的数据 ,之后在数组起始位置定义一个L指针,在L的下一个位置定义一个cur指针,最后在末尾位置定义一个R指针
  2. 我们从cur位置开始,找一个比key小的数,找到以后,交换curL的位置的值,并且++cur++L
  3. 如果cur等于key,++cur
  4. 如果cur遇到一个比key大的数,交换交换curR的位置的值,并且--R,这里要注意不能动cur的值,因为从R位置交换过来的值,我们没办法判断它与key的大小关系,所以保持cur的位置不变,再继续将cur位置的值与key做比较
  5. cur > R的时候,这个过程就结束了

在实现这个代码的过程中,我们需要返回的是一个范围:[begin,L)[R + 1,end)

为什么是这样呢?
在这里插入图片描述
因为我们在进行一次三路划分过后,已经将key与key相等的数排好位置了,剩下的就只剩[begin,L)[R + 1,end)需要排序(这里我是设置成左闭右开区间,大家写的时候,也可以自己设置成左闭右闭区间),因为实现三路划分需要得到一个范围,所以写成函数不太好控制,所以我就直接将这个过程写在Quick_Sort函数中了

我们来看一下三路划分法实现快排用代码是怎么实现的:

void Quick_Sort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	
	int left = begin;
	int right = end - 1;
	int cur = left + 1;
	int key = a[left];
	while (cur <= right) // cur > right 循环才结束
	{
		if (a[cur] < key) // cur位置的值小于key
		{
			// 交换cur和L的位置的值,并且++cur ,++left
			Swap(&a[cur], &a[left]);
			++cur;
			++left;
		}
		else if (a[cur] > key) // cur位置的值大于key
		{
			// 交换cur和L的位置的值,并且--right
			Swap(&a[cur], &a[right]);
			--right;
		}
		else // cur位置的值等于key
		{
			++cur;
		}
	}

	// [begin, left) 和 [right + 1, end)
	Quick_Sort(a, begin, left);
	Quick_Sort(a, right + 1, end);
}


5. 快速排序的一些小优化


我们在上面实现排序的时候,面对一些情况我们写的快排也还是有些吃力,所以我们也还需要对一些情况做一些处理

这里有一点,可以注意一下:
就是我在上面写4种思想实现快排的时候,前三种思想我是将递归过程单独写成子函数来调用的,这样做的目的是使我写这篇博客时的可读性提高。但如果是我们自己写的时候,也可以不用单独写成一个子函数来调用,可以直接将这些思想的过程写进函数体里面。毕竟函数调用时,还是会有一点消耗的,当然这些消耗可以忽略不计

5.1 三数取中

三数取中有两种取法(当然不止两种),这里我要介绍的是两种思想:

常规的三数取中

这个思想主要是:我们通过比较数组起始位置和中间位置还有末尾位置的数据,第二大的数作为key

具体我们写代码来看一下:

int GetMidi(int* a, int begin, int end) // 三数取中
{
	int left = begin;
	int right = end - 1;
	int mid = (left + right) / 2;
	if (a[left] < a[mid])                // a[left] < a[mid]
	{
		if (a[mid] < a[right])           // a[left] < a[mid] < a[right]
			return mid;
		else if (a[left] < a[right])     // a[mid] >= a[right] > a[left]
			return right;
		else                             // a[mid] > a[left] >= a[right]
			return left;
	}
	else                                 // a[left] >= a[mid]
	{
		if (a[left] < a[right])          // a[mid] <= a[left] < a[right]
			return left;
		else if (a[right] > a[mid])      // a[left] >= a[right] > a[mid]
			return right;
		else                             // a[left] > a[mid] >= a[right]
			return mid;
	}
}

伪随机的三数取中

这个思想主要是:我们通过比较数组起始位置和数组中一个随机的位置,和他们中间位置的数据,第二大的数作为key

具体实现过程如下:

int GetMidi_random(int* a, int begin, int end) 
{
	int left = begin;
	int right = begin + rand() % (end - begin); // 后面加的数是整个数组的范围
	int mid = (left + right) / 2;
	if (a[left] < a[mid])                // a[left] < a[mid]
	{
		if (a[mid] < a[right])           // a[left] < a[mid] < a[right]
			return mid;
		else if (a[left] < a[right])     // a[mid] >= a[right] > a[left]
			return right;
		else                             // a[mid] > a[left] >= a[right]
			return left;
	}
	else                                 // a[left] >= a[mid]
	{
		if (a[left] < a[right])          // a[mid] <= a[left] < a[right]
			return left;
		else if (a[right] > a[mid])      // a[left] >= a[right] > a[mid]
			return right;
		else                             // a[left] > a[mid] >= a[right]
			return mid;
	}
}

int main()
{
	srand((unsigned)time(NULL)); // 生成随机数种子

	return 0;
}

我们在用三数取中时,获取到的是想要的数的位置,然后将这个位置的数和数组起始位置的数交换一下,这样就方便我们定义key了。具体实现方式,我写在了本小节的最后。

5.2 小区间优化

在这里插入图片描述

我们在学过二叉树以后,应该知道,完全二叉树,倒数第二层和最后一层加起来占了整个节点75%的节点,而我们在处理大量数据使用快排实现递归时当只有10个数据时,还会递归多次:
在这里插入图片描述
而在处理这些大量数据,我们递归下来时,无疑会产生大量范围为10及10以下的多组数据,如果我们此时继续递归,无疑会产生很多组小范围的数据。所以范围小于等于10的时候,就没必要使用快排继续递归了,这里可以考虑使用直接插入排序来排每组范围小于等于10的数组。这样就大大降低了递归时开辟栈帧的消耗了。在使用插入排序时,我们必须要弄清楚所需排序的范围:
在这里插入图片描述

下面我以三路划分的思想写一个我上述优化后的排序,里面有用到直接插入排序,这个不是本章的重点,不了解的可以看看这篇博客:《简单排序》:

int GetMidi_random(int* a, int begin, int end) 
{
	int left = begin;
	int right = begin + rand() % (end - begin); // 后面加的数是整个数组的范围
	int mid = (left + right) / 2;
	if (a[left] < a[mid])                // a[left] < a[mid]
	{
		if (a[mid] < a[right])           // a[left] < a[mid] < a[right]
			return mid;
		else if (a[left] < a[right])     // a[mid] >= a[right] > a[left]
			return right;
		else                             // a[mid] > a[left] >= a[right]
			return left;
	}
	else                                 // a[left] >= a[mid]
	{
		if (a[left] < a[right])          // a[mid] <= a[left] < a[right]
			return left;
		else if (a[right] > a[mid])      // a[left] >= a[right] > a[mid]
			return right;
		else                             // a[left] > a[mid] >= a[right]
			return mid;
	}
}

void Quick_Sort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	if (end - begin > 10)  // 小区间优化
	{
		int left = begin;
		int right = end - 1;
		int cur = left + 1;
		int mid = 0;
		if (left < end)
		{
			mid = GetMidi(a, left, end); //三数取中,获取我们要找的数
			Swap(&a[mid], &a[left]); // 将三数取中得到的数换到第一个位置
		}
		int key = a[left];

		while (cur <= right)
		{
			if (a[cur] < key)
			{
				Swap(&a[cur], &a[left]);
				++left;
				++cur;
			}
			else if (a[cur] > key)
			{
				Swap(&a[cur], &a[right]);
				--right;
			}
			else
				++cur;
		}
		// 分区间进行递归
		Quick_Sort(a, begin, left);
		Quick_Sort(a, right + 1, end);
	}
	else
	{
		// 使用插入排序时,要注意使用的范围
		InsertSort(a + begin, end - begin);
	}
}



6. 非递归版本的快排


在我们使用快排时,其实用递归也递归不了多少层。所以在平时我们要使用快排时,使用递归版的完全够用了。但由于现在还在学习阶段,所以掌握一下非递归版的快排还是有必要的。

我们实现非递归版本的快排时,用到的思想是我上面介绍的几种思想,只不过接下来讲的不是用递归去完成这个过程,这里我们要使用一个工具——,能够看到这里的我相信大家都对栈有一定的了解了,所以我在下面代码中,就将直接使用我写的栈了。不太了解栈的也可以看看这篇文章:《栈和队列》

void QuickSortNonR(int* a,int begin, int end)
{
	int left = begin;
	int right = end;
	Stack stack; // 创建一个栈的数据结构
	StackInit(&stack); // 将这个栈初始化一下
	StackPush(&stack, left); // 插入最开始的数
	StackPush(&stack, right); // 插入最后一位数的下一位,因为我们上面实现的三种方法都是左闭右开的
	while (!StackEmpty(&stack))
	{
		right = StackTop(&stack); // 我们是先插入最右边的数,所以我们应该先拿出最右边的数
		StackPop(&stack); // 栈顶元素删除
		left = StackTop(&stack); // 在上面删除right时,栈顶元素就是left了
		StackPop(&stack); // 栈顶元素删除
		int key = PartSort_Hoare(a, left, right); // 将这段区间进行排序
		// [left,key) key [key+1,right)
		
		if (right > key + 1) // 如果这个区间存在,就将这个区间表示的范围入栈(这里要注意入栈顺序和出栈顺序)
		{
			StackPush(&stack, key + 1);
			StackPush(&stack, right);
		}
 		if (left < key) // 如果这个区间存在,就将这个区间表示的范围入栈(这里要注意入栈顺序和出栈顺序)
		{
			StackPush(&stack, left);
			StackPush(&stack, key);
		}
	}
	// 销毁栈
	StackDestroy(&stack);
}

有人可能会问我们递归时用到的栈帧和我们自己做的栈,有什么区别呢?

因为我自己做的栈是在堆上开辟的空间,而如果使用递归版本的栈帧是在栈上开辟的空间。32位机器下,栈上的空间只有8MB左右,而堆上的空间有4G左右。所以这里选择在堆上开辟空间,可以很好的节约空间,也能减少递归时产生的消耗。


7. 快速排序的特性总结


  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

  2. 时间复杂度:O(N*logN)
    在这里插入图片描述

  3. 空间复杂度:O(logN)

    • 快速排序的空间复杂度取决于递归深度和每个递归函数栈空间的大小。
    • 在最坏情况下,快速排序的空间复杂度为O(n);在平均情况下,快速排序的空间复杂度为O(logn)1。
    • 快速排序是一种原地排序算法,它的工作原理是选择一个基准值,将待排序的数组划分为两个子数组,一个小于等于基准值,一个大于基准值,然后对两个子数组递归进行快速排序,直到整个数组有序。
  4. 稳定性:不稳定

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

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

相关文章

Leetcode 416 分割等和子集

题意理解&#xff1a; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 即将数组的元素分成两组&#xff0c;每组数值sum(nums)/2 若能分成这样的两组&#xff0c;则返回true,否则返回false 本质上…

国标28181平台的手机视频监控客户端的电子地图功能对比

目 录 一、手机客户端 1、概述 2、具体功能简述 二、电子地图功能 1、经纬度定位 2、附近设备 3、实时浏览功能 4、录像回放 5、缩放功能 三、手机web客户端和CS客户端上的电子地图功能对比 1、对比表 2、测距&#xff08;PC客户端功能&#xff09; 3…

【分布式技术】rsync远程同步服务

目录 一、rsync&#xff08;远程同步&#xff09; 二、实操rsync远程文件同步 准备一个服务端192.168.20.18以及一个客户端192.168.20.30 1、服务端搭建&#xff1a;先完成服务端配置&#xff0c;启动服务 rsync拓展 1、关于rsyncd服务的端口号 2、rsync和scp的区别 2、测…

在qemu虚拟机环境下,使用kgdb调试kernel

enable kgdb的情况下&#xff0c;使用qemu启动kernel 1&#xff0c;需要先在内核配置中增加kgdb的支持 2&#xff0c;启动qemu虚拟机时&#xff0c;增加参数-s -S&#xff0c;这两个参数会使得kernel在启动之后遇到的第一个指令等待gdb连接 例子&#xff1a; /qemu-project…

爬虫之牛刀小试(三):爬取中国天气网全国天气

天气网&#xff1a; import requests from bs4 import BeautifulSoup import time from pyecharts.charts import Bar from pyecharts import options as optsurl_hb http://www.weather.com.cn/textFC/hb.shtml url_db http://www.weather.com.cn/textFC/db.shtml url_hd …

RocketMQ Dashboard可视化工具

RocketMQ Dashboard 将 RocketMQ的相关指标展示在web页面 &#xff0c;支持以可视化工具代替 Topic 配置、Broker 管理等命令行操作。 官方文档地址&#xff1a;RocketMQ Dashboard | RocketMQ 目录 1.下载安装 1.1 系统要求&#xff1a; 1.2 源码安装 1.3 访问页面 2.功…

微信小程序地图展示区轮廓+展示指定地区标点气泡

需求&#xff1a;显示当前地区所有的学校列表&#xff1a;名称。区域显示区域名称下面所属学校数量 根据用户缩小放大当前区域&#xff08;大于12显示区&#xff0c;小于12显示当前区学校列表&#xff09;&#xff0c;获取&#xff1a;regionchange的type&#xff1a;end数据&…

中央处理器CPU(1)----指令周期和微程序

前言&#xff1a;由于期末复习计算机组成效率太慢所以抽时间写一下文章总结一下思路&#xff0c;理解不是很深&#xff0c;欢迎各位不吝赐教。 由于时间不是很充分&#xff0c;所以有些考点由于我们不考试&#xff0c;一笔带过了。 我这是期末复习总结&#xff0c;不是考研知识…

【BIAI】Lecture 7 - EEG data analysis

EEG data analysis 专业术语 EEG 脑电图 excitatory postsynaptic potential(EPSP)兴奋性突触后电位 inhibitory postsynaptic potential(IPSP) 抑制性突触后电位 action potential 动作电位 dipoles 偶极子 Pyramidal neurons 椎体细胞 Axon 轴突 Dendrite 树突 Synapse 突触…

python 识别验证码图片

描述&#xff1a;利用cv2和百度的aipocr 识别有噪点的验证码图&#xff0c;百度aip需要自己注册&#xff0c;把密钥替换为自己的即可 百度智能云 测试用的图片 # encoding: utf-8 from PIL import Image # 调用百度OCR接口识别验证码 from aip import AipOcr class preserv…

R语言下载安装及VScode配置

文章目录 1. R 下载和安装1.1 下载1.2 安装 2. VSCODE 配置2.1 安装R拓展2.2 安装R语言辅助功能包2.3 DEBUG 1. R 下载和安装 1.1 下载 网址&#xff1a;https://www.r-project.org/ 选择一个镜像地址下载 选择对应的版本 一般选择base即可 1.2 安装 下载安装包后按提示安装…

2.【CPP】入门(宏||内联函数||拷贝构造||析构函数||构造函数)

0x01.引言 1.实现一个宏函数ADD #define ADD(x,y) ((x)(y))//宏是预编译阶段完成替换&#xff0c;注意括号2.宏的优缺点 优点&#xff1a; 1.增强代码的复用性 2.宏函数不用建立栈帧&#xff0c;提高性能 缺点&#xff1a; 1.不方便调试 2.没有安全检查 0x02.内联函数 1.以空…

SpringCloud系列篇:核心组件之熔断器组件

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于SpringCloud的相关操作吧 前言 在微服务架构中&#xff0c;一个应用往往由多个服务组成&#xff0c;这些服务之间相互依赖&#xff0c;依赖关系错综复杂。 例…

three.js 关键帧动画

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div class"box-right"…

二维激光雷达

目录 前言 1 单线激光雷达 激光器 接收器 信号处理单元 旋转机构 2 单线激光雷达原理 2.1 三角测距法 1、直射式 2、斜射式 2.2 TOF飞行时间测距法 总结 前言 开始干活啦&#xff0c;单线激光雷达&#xff0c;三维知道原理&#xff0c;但不研究了&#xff0c;问就是买不起。 1 …

【JaveWeb教程】(19) MySQL数据库开发之 MySQL数据库操作-DML 详细代码示例讲解

目录 3. 数据库操作-DML3.1 增加(insert)3.2 修改(update)3.3 删除(delete)3.4 总结 3. 数据库操作-DML DML英文全称是Data Manipulation Language(数据操作语言)&#xff0c;用来对数据库中表的数据记录进行增、删、改操作。 添加数据&#xff08;INSERT&#xff09;修改数据…

mybatis-flex与springBoot整合

mybatis-flex基本使用 1.测试sql2.导入坐标3.框架搭建1.pojo层2.mapper层3.service层4.controller层5.启动类6.配置类7.EmpMapper.xml 4.启动测试 本片文章在springBoot3&#xff0c;jdk21下测试通过 注意官方网站为&#xff1a;https://mybatis-flex.com/ 请点击&#xff1a;直…

【C++】C++11中的常见语法(上)

C11 一、C11简介二、统一的列表初始化1.&#xff5b;&#xff5d;初始化2. std::initializer_list 三、声明1. auto2. decltype3. nullptr 四、右值引用和移动语义1. 左值引用和右值引用2. 左值引用与右值引用比较3. 右值引用使用场景和意义4. 右值引用引用左值及其一些更深入的…

如何使用Postman创建Mock Server?

这篇文章将教会大家如何利用 Postman&#xff0c;通过 Mock 的方式测试我们的 API。 什么是 Mock Mock 是一项特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行单元测试。通常情况下&#xff0c;Mock 与其他方法的主要区别就是&#xff0c;用于取代代码依赖项的模拟对…

Vue3 + TS + Element-Plus —— 项目系统中封装表格+搜索表单 十分钟写五个UI不在是问题

前期回顾 纯前端 —— 200行JS代码、实现导出Excel、支持DIY样式&#xff0c;纵横合并-CSDN博客https://blog.csdn.net/m0_57904695/article/details/135537511?spm1001.2014.3001.5501 目录 一、&#x1f6e0;️ newTable.vue 封装Table 二、&#x1f6a9; newForm.vue …