c++数据结构算法复习基础--11--高级排序算法-快速排序-归并排序-堆排序

news2025/2/24 4:35:22

高阶排序

在这里插入图片描述
在这里插入图片描述

1、快速排序

冒泡排序的升级算法

每次选择一个基准数,把小于基准数的放到基准数的左边,把大于基准数的放到基准数的右边,采用 “ 分治算法 ”处理剩余元素,直到整个序列变为有序序列。

最好和平均的复杂度:
时间复杂度:O(n)*O(logn) = O(nlogn)
空间复杂度:O(logn) 递归的深度所占用的栈内存

最坏的情况(有序的元素):元素有几个,其深度就有几个,此时时间复杂度为 O(n^2) , 空间复杂度为O(n)
在这里插入图片描述

思路

在这里插入图片描述

实例理解

对于数组arr[] = {46,8,76,10,38,7,68,32,65,53};进行快速排序。
在这里插入图片描述
循环的条件 L< R
1、选取基准数 val = arr[L]; // val = 46
2、从R开始往前找第一个 <val 的数字,放到L的地方。(这里不用担心数据被覆盖,因为val已经将值保存), L++ 。
在这里插入图片描述在这里插入图片描述
3、从L开始,往后找第一个 >val 的数字,放到R的地方, R-- 。在这里插入图片描述
4、重复上面的过程,直到循环结束(循环条件为 L<R)在这里插入图片描述
运行到循环结束在这里插入图片描述
此时,将val的值写入 arr[L] 最终一趟下来的结果为在这里插入图片描述

一趟下来,此时,arr[L] 左边的值全部小于val–46,左边全部大于val–46。
此时,继续对两边的数据继续快排。
在这里插入图片描述
最终结果为:
在这里插入图片描述

代码实现


//快排分割处理函数
int Partation(int arr[], int left, int right)
{
	//记录基准数
	int val = arr[left];

	//进行一次快排分割处理   O(n)*O(logn) = O(nlogn)  空间复杂度:O(logn) 递归的深度所占用的栈内存
	while (left < right)
	{
		while (left < right && arr[right] > val)
		{
			right--;
		}
		if (left < right)
		{
			arr[left] = arr[right];
			left++;
		}
		while (left < right && arr[left] < val)
		{
			left++;
		}
		if (left < right)
		{
			arr[right] = arr[left];
			right--;
		}
	}

	//left == right   的位置,就是放基准数的位置
	arr[left] = val;
	return left;
}

//快排的递归接口
void QuickSort(int arr[], int begin, int end)
{
	if (begin >= end)//快排递归结束的条件
	{
		return;
	}
	//在[begin,end]区间的元素进行一次快排分割处理
	int pos = Partation(arr,begin,end);

	//对基准数的左边和右边的序列,再分别进行快排
	QuickSort(arr,begin,pos-1);
	QuickSort(arr,pos+1,end);

}

//快速排序
void QuickSort(int arr[], int size)//为了区别自带的快速排序函数
{
	return QuickSort(arr,0,size-1);
}

int main()
{
	int arr[10];
	srand(time(NULL));

	for (int i = 0; i < 10; i++)
	{
		arr[i] = rand() % 100 + 1;
	}

	for (int v : arr)
	{
		cout << v << "  ";
	}
	cout << endl;

	QuickSort(arr, sizeof(arr) / sizeof(arr[0]));

	for (int v : arr)
	{
		cout << v << "  ";
	}
	cout << endl;

	return 0;
}

测试

在这里插入图片描述

快速排序的算法优化、效率提升

1)对于小段趋于有序的序列采用插入排序
2)三数取中法。旨在挑选合适的基准数,防止快排退化成冒泡排序。
3)随机数法

特点

快速排序是个不稳定的排序算法

当数据趋于有序,或者已经有序了,快速排序的效率是很差的,但是快速排序的效率是最好的。

快排算法优化一:

1、随着快速排序算法执行,数据越来越趋于有序,在一定范围内,可以采用插入排序代替快速排序
相关代码

