【排序算法】六、快速排序(C/C++)

「前言」文章内容是排序算法之快速排序的讲解。(所有文章已经分类好,放心食用)

「归属专栏」排序算法

「主页链接」个人主页

「笔者」枫叶先生(fy)

目录

  • 快速排序
    • 1.1 原理
    • 1.2 Hoare版本(单趟)
    • 1.3 快速排序完整代码(Hoare版)(递归实现)
    • 1.4 选择基准数key优化(三数取中)
    • 1.5 挖坑法(单趟)
    • 1.6 快速排序完整代码(挖坑法)(递归实现)
    • 1.7 前后指针版(单趟)
    • 1.8 快速排序完整代码(前后指针版)(递归实现)
    • 1.9 快速排序小区间优化
    • 1.9 快速排序非递归实现
    • 1.10 特性总结

快速排序

1.1 原理

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,一种高效的排序算法

基本思想:是通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分小,然后再按照此方法对这两部分数据分别进行快速排序,以达到整个数据变成有序序列

具体步骤如下:

  1. 从数列中挑出一个元素作为基准元素
  2. 将比基准元素小的元素放在其左边,比基准元素大的元素放在其右边,基准元素所在位置即为最终位置
  3. 分别对左右两个子序列递归地进行快速排序

对于如何按照基准值将待排序列分为两个子序列(单趟),单趟常见的方式有:

  1. Hoare版本
  2. 挖坑法
  3. 前后指针法

下面先谈Hoare版本的

1.2 Hoare版本(单趟)

Hoare版本的单趟排序的动图如下:
在这里插入图片描述