//针对快排优化设计的插入排序
void InsertSort(int arr[], int begin,int end)
{
	for (int i = begin; i <= end; i++)//O(n)
	{
		int val = arr[i];
		int j = i - 1;
		for (; j >= 0; j--) //O(n)
		{
			if (arr[j] <= val)
			{
				break;
			}
			arr[j + 1] = arr[j];
		}
		//val -> j+1
		arr[j + 1] = val;
	}
}
void QuickSort(int arr[], int begin, int end)
{
	if (begin >= end)//快排递归结束的条件
	{
		return;
	}

	//优化一:当[begin,end] 序列的元素个数小到指定数量,采用插入排序
	if (end - begin <= 50)//这里的范围视情况而定
	{
		InsertSort(arr,begin,end);
		return;
	}

	//在[begin,end]区间的元素进行一次快排分割处理
	int pos = Partation(arr,begin,end);

	//对基准数的左边和右边的序列,再分别进行快排
	QuickSort(arr,begin,pos-1);
	QuickSort(arr,pos+1,end);

}

快排算法优化二:选择基准数的优化,采用“三数取中”法

采用“三数取中”法,找合适的基准数。

mid = (L+R)/2;

2、归并排序

也采用 “ 分治思想 ”,先进行序列划分,再进行元素的有序合并。
时间复杂度:O(n logn)
空间复杂度:O(logn)+O(n) — 取大的 — O(n)

核心思想

类比于递归思想,核心在于
递—将数据规模不断的减小,减小到结果已知的规模。
归—通过已知的规模反推而上,不断解决上一层规模的问题,最终累计出原始数据 的结果。
所以归并排序的思想:在归的过程中,进行的数据合并,达到排序的效果

难点

上述思想衍生出的问题和难点

1)递到什么程度,才开始归?
2)在归的过程中,如何进行数据合并?

对数数据规模需要一个前后指针,一个起始下标,一个末尾下标。进行二路归并,需要中间值,int mid = (L+R)/2;

递的过程
将数据分两半,
在这里插入图片描述
归并的过程
数据合并,也就是叶子节点网根节点回退。
重新开辟一个空间,将两个有序的叶子节点合并到上一层,不断重复,直到归并到根节点,此时数据处理完毕。
在这里插入图片描述

代码实现

//归并过程函数
void Merge(int arr[], int l, int m, int r)
{
	int* p = new int[r-l+1];

	int idx = 0;
	int i = l;
	int j = m + 1;

	while (i <= m && j <= r)
	{
		if (arr[i] <= arr[j])
		{
			p[idx++] = arr[i++];
		}
		else
		{
			p[idx++] = arr[j++];
		}
	}
	while (i <= m)
	{
		p[idx++] = arr[i++];
	}
	while (j <= r)
	{
		p[idx++] = arr[j++];
	}

	//再把合并好的大段有序的结果,拷贝到原始arr[数组[l,r]区间内
	for (i = l, j = 0; i <= r; i++, j++)
	{
		arr[i] = p[j];
	}

	delete[]p;

}


//归并排序递归接口
void MergeSort(int arr[], int begin, int end)
{
	//递归结束的条件
	if (begin >= end)
	{
		return;
	}

	int mid = (begin + end) / 2;
	//先递
	MergeSort(arr,begin,mid);
	MergeSort(arr,mid+1,end);
	//再归并  [begin,mid]  [mid+1,end]  -- 把两个小段有序的序列,合并成一个大段的有序序列
	Merge(arr,begin,mid,end);
}

//归并排序
void MergeSort(int arr[], int size)
{
	return MergeSort(arr, 0, size - 1);
}

测试