Hoare版本的单趟排序的基本步骤如下:

  1. 从数组中选出一个key,一般是最左边或是最右边的,上面动图是选左边的
  2. 定义一个left: L和一个right: R。如果选的key是左边的,则需要R先走,然后L从左向右走,R从右向左走。(注意:若选择最右边的数据作为key,则需要L先走
  3. 选左:
  4. 选取最左边的基准值作为key,L和R的任务:(1)L:找比key大的;(2)R:找比key小的
  5. 选取最左边的基准值作为key 在走的过程中,若R遇到小于key的数,则停下,L开始走,直到L遇到一个大于key的数时,将L和R的内容交换,R再次开始走,如此进行下去,直到L和R最终相遇,相遇结束。此时将相遇点的内容与key交换即可
  6. 选右:
  7. 选取最左边的基准值作为key,L和R的任务:(1)L:找比key小的;(2)R:找比key大的
  8. 选取最右边的基准值作为key 在走的过程中,若L遇到小于key的数,则停下,R开始走,直到R遇到一个大于key的数时,将L和R的内容交换,L再次开始走,如此进行下去,直到L和R最终相遇,相遇结束。此时将相遇点的内容与key交换即可

经过一次单趟排序,最终使得key左边的数据全部都小于keykey右边的数据全部都大于key,并且key所在的位置就是最终的位置

相遇位置的值如何保证比key小,相遇点直接和key交换会不会出错,会不会把较大的值换到了左边?

  • 如果选取最左边的基准值作为key,R先走保证相遇位置的值保证比key
  • 如果R停下来,L走,L撞到R(相遇),相遇位置的值必定比key
  • 如果L停下来,R走,R撞到L(相遇),相遇位置的值必定比key
  • 同理,如果选取最右边的基准值作为key,L先走保证相遇位置的值保证比key

1.3 快速排序完整代码(Hoare版)(递归实现)

首先经过一次单趟排序,然后将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,停止操作,此时序列(或者子序列)已经有序

排序步骤演示:

类似于二叉树的前序遍历
在这里插入图片描述
代码如下(递归,Hoare版):(升序)

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

// Hoare版本(单趟)
int PartQuickSort(int* arr, int left, int right)
{
	int keyi = left; // key的下标,选左边为key
	while (left < right) // left == right即相遇
	{
		// right先走,找比key小的值
		while (left < right && arr[right] >= arr[keyi])
		{
			--right;
		}
		// left后走,找比key大的值
		while (left < right && arr[left] <= arr[keyi])
		{
			++left;
		}
		// 找到一组值,交换:交换arr[left] 和 arr[right]
		if (left < right)
		{
			Swap(&arr[left], &arr[right]);
		}
	}
	Swap(&arr[keyi], &arr[left]); // 交换 keyi 和相遇点位置的值,left==right
	return left; // 返回相遇点的下标
}

// 快速排序
void QuickSort(int* arr, int left, int right)
{
	// 1、区间不存在(left > right) 2、只有一个数数不需要再处理(left == right)
	if (left >= right)
	{
		return;
	}

	// 单趟排序并获取基准值
	int keyi = PartQuickSort(arr, left, right);

	// 递归处理每一个子序列(左、右)
	QuickSort(arr, left , keyi - 1); // keyi 左序列进行排序
	QuickSort(arr, keyi + 1, right); // keyi 右序列进行排序
}

1.4 选择基准数key优化(三数取中)

快速排序的时间复杂度是O(NlogN),是我们在理想情况下计算的结果

在理想情况下,我们每次进行完单趟排序后,key的左序列与右序列的长度都相同
在这里插入图片描述
若每趟排序所选的key都正好是该序列的中间值,即单趟排序结束后key位于序列正中间,那么快速排序的时间复杂度就是O(NlogN)

可是谁能保证你每次选取的key都是正中间的那个数呢?

例如,以下数组:
在这里插入图片描述
如果基准值key是数组中最大或最小的数值(假设选左边),则快速排序的递归深度会非常深,排序效率会很低

若是一个有序数组使用快速排序,这种情况下,快速排序的时间复杂度退化为O(N^2)
在这里插入图片描述
对快速排序效率影响最大的就是选取的key,若选取的key越接近中间位置,则则效率越高

为了避免这种极端情况的发生,于是出现了三数取中

三数取中

三数取中的方法是从待排序数组中选择三个元素,分别是左端、右端和中间位置的元素,然后取这三个元素的中间值作为基准元素

代码如下:

// 三数取中
int GetMidIndex(int* arr, int left, int right)
{
	int mid = left + (right - left) / 2;
	if (arr[left] < arr[mid]) 
	{
		if (arr[mid] < arr[right]) 
		{
			return mid; // [mid]是中间值
		}
		else if (arr[left] > arr[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else  // arr[left] >= arr[mid]
	{
		if (arr[mid] > arr[right])
		{
			return mid;
		}
		else if (arr[left] < arr[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

需要注意,当大小居中的数不在序列的最左或是最右端时,我们不是就以居中数的位置作为key的位置,而是将key的值与最左端的值进行交换,这样key就还是位于最左端了,所写代码就无需改变,而只需在单趟排序代码开头加上以下两句代码即可:

如下:

// Hoare版本(单趟)
int PartQuickSort(int* arr, int left, int right)
{
	// 三数取中
	int midi = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[midi]);

	int keyi = left; // key的下标,选左边为key
	// ...
	return left; // 返回相遇点的下标
}

1.5 挖坑法(单趟)

挖坑法是基于Hoare版本改良的(Hoare版本代码细节太多)

挖坑法的单趟排序的动图如下:
在这里插入图片描述
挖坑法的单趟排序的基本步骤如下:

  1. 选出一个数据(上图选最左为坑)存放在key变量中,在该数据位置形成一个坑
  2. 定义一个left: L和一个right: R,L从左向右走,R从右向左走(若在最左边挖坑,则需要R先走;若在最右边挖坑,则需要L先走)
  3. 选取最左边的作为坑位为例:
  4. L和R的任务:(1)L:找比key大的;(2)R:找比key小的
  5. R先走,在走的过程中,若R遇到小于key的数,则将该数抛入坑位,并在此处形成一个新的坑位,这时L再向后走,若遇到大于key的数,则将其抛入坑位,又形成一个坑位,如此循环下去,直到最终L和R相遇,这时将key抛入坑位即可

经过一次单趟排序,最终也使得key左边的数据全部都小于keykey右边的数据全部都大于key,并且key所在的位置就是最终的位置

挖坑法代码如下:(升序)

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

// 挖坑法(单趟)
int PartQuickSort2(int* arr, int left, int right)
{
	// 三数取中
	int midi = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[midi]);
	
	int key = arr[left]; // 在最左形成一个坑位
	int hole = left; // 坑的下标
	while (left < right)
	{
		// right先走,找比key小的
		while (left < right && arr[right] >= key)
		{
			--right;
		}
		arr[hole] = arr[right]; // 填坑
		hole = right;
		// left后走,找比key大的
		while (left < right && arr[left] <= key)
		{
			++left;
		}
		arr[hole] = arr[left]; // 填坑
		hole = left;
	}
	arr[hole] = key;
	return hole;
}

1.6 快速排序完整代码(挖坑法)(递归实现)

代码如下(递归,挖坑法):(升序)

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

// 挖坑法(单趟)
int PartQuickSort2(int* arr, int left, int right)
{
	// 三数取中
	int midi = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[midi]);
	
	int key = arr[left]; // 在最左形成一个坑位
	int hole = left; // 坑的下标
	while (left < right)
	{
		// right先走,找比key小的
		while (left < right && arr[right] >= key)
		{
			--right;
		}
		arr[hole] = arr[right]; // 填坑
		hole = right;
		// left后走,找比key大的
		while (left < right && arr[left] <= key)
		{
			++left;
		}
		arr[hole] = arr[left]; // 填坑
		hole = left;
	}
	arr[hole] = key;
	return hole; // 返回key的下标
}

// 快速排序
void QuickSort(int* arr, int left, int right)
{
	// 1、区间不存在(left > right) 2、只有一个数数不需要再处理(left == right)
	if (left >= right)
	{
		return;
	}

	// 单趟排序并获取基准值
	int keyi = PartQuickSort2(arr, left, right);

	// 递归处理每一个子序列(左、右)
	QuickSort(arr, left , keyi - 1); // keyi 左序列进行排序
	QuickSort(arr, keyi + 1, right); // keyi 右序列进行排序
}

1.7 前后指针版(单趟)

前后指针版的单趟排序的动图如下:
在这里插入图片描述
前后指针法的单趟排序的基本步骤如下:

  1. 选出一个key,最左边或是最右边的(上图选最左)
  2. 开始时,prev指向序列的左边,cur指向prev+1的位置
  3. 情况1:若cur指向的内容小于key,则prev向右后移动一位,然后交换prevcur指针指向的内容,然后cur指针++
  4. 情况2:若cur指向的内容大于key,则cur指针直接++。如此进行下去,直到cur指针越界,此时将keyprev指针指向的内容交换即可

前后指针版代码如下:(升序)

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

// 前后指针版(单趟)
int PartQuickSort3(int* arr, int left, int right)
{
	// 三数取中
	int midi = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[midi]);

	int keyi = left;
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (arr[cur] < arr[keyi]) // cur指向的内容小于key
		{
			++prev;
			if(prev != cur)
				Swap(&arr[prev], &arr[cur]);
		}
		++cur;
	}
	Swap(&arr[keyi], &arr[prev]);
	return prev; // 返回key当前的下标
}

1.8 快速排序完整代码(前后指针版)(递归实现)

代码如下(递归,前后指针版):(升序)

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

// 前后指针版(单趟)
int PartQuickSort3(int* arr, int left, int right)
{
	// 三数取中
	int midi = GetMidIndex(arr, left, right);
	Swap(&arr[left], &arr[midi]);

	int keyi = left;
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (arr[cur] < arr[keyi]) // cur指向的内容小于key
		{
			++prev;
			if(prev != cur)
				Swap(&arr[prev], &arr[cur]);
		}
		++cur;
	}
	Swap(&arr[keyi], &arr[prev]);
	return prev; // 返回key当前的下标
}

// 快速排序
void QuickSort(int* arr, int left, int right)
{
	// 1、区间不存在(left > right) 2、只有一个数数不需要再处理(left == right)
	if (left >= right)
	{
		return;
	}

	// 单趟排序并获取基准值
	int keyi = PartQuickSort3(arr, left, right);

	// 递归处理每一个子序列(左、右)
	QuickSort(arr, left , keyi - 1); // keyi 左序列进行排序
	QuickSort(arr, keyi + 1, right); // keyi 右序列进行排序
}

1.9 快速排序小区间优化

快速排序的递归类似于二叉树的形式,深度越深待排数组的长度越短,但是数量也越多,调用函数的次数就越多,开辟函数栈帧的消耗越大,导致效率下降(每一层的递归次数会以2倍的形式快速增长,后三层的递归次数占据了总递归次数的70%以上)

优化的方式就是设置一个排序序列的长度大小,若是数组长度大于这个指定的数,则调用快速排序,若是数组长度小于这个指定的数,不再调用快速排序,而是调用插入排序或者其他排序

这个数可自行调整,代码如下:

// 快速排序(优化)
void QuickSort2(int* arr, int left, int right)
{
	// 1、区间不存在(left > right) 2、只有一个数数不需要再处理(left == right)
	if (left >= right)
	{
		return;
	}

	if (right - left + 1 > 8) // 可自行调整
	{
		// 单趟排序并获取基准值
		int keyi = PartQuickSort3(arr, left, right);
		// 递归处理每一个子序列(左、右)
		QuickSort(arr, left, keyi - 1); // keyi 左序列进行排序
		QuickSort(arr, keyi + 1, right); // keyi 右序列进行排序
	}
	else // 调用其他排序
	{
		InsertSort(arr + left, right - left + 1);
	}
}

1.9 快速排序非递归实现

将一个用递归实现的算法改为非递归时,一般需要借用一个数据结构,那就是栈

C语言的库没有封装有stack,需要自己手搓一个,C++的STL库则封装有

如果是C语言,我就不贴代码了,代码链接:stack,下面简单介绍一下手搓stack的接口

// 初始化
void StackInit(ST* ps);
// 销毁栈
void StackDestroy(ST* ps);
// 入栈
void StackPush(ST* ps, STDataType x);
// 出栈
void StackPop(ST* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(ST* ps);
// 获取栈顶元素
STDataType StackTop(ST* ps);
// 获取栈中有效元素个数
int StackSize(ST* ps);

快速排序的非递归算法基本思路:

  1. 先将待排序列的第一个元素的下标和最后一个元素的下标入栈
  2. 当栈不为空时,读取栈中的信息(一次读取两个:一个是left,另一个是right),然后调用某一版本的单趟排序,排完后获得了key的下标,然后判断key的左序列和右序列是否还需要排序,若还需要排序,就将相应序列的left和right入栈;若不需排序了(序列只有一个元素或是区间不存在),就不需要将该序列区间的信息入栈
  3. 重复执行步骤2,直到栈为空为止

C语言代码如下:(升序)

void QuickSort3(int* arr, int left, int right)
{
	Stack st; // 创建栈
	StackInit(&st); // 初始化栈
	StackPush(&st, left); // 待排序列的left
	StackPush(&st, right); // 待排序列的right

	while (!StackEmpty(&st)) // 栈为空结束
	{
		int right = StackTop(&st); // 读取right
		StackPop(&st); // 出栈
		int left = StackTop(&st); // 读取left
		StackPop(&st); // 出栈

		// 判断左右区间是否合理,若不合理则跳过本次循环
		if (left >= right)
		{
			continue;
		}
	
		int keyi = PartQuickSort2(arr, left, right); // 调用单趟的方法

		StackPush(&st, left); // 左序列的left入栈
		StackPush(&st, keyi - 1); // 左序列的right入栈
		
		StackPush(&st, keyi + 1); // 右序列的left入栈
		StackPush(&st, right); // 右序列的right入栈
	}
}

C++代码如下:(升序)

void QuickSort4(int* arr, int left, int right)
{
	stack<int> st; // 创建栈
	st.push(left); // 待排序列的left
	st.push(right); // 待排序列的right

	while (!st.empty()) // 栈为空结束
	{
		int right = st.top(); // 读取right
		st.pop(); // 出栈
		int left = st.top(); // 读取left
		st.pop(); //出栈

		//判断左右区间是否合理,若不合理则跳过本次循环
		if (left >= right)
		{
			continue;
		}

		int keyi = PartQuickSort2(arr, left, right); // 调用单趟的方法

		st.push(left); // 左序列的left入栈
		st.push(keyi - 1); // 左序列的right入栈

		st.push(keyi + 1); // 右序列的left入栈
		st.push(right); // 右序列的right入栈
	}
}

1.10 特性总结

快速排序的特性总结如下:

  • 时间复杂度:平均情况下,快速排序的时间复杂度为O(nlogn)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 不适用于小规模数据,适用于大规模数据的排序

--------------------- END ----------------------

「 作者 」 枫叶先生
「 更新 」 2024.1.20
「 声明 」 余之才疏学浅,故所撰文疏漏难免,
          或有谬误或不准确之处,敬请读者批评指正。

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

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

相关文章

基于Elasticsearch+Logstash+Kibana+Filebeat的日志收集分析及可视化

sudo rm /var/lib/dpkg/lock* sudo dpkg --configure -a apt update tail -f /var/log/car.log 1.1、项目概述 海量的业务应用&#xff0c;也带来了海量的日志数据&#xff0c;给业务应用的运维带来了新的挑战。例如&#xff0c;我们常用的网约车应用&#xff0c;单个平台…

4496 蓝桥杯 求函数零点 简单

4496 蓝桥杯 求函数零点 简单 //C风格解法1&#xff0c;通过率100% #include <bits/stdc.h> // int a, b; 一定会自动初始化为 0int main(){int a 2, b 3; // 定义a&#xff0c;b&#xff0c;不会自动初始化&#xff0c;最好自己定义时初始化// windows环境下a值固定&…

在WIN从零开始在QMUE上添加一块自己的开发板(二)

文章目录 一、前言往期回顾 二、CPU虚拟化&#xff08;一&#xff09;相关源码&#xff08;二&#xff09;举个例子&#xff08;三&#xff09;测试 三、内存虚拟化&#xff08;一&#xff09;相关源码&#xff08;二&#xff09;举个例子测试 参考资料 一、前言 笔者这篇博客…

[MySQL]基础的增删改查

目录 1.前置介绍 2.数据库操作 2.1显示当前数据库 2.2创建数据库 2.3 使用数据库 2.4 删除数据库 3.常用数据类型 3.1整型和浮点型 3.2字符串类型 4.表的操作 4.1查看表结构 4.2创建表 4.3删除表 5.重点 5.1操作数据库 5.2常用数据类型 5.3操作表 1.前置介绍 …

IEEE-2024年第五届人工智能、机器人及控制国际会议(AIRC 2024)

IEEE--2024年第五届人工智能、机器人及控制国际会议(AIRC 2024) 会议时间: 2024年4月22-24日 会议地点: 埃及开罗 埃及英国大学 会议网址:AIRC 2024 | Artificial Intelligence, Robotics and Controlhttps://www.airc.org/ 埃及开罗 埃及英国大学 会议组织单位&#xff1a; 征…

【精选】中间件 tomcat漏洞复现

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

139基于matlab多旅行商MTSP问题

基于matlab多旅行商MTSP问题&#xff0c;利用遗传算法求解多旅行商问题的算法设计&#xff0c;输出MTSP路径。相互独立路径&#xff0c;同一起点路径。程序已调通&#xff0c;可直接运行。 139 matlab多旅行熵M-TSP (xiaohongshu.com)https://www.xiaohongshu.com/explore/65ab…

宝塔 ftp 服务器发回了不可路由的地址/读取目录列表失败

ftp连接不上&#xff1a; 1.注意内网IP和外网IP 2.检查ftp服务是否启动 &#xff08;面板首页即可看到&#xff09; 3.检查防火墙20端口 ftp 21端口及被动端口39000 - 40000是否放行 &#xff08;如是腾讯云/阿里云等还需检查安全组&#xff09; 4.是否主动/被动模式都不能连接…

2024 Windows10 | 搭建MySQL Cloudbeaver 可视化DBS | Docker Compose本地环境

2024 Windows10 | 搭建MySQL Cloudbeaver 可视化DBS | Docker Compose本地环境 前提条件docker-compose.yml总结 | 用Docker的原因&#xff1f; | 遇到的问题&#xff1f; 前提条件 Windows10 已安装 Docker Desktop提前准备映射用的4个文件夹&#xff08;3个用在 MySQL&#…

51单片机中断

1、什么是中断&#xff1f; CPU在处理某一事件A时&#xff0c;发生了另一事件B请求CPU迅速去处理&#xff08;中断发生&#xff09;&#xff1b; CPU暂时中断当前的工作&#xff0c;转去处理事件B&#xff08;中断响应和中断服务&#xff09;&#xff1b; 待CPU将事件B处理完…

中小企业如何快速融资-----股权融资的四种方式(上)

’在企业融资的多种手段中&#xff0c;股权质押融资、股权交易增值融资、股权增资扩股融资和股权的私募融资&#xff0c;逐渐成为中小企业利用股权实现融资的有效方式。随着市场体系和监管制度的完善&#xff0c;产权市场为投融资者搭建的交易平台日益成熟&#xff0c;越来越多…

Java医药WMS进销存系统

技术架构&#xff1a; jdk8 IntelliJ IDEA maven Mysql5.7 有需要的可以私信我。 系统功能与介绍&#xff1a; 医药进销存系统&#xff0c;主要分两种角色&#xff1a;员工、客户。本系统具有进销存系统的通用性&#xff0c;可以修改为其它进销存系统&#xff0c;如家电进…

c语言-位段

文章目录 前言一、位段是什么&#xff1f;1.1 位段的声明1.2 关于位段的说明 二、位段的内存分配2.1 关于位段内存分配的说明2.2 位段类型为int的内存分配方式&#xff08;Visual Studio 2022&#xff09;2.3 位段类型为char的内存分配方式&#xff08;Visual Studio 2022&…

Vulnhub-LORD OF THE ROOT: 1.0.1

一、信息收集 端口扫描、发现只开了22端口 连接ssh后提示端口碰撞&#xff1a; 端口敲门是一种通过在一组预先指定的关闭端口上产生连接请求&#xff0c;从外部打开防火墙上的端口的方法。一旦收到正确地连接请求序列&#xff0c;防火墙规则就会被动态修改&#xff0c;以允许…

宏任务与微任务执行顺序及对比记录

目录 前言 一、 宏任务、微任务的基本概念 1.宏任务介绍 2.微任务介绍 3.宏任务、微任务发展及出现的原因&#xff1a; 4.宏任务、微任务的基本类型 二、 事件循环模型&#xff08;Event Loop&#xff09; 三、 Promise、async和await 在事件循环中的处理 1.Promise: 2.…

Linux内存管理:(九)内存规整

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 1. 引言 伙伴系统以页面为单位来管…

Pytest 测试框架与Allure 测试报告——Allure2测试报告-L3

目录&#xff1a; allure2报告中添加附件-图片 Allure2报告中添加附件Allure2报告中添加附件&#xff08;图片&#xff09;应用场景Allure2报告中添加附件&#xff08;图片&#xff09;-Python代码示例&#xff1a;allure2报告中添加附件-日志 Allure2报告中添加附件&#xff…

Linux的权限(3)

目录 文件类型 ​d目录文件 -普通文件 l链接文件 b块设备文件 p管道文件 c字符设备文件 文件权限 目录权限 umask 粘滞位 Q1umask权限默认值664/775 Q2"可执行性"权限 Q3"删除"权限 Q4怎么共享一批文件 【1】粘滞位 【2】添加交互人员到所…

第八讲_ArkTS装饰器(五)

ArkTS装饰器&#xff08;五&#xff09; 1. Watch装饰器1.1 Watch装饰器的特点1.2 Watch装饰器使用示例 2. ArkTS装饰器总结 1. Watch装饰器 Watch用于对状态变量的监听。如果需要关注某个状态变量的值是否改变&#xff0c;可以使用Watch为状态变量设置回调函数。 何为状态变…

基于DUP的网络聊天室

基于UDP的网络聊天室的使用&#xff08;select&#xff09;完成的服务器端 #include<head.h> typedef struct de {char name[10];struct sockaddr_in cin;struct de* next; }*linklist; //创建节点 linklist a_creat() {linklist p(linklist)malloc(sizeof(struct de));…