int main()
{
	int arr[10];
	srand(time(NULL));

	for (int i = 0; i < 10; i++)
	{
		arr[i] = rand() % 100 + 1;
	}

	for (int v : arr)
	{
		cout << v << "  ";
	}
	cout << endl;

	MergeSort(arr, sizeof(arr) / sizeof(arr[0]));

	for (int v : arr)
	{
		cout << v << "  ";
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

3、堆排序

二叉堆

二叉堆就是一颗完全二叉树,范围两种典型的堆,分别是大根堆小根堆

基于二叉堆的基础,规定了当前节点和两个孩子节点值的大小关系。

满足 0 <= i <= (n-1)/2 , n 代表最后一个元素的下标。
如果 arr[i] <= arr[2 * i + 1] && arr[i] <= arr[2*i + 2] ,就是小根堆
如果 arr[i] >= arr[2 * i + 1] && arr[i] >= arr[2*i + 2] ,就是大根堆
在这里插入图片描述
二叉堆,实际上(物理上)还是用数组存储的元素,只是逻辑上,将其看成有一个人口根节点,
每个节点都有两个孩子,没有孩子的最下一层,称之为叶子节点,这样的二叉树结构,称之为完全二叉树。

完全二叉树:

完全二叉树相比于二叉树,最后一层的叶子节点都是靠左排列,
优点是在存储二叉树节点的时候,比较省数组内存空间。

最后一层的叶子节点都是靠左排列的,每一层的节点都是满的。
最下层节点,必须从左往右都有,中间空一个都不算。

在这里插入图片描述
如何将数组存储的元素,逻辑上看成完全二叉树?当前节点和两个孩子节点有什么关系?
在数组中,满足下图关系的节点,在逻辑上看成二叉树当前节点和其左右节点。
在这里插入图片描述

如何获得第一个非叶子节点的下标?

(n-1)/2

堆的上浮和下层调整(添加删除元素)

入堆(大根堆入堆示例)

数组的元素添加,在数组末尾添加元素。
相对应的,逻辑上,在二叉树的最下层如图所示位置,添加新元素。
在这里插入图片描述

但此时,破坏了此时完全二叉树的逻辑,需要进行调整。、
因为是大根堆,所以要对数据进行上浮调整
这里新的元素10大于父节点5,进行交换。

在这里插入图片描述

交换过后,发现还是比父节点大,继续交换。

在这里插入图片描述
如下图所示,元素上浮到根节点,上浮结束。
在这里插入图片描述
注意,人堆不是入到堆顶,是入到小于父节点的位置。

出堆(大根堆出堆示例)

大根堆出堆,出堆顶元素。
优先级队列实现的时候,假设值越大,优先级越大,这里为大根堆,出元素,就先出堆顶元素。
当然也可以规定,值越小,优先级越小。

出堆操作

将堆顶元素取出。
在这里插入图片描述

将最后的数据放到堆顶,该值相对是偏小的。
在这里插入图片描述
从零号元素开始,往下调整,继续下层调整操作
比较节点两个孩子节点,将孩子节点中较大值,覆盖,进行下层操作。
以此类推,直到元素无孩子节点,或者孩子节点都小于该元素。
将val覆盖该位置的值。

基于堆的优先级队列代码实现

类似于C++库中的priority_queue,其底层也是数组,只不过没有自己写数组。如果自己写数组,就成为容器了,而不是容器适配器,其只是对底层容器(vector)进行适配。

//优先级队列实现 priority_queue(vector)   -- push  pop  top  empty size
class PriorityQueue
{
public:
	//模板定义一个函数对象
	using Comp = function<bool(int,int)>;
	//这里默认大根堆
	PriorityQueue(int cap = 20, Comp comp = greater<int>())
		:size_(0)
		, cap_(cap)
		, comp_(comp)
	{
		que_ = new int[cap_];
	}
	
	//PriorityQueue(Comp comp = greater<int>())
	PriorityQueue(Comp comp)
		:size_(0)
		, cap_(20)
		, comp_(comp)
	{
		que_ = new int[cap_];
	}
	
	~PriorityQueue()
	{
		delete[] que_;
		que_ = nullptr;
	}
public:
	//入堆操作  O(log n)  O(1)
	void push(int val)
	{
		//判断扩容
		if (size_ == cap_)
		{
			int* p = new int[2 * cap_];
			memcpy(p,que_,cap_*sizeof(int));
			delete[]que_;
			que_ = p;
			cap_ *= 2;
		}

		if (size_ == 0)
		{
			//只有一个元素,不用进行堆的上浮调整
			que_[size_] = val;
		}
		else
		{
			//堆里面有多个元素,需要进行上浮调整
			siftUp(size_,val);
		}
		size_++;
	}

	//出堆操作
	void pop()
	{
		if (size_ == 0)
			throw "container is empty!";

		size_--;
		if (size_ > 0)
		{
			// 删除堆顶元素,还有剩余的元素,要进行堆的下沉调整
			siftDown(0,que_[size_]);
		}
	}

	bool empty() const { return size_ == 0; }

	int top()const
	{
		if (size_ == 0)
			throw "container is empty!";
		return que_[0];
	}

	int size() const { return size_; }
private:
	//入堆上浮调整
	void siftUp(int i, int val)
	{
		while (i > 0)//最多计算到根节点(0号位)
		{
			int father = (i - 1) / 2;
			if (comp_(val, que_[father]))
			{
				que_[i] = que_[father];
				i = father;
			}
			else
			{
				break;
			}
		}
		//把val放到i的位置
		que_[i] = val;
	}

	//出堆下沉调整
	void siftDown(int i, int val)
	{
		//i下沉不能超过最后一个有孩子的节点
		//while (i <= (size_ - 1 - 1) / 2)
		while (i < size_ / 2)
		{
			int child = 2 * i + 1;//第i个节点的左孩子
			if (child + 1 < size_ && comp_(que_[child+1],que_[child]))
			{
				//如果i节点右孩子的值大于左孩子,child记录右孩子的下标
				child = child + 1;
			}

			if (comp_(que_[child], val))
			{
				que_[i] = que_[child];
				i = child;
			}
			else
			{
				break;//已经满足堆的性质,提前结束
			}
		}
		que_[i] = val;
		
	}
private:
	int* que_; // 指向动态扩容的数组
	int size_; // 数组元素的个数
	int cap_;  // 数组的总内存空间大小
	Comp comp_;  //比较器对象

};

测试

int main()
{
	PriorityQueue que;//基于大根堆实现的优先级队列

	PriorityQueue que1([](int a, int b) {return a < b; });//基于小根堆实现的优先级队列

	srand(time(NULL));

	for (int i = 0; i < 10; i++)
	{
		que.push(rand()%100);
		que1.push(rand() % 100);
	}
	while (!que.empty())
	{
		cout << que.top() << "  ";
		que.pop();
	}

	cout << endl;

	while (!que1.empty())
	{
		cout << que1.top() << "  ";
		que1.pop();
	}

	cout << endl;
}

堆排序代码实现

堆排序的实现,需要借助于大根堆或者小根队的性质。如果需要从小到大排序,需要借助于大根堆。反之,借助小根堆。

思路

对于下图所示无序原始序列,将其这些在数组里存放的元素,逻辑上看作一个二叉堆。
在这里插入图片描述

1、从第一个非叶子节点开始,把二叉堆调整成一个大根堆

其中,第一个非叶子节点的小标为 ( n - 1 )/2,如图所示为数字8
从第 ( n - 1 )/2号位元素开始,到堆元素(0),进行下沉操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、把堆顶元素和当前末尾元素进行交换,从零号位继续开始进行部分的堆的下沉

上一趟取得的剩余元素的最大值,将其置于末尾,该元素处理完毕。
下一趟就不管该值,对剩下的元素继续进行下沉操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、重复操作二,不断获取剩余元素最大值,并将其下沉

在这里插入图片描述

代码实现

//堆的下沉调整
void siftDown(int arr[], int i, int size)
{
	int val = arr[i];//记录一下要调整的值

	//while(i <= (size-1-1)/2)
	while (i < size / 2)
	{
		int child = 2 * i + 1;
		if (child + 1 < size && arr[child + 1] > arr[child])
		{
			child = child + 1;
		}

		if (arr[child] > val)
		{
			arr[i] = arr[child];
			i = child;  //i 继续指向其孩子,继续调整,直到最后一个有孩子节点的节点
		}
		else
		{
			break;
		}
	}
	arr[i] = val;
}

//堆排序
void HeapSort(int arr[], int size)
{
	int n = size - 1;

	//从第一个非叶子节点
	for (int i = (n - 1) / 2; i >= 0; i--)
	{
		siftDown(arr,i,size);
	}

	//将堆顶元素和当前末尾元素进行交换,从堆顶再一次进行下沉操作
	for (int i = n; i > 0; i--)
	{
		int tmp = arr[0];
		arr[0] = arr[i];
		arr[i] = tmp;

		siftDown(arr,0,i); //第三个参数,参与调整的元素的个数,没处理一次,减一
	}
}

测试

int main()
{
	int arr[10];
	srand(time(NULL));

	for (int i = 0; i < 10; i++)
	{
		arr[i] = rand() % 100 + 1;
	}

	for (int v : arr)
	{
		cout << v << "  ";
	}
	cout << endl;

	HeapSort(arr, sizeof(arr) / sizeof(arr[0]));

	for (int v : arr)
	{
		cout << v << "  ";
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

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

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

相关文章

洛谷P1827 [USACO3.4] 美国血统 American Heritage(c嘎嘎)

题目链接&#xff1a;P1827 [USACO3.4] 美国血统 American Heritage - 洛谷 | 计算机科学教育新生态 题目难度&#xff1a;普及 首先介绍下二叉树的遍历&#xff1a; 学过数据结构都知道二叉树有三种遍历&#xff1a; 1.前序遍历&#xff1a;根左右 2.中序遍历&#xff1a;左根…

# 全过程 快速创建一个Vue项目

如何快速创建一个Vue项目 前置知识 ​ 下载 Node.js 并且进行安装和配置 Node.js&#xff0c;因为 npm&#xff08;Node Package Manager&#xff09;是随 Node.js 一起安装的。 Node.js 下载地址 : Node.js 官方网站 ​ (如果你还没有关于 Node.js&webpack 的相关知识…

小程序 模版与配置

WXML模版语法 一、数据绑定 1、数据绑定的基本原则 &#xff08;1&#xff09;在data中定义数据 &#xff08;2&#xff09;在WXML中使用数据 2、在data中定义页面的数据 3、Mustache语法的格式&#xff08;双大括号&#xff09; 4、Mustache语法的应用场景 &#xff08;…

智慧银行反欺诈大数据管控平台方案(四)

智慧银行反欺诈大数据管控平台的核心内容&#xff0c;是通过整合多维度、多层次的金融交易信息&#xff0c;利用先进的大数据分析、机器学习与人工智能算法&#xff0c;构建一个系统性、实时性和智能化的反欺诈管控网络&#xff0c;旨在提供全面、高效、精准的风险评估机制。该…

MSSQL2022的一个错误:未在本地计算机上注册“Microsoft.ACE.OLEDB.16.0”提供程序

MSSQL2022导入Excel的一个错误&#xff1a;未在本地计算机上注册“Microsoft.ACE.OLEDB.16.0”提供程序 一、导入情况二、问题发现三、问题解决 最近在安装新版SQLServer SSMS 2022后&#xff0c;每次导入Excel都会出现错误提示&#xff1a;未在本地计算机上注册“Microsoft.AC…

基于极角排序实现二维点的逆时针排列

在二维几何计算中,常常需要对一组点进行逆时针排序,以便用于构建多边形、实现凸包算法或绘制几何图形等。本文将详细介绍一种基于极角计算的方法,使用C++实现将点集按照逆时针顺序排列,并提供完整代码和输出示例,适合直接应用于工程项目或算法学习。 一、问题背景 在一个…

Hbase整合Mapreduce案例2 hbase数据下载至hdfs中——wordcount

目录 整合结构准备数据下载pom.xmlMain.javaReduce.javaMap.java操作 总结 整合结构 和案例1的结构差不多&#xff0c;Hbase移动到开头&#xff0c;后面跟随MR程序。 因此对于输入的K1 V1会进行一定的修改 准备 在HBASE中创建表&#xff0c;并写入数据 create "wunaii…

学习threejs,使用canvas更新纹理

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️Texture 贴图 二、&#x1…

K8s 十年回顾(Ten Year Review of K8s)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。Kubernetes 十年回顾 起源与…

AMR移动机器人赋能制造业仓储自动化升级

在当今制造业的激烈竞争中&#xff0c;智能化、数字化已成为企业转型升级的关键路径。一家制造业巨头&#xff0c;凭借其庞大的生产体系和多个仓库资源&#xff0c;正以前所未有的决心和行动力&#xff0c;在制造业智能化浪潮中勇立潮头&#xff0c;开启了降本增效的新篇章。这…

数据分析(一): 掌握STDF 掌握金钥匙-码农切入半导体的捷径

中国的半导体行业必然崛起&#xff01;看清这个大势&#xff0c;就会有很多机会。 今天&#xff0c;我们一起来了解一下半导体行业的一朵金花&#xff1a;STDF。 实际上这只是一种文件格式&#xff0c;但是当你熟练掌握解析这种文件的时候&#xff0c;你就已经打开在这个基础…

自动化测试之等待方式详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在自动化测试中&#xff0c;等待是一个重要的技术&#xff0c;用于处理页面加载、元素定位、元素状态改变等延迟问题。 等待能够确保在条件满足后再进行后续操…

Solving the Makefile Missing Separator Stop Error in VSCode

1. 打开 Makefile 并转换缩进 步骤 1: 在 VSCode 中打开 Makefile 打开 VSCode。使用文件浏览器或 Ctrl O&#xff08;在 Mac 上是 Cmd O&#xff09;打开你的 Makefile。 步骤 2: 打开命令面板 按 Ctrl Shift P&#xff08;在 Mac 上是 Cmd Shift P&#xff09;&…

HTML CSS JS基础考试题与答案

一、选择题&#xff08;2分/题&#xff09; 1&#xff0e;下面标签中&#xff0c;用来显示段落的标签是&#xff08; d &#xff09;。 A、<h1> B、<br /> C、<img /> D、<p> 2. 网页中的图片文件位于html文件的下一级文件夹img中&#xff0c;…

vulnhub靶场之momentum-2

前言 靶机采用virtual box虚拟机&#xff0c;桥接网卡 攻击采用VMware虚拟机&#xff0c;桥接网卡 靶机&#xff1a;momentum-2 192.168.1.40 攻击&#xff1a;kali 192.168.1.16 主机发现 使用arp-scan -l扫描 信息收集 使用namp扫描 这里的命令对目标进行vulner中的漏…

Hadoop生态圈框架部署(八)- Hadoop高可用(HA)集群部署

文章目录 前言一、部署规划二、Hadoop HA集群部署&#xff08;手动部署&#xff09;1. 下载hadoop2. 上传安装包2. 解压hadoop安装包3. 配置hadoop配置文件3.1 虚拟机hadoop1修改hadoop配置文件3.1.1 修改 hadoop-env.sh 配置文件3.3.2 修改 core-site.xml 配置文件3.3.3 修改 …

Flink问题总结

目录 1、Flink 的四大特征(基石) 2、Flink 中都有哪些 Source,哪些 Sink,哪些算子(方法) 3、什么是侧道输出流,有什么用途 4、Flink 中两个流如何合并为一个流 5、Flink 中两个流如何 join 6、Flink 中都有哪些 window,什么是滑动,滚动窗口 7、flink 中都有哪些…

数据结构 (26)图的遍历

前言 数据结构中的图遍历是指从图中的任一顶点出发&#xff0c;按照某种方法访问图中的所有顶点&#xff0c;且每个顶点只访问一次。 一、遍历方法 遍历主要有两种方法&#xff1a;深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09;。 1.深度…

【后端面试总结】golang channel深入理解

在Go语言中&#xff0c;Channel是一种用于在goroutine之间进行通信和同步的重要机制。它提供了一种安全、类型安全的方式来传递数据&#xff0c;使得并发编程变得更加直观和简单。本文将详细介绍Golang中Channel的基本概念、创建与关闭、发送与接收操作&#xff0c;以及相关的使…

RabbitMQ消息可靠性保证机制6--可靠性分析

在使用消息中间件的过程中&#xff0c;难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程&#xff08;轨迹溯源&#xff09;&#xff0c;帮助我们排查问题。 在RabbitMQ中可以使用Firehose实现消息的跟踪&#xff0c;Firehose可…