【c++】优先级队列与仿函数:C++编程的强大组合

news2024/11/24 1:03:34

Alt

🔥个人主页Quitecoder

🔥专栏c++笔记仓

Alt

朋友们大家好,本篇文章我们来讲解优先级队列priority_queue

目录

  • `1.priority_queue的介绍和使用`
    • `函数使用`
    • `仿函数的使用与介绍`
    • `greater和less`
  • `2.priority_queue的模拟实现`
    • `基本框架`
    • `两个调整函数的优化`
    • `对于自定义类型的其他仿函数使用`

1.priority_queue的介绍和使用

在这里插入图片描述

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的
  2. 此上下文类似于,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
    • empty():检测容器是否为空
    • size():返回容器中有效元素个数
    • front():返回容器中第一个元素的引用
    • push_back():在容器尾部插入元素
    • pop_back():删除容器尾部元素
  5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作

函数使用

在这里插入图片描述

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆

在这里插入图片描述
🔥构造函数

在这里插入图片描述
有关这些参数的使用我们后文进行详细讲解,创建一个优先级队列:

priority_queue <int> pq;

🔥empty( )

检测优先级队列是否为空,是返回true,否则返回false

🔥top( )

返回优先级队列中最大(最小元素),即堆顶元素

🔥push( )

在优先级队列中插入元素x

🔥pop( )

删除优先级队列中最大(最小)元素,即堆顶元素

测试函数:

void test()
{
	priority_queue<int> pq;
	pq.push(3);
	pq.push(1);
	pq.push(5);
	pq.push(2);
	pq.push(4);
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
}

我们按照不同顺序插入,来观察它的取顶端元素结果:

在这里插入图片描述
默认情况下,priority_queue是大堆

那么如何构建一个小堆呢?这里就涉及到仿函数

仿函数的使用与介绍

在这里插入图片描述
s在 C++ 的 std::priority_queue` 实现中,默认情况下,优先级是用元素之间的小于操作来判定的,即元素越大优先级越高

模板参数解释如下:

  1. class Container = vector<T>
    这是用来内部存储队列中元素的容器类型。默认是 std::vector,但也可以是其他符合要求的容器类型,比如 std::deque。有一点要注意的是,必须支持随机访问迭代器(Random Access Iterator),以及 front()push_back()pop_back() 的操作

  2. class Compare = less<typename Container::value_type>
    这是用来比较元素优先级的比较函数对象。默认是 std::less,该函数使得最大的元素被认为是最高优先级(形成最大堆)。如果想要最小的元素为最高优先级(形成最小堆),可以通过提供 std::greater 函数对象作为这个模板参数来改变这个行为

默认使用less这个仿函数,如果我们需要建立小堆,需要自己传参:

priority_queue<int,vector<int>,greater<int>> pq;

我们接下来详细讲解一下什么是仿函数

在C++中,仿函数是一种使用对象来模拟函数的技术。它们通常是通过类实现的,该类重载了函数调用操作符operator())。仿函数可以像普通函数一样被调用,但它们可以拥有状态(即,它们可以包含成员变量,继承自其它类等)

下面是使用仿函数的一个简单例子:

#include <iostream>
using namespace std;
// 定义一个仿函数类
class Add {
public:
    // 构造函数,可以用来初始化内部状态,这里没有使用
    Add() {}

    // 重载函数调用操作符
    int operator()(int a, int b) {
        return a + b;
    }
};

int main() {
    // 创建一个仿函数对象
    Add add_func;
    // 使用仿函数对象
    cout << add_func(10, 5) << endl;
    cout << add_func.operator()(10,5)<<endl
    cout << Add()(10,5)<<endl;
    return 0;
}

在这个例子中,我们定义了一个名为 Add 的仿函数类,它重载了 operator() 来实现两数相加的功能。然后在 main 函数中创建了该类的一个实例 add_func 并且像调用函数一样使用 add_func(10, 5) 来求和

Add()(10,5)使用了匿名对象

仿函数广泛用于C++标准库中,特别是在算法std::sort, std::for_each 等)中作为比较函数或者操作函数,以及在容器(如 std::set 或者 std::map)中作为排序准则
在这里插入图片描述

这是如何在 std::sort 算法中使用仿函数的一个实例:

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class Compare {
public:
    bool operator()(int a, int b) {
        return a > b; // 降序排列
    }
};

int main() {
    vector<int> v {2, 4, 1, 3, 5};    
    // 使用仿函数对象
    sort(v.begin(), v.end(), Compare());
    for (int i : v) {
        std::cout << i << " ";
    }
    // 输出:5 4 3 2 1 
    return 0;
}

在上面的例子中,Compare 仿函数用来定义一个降序规则,随后在 std::sort 中将其实例化并传递给算法进行降序排序

仿函数的一个主要优点是它们可以保持状态,这意味着它们可以在多次调用之间保存和修改信息。这使得它们非常灵活和强大。此外,由于它们是类的实例,它们也可以拥有额外的方法和属性

greater和less

std::greaterstd::less 是预定义的函数对象模板,用于执行比较操作。它们定义在<functional>头文件中。std::greater 用来执行大于(>)的比较,而 std::less 用来执行小于(<)的比较

以下是 std::lessstd::greater 的典型用法:

#include <functional>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v {5, 2, 4, 3, 1};

    // 使用 std::less 来升序排序
    sort(v.begin(), v.end(), less<int>());
    for (int i : v) {
        cout << i << " ";
    }
    cout << endl;

    // 使用 std::greater 来降序排序
    sort(v.begin(), v.end(), greater<int>());
    for (int i : v) {
        cout << i << " ";
    }
    cout << endl;

    return 0;
}

输出:

1 2 3 4 5
5 4 3 2 1

函数对象模板 std::lessstd::greater 的实现通常如下:

namespace std {

template<class T>
struct less {
    bool operator()(const T& lhs, const T& rhs) const {
        return lhs < rhs;
    }
};

template<class T>
struct greater {
    bool operator()(const T& lhs, const T& rhs) const {
        return lhs > rhs;
    }
};

} // namespace std

在C++11及之后的版本中,由于引入了泛型 lambda 表达式,直接传递 lambda 函数给标准算法(如 std::sort),使得使用 std::greaterstd::less 变得不那么必要了。以下是使用lambda表达式的例子:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    vector<int> v {2, 4, 1, 3, 5};
    
    // 使用lambda表达式作为比较函数进行升序排列
    sort(v.begin(), v.end(), [](int a, int b) { return a < b; });

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

    cout << endl;

    // 使用lambda表达式作为比较函数进行降序排列
    sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

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

    cout << endl;

    return 0;
}

输出:

1 2 3 4 5
5 4 3 2 1

来看这里的参数传递

priority_queue<int,vector<int>,greater<int>> pq;
sort(v.begin(), v.end(), greater<int>());

在这里插入图片描述
priority_queue传的是一个类型,而sort需要传递对象,我们这里传递的是匿名对象

2.priority_queue的模拟实现

基本框架

基本框架如下:

#include<vector>
#include<iostream>
#include<list>

using namespace std;
namespace myown {
	template<class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		void adjust_up(size_t child)
		{}
		void push(const T& x)
		{}
		void adjust_down(size_t parent)
		{}
		void pop()
		{}
		bool empty()
		{}
		size_t size()
		{}
		const T& top()
		{
		}
	private:
		Container _con;
	};
}

它的底层是堆,我们就使用vector作为底层容器

我们先补充简单的接口

🔥push( )

优先级队列里面,我们要插入数据,会进行向上调整

所以实现如下:

void push(const T& x)
{
	_con.push_back(x);
	adjust_up(_con.size() - 1);
}

🔥pop( )

pop需要删除堆顶的数据,我们的方式是首尾交换,尾删,再向下调整

void pop()
{
	swap(_con[0], _con[_con.size() - 1]);
	_con.pop_back();
	adjust_down(0);
}

🔥empty( )

直接判断即可

bool empty()
{
	return _con.empty();
}

🔥size( )

size_t size()
{
	return _con.size();
}

🔥top( )

取堆顶元素

const T& top()
{
	return _con[0];
}

接着我们来完成两个关键的函数,向上调整和向下调整

🔥adjust_up( )

在这里插入图片描述

当前位置每次和他的父节点比较

void adjust_up(size_t child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (_con[child]>_con[parent])
		{
			swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
  • 对于给定的子节点索引child,其父节点的索引计算为(child - 1) / 2
  • 循环条件:while (child > 0)循环确保我们不会尝试移动根节点(因为根节点的索引为0,没有父节点)。循环继续执行,只要当前节点的索引大于0。
  • 完成交换后,更新child变量为原父节点的索引,因为交换后当前元素已经移动到了父节点的位置。然后,对新的child值重新计算parent索引,继绀执行可能的进一步交换
  • 循环终止条件:如果当前节点的值不小于其父节点的值(即堆的性质得到了满足),循环终止,else break;执行

🔥adjust_down( )
在这里插入图片描述

void Ajustdown(size_t parent)
{
	size_t child = parent * 2 + 1;
	while (child<_con.size())
	{
		if (child + 1 < _con.size() && _con[child + 1] >_con[child])//防止只有左孩子而越界
		{
			child++;
		}
		if (_con[child] >_con[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

两个调整函数的优化

我上面实现的代码只能完成一种堆的实现,如何进行封装使我们能够根据传参实现大堆或小堆呢?

这里就涉及到仿函数了,注意看我们模版中的第三个参数:

template<class T, class Container = vector<T>, class Compare = less<T>>

我们首先补充greater和less两个类:

template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

我们控制大小堆,则需要控制两个adjust函数的比较逻辑

仿函数本质是一个类,可以通过模版参数进行传递,默认传的为less,控制它为大堆

template<class T, class Container = vector<T>, class Compare = less<T>>
void adjust_up(size_t child)
{
	Compare com;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//if (_con[child] > _con[parent])
		//if (_con[parent] < _con[child])
		if (com(_con[parent], _con[child]))
		{
			swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

com是Compare的对象,它的对象可以像函数一样使用

void adjust_down(size_t parent)
{
	Compare com;
	size_t child = parent * 2 + 1;
	while (child < _con.size())
	{
		//if (child + 1 < _con.size() && _con[child + 1] >_con[child])
		//if (child + 1 < _con.size() && _con[child] < _con[child +1])
		if (child + 1 < _con.size() &&com(_con[child],_con[child +1]))
		{
			++child;
		}
		//if (_con[child] > _con[parent])
		//if (_con[parent] < _con[child])
		if (com(_con[parent], _con[child]))
		{
			swap(_con[child], _con[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

对于自定义类型的其他仿函数使用

如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}	
	bool operator<(const Date& d)const
   {
	return (_year < d._year) ||
		(_year == d._year && _month < d._month) ||
		(_year == d._year && _month == d._month && _day < d._day);
   }
    bool operator>(const Date& d)const
   {
	return (_year > d._year) ||
		(_year == d._year && _month > d._month) ||
		(_year == d._year && _month == d._month && _day > d._day);
    }
    friend ostream& operator<<(ostream& _cout, const Date& d)
    {
    _cout << d._year << "-" << d._month << "-" << d._day;
     return _cout;
    }
private:
	int _year;
	int _month;
	int _day;
};

void test()
{
	myown::priority_queue<Date, vector<Date>, myown::greater<Date>> pq;

	Date d1(2024, 4, 8);
	pq.push(d1);
	pq.push(Date(2024, 4, 10));
	pq.push({ 2024, 2, 15 });

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

结果如下:
在这里插入图片描述
再看下面这个:我如果存的是指针呢?

void test5()
{
	myown::priority_queue<Date*, vector<Date*>, myown::greater<Date*>> pqptr;
	pqptr.push(new Date(2024, 4, 14));
	pqptr.push(new Date(2024, 4, 11));
	pqptr.push(new Date(2024, 4, 15));

	while (!pqptr.empty())
	{
		cout << *(pqptr.top()) << " ";
		pqptr.pop();
	}
	cout << endl;
}

在这里插入图片描述
在这里插入图片描述
我们发现两次运行的结果不一样,这里是因为我们比较的是地址,而不是值,地址是new出来的,无法保证大小

我们需要重新构造一个仿函数:

class GreaterPDate
{
public:
	bool operator()(const Date* p1, const Date* p2)
	{
		return *p1 > *p2;
	}
};

myown::priority_queue<Date*, vector<Date*>,GreaterPDate> pqptr;

在这里插入图片描述
再看一个实际问题,如果我的一个结构体存储一个商品

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价

	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

我们可以利用仿函数来实现对不同指标的排序

struct ComparePriceLess
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price < gr._price;
	}
};
struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};

struct CompareEvaluateLess
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._evaluate < gr._evaluate;
	}
};
struct CompareEvaluateGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._evaluate > gr._evaluate;
	}
};
int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
	3 }, { "菠萝", 1.5, 4 } };

	sort(v.begin(), v.end(), ComparePriceLess());
	sort(v.begin(), v.end(), ComparePriceGreater());
	sort(v.begin(), v.end(), CompareEvaluateLess());
	sort(v.begin(), v.end(), CompareEvaluateGreater());
}

有了仿函数,我们就可以对这种自定义类型实现想要的排序

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

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

相关文章

arthas如何排除CPU使用率过高问题

1、首先启动arthas java -jar arthas-boot.jar 2、使用thread查看各线程CPU使用率 thread 可以看到CPU使用率最高的有2个线程&#xff0c;以线程ID为19的为例子&#xff1a; 输入thread 19查看线程19的堆栈信息&#xff1a; thread 19 可以看到是(CpuController.java:78行…

Git Tag:为你的代码版本打上优雅的标签

为你的代码版本打上优雅的标签 在软件开发过程中&#xff0c;版本控制是项目管理的重要一环。Git 作为最流行的版本控制系统之一&#xff0c;为我们提供了强大的工具来管理代码版本。其中&#xff0c;git tag 命令允许我们为代码仓库中的特定提交打上标签&#xff0c;这些标签…

【面试经典 150 | 数组】找出字符串中第一个匹配项的下标

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;find方法二&#xff1a;暴力匹配方法三&#xff1a;KMP 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;…

Python | Leetcode Python题解之第59题螺旋矩阵II

题目&#xff1a; 题解&#xff1a; class Solution:def generateMatrix(self, n: int) -> List[List[int]]:matrix [[0] * n for _ in range(n)]num 1left, right, top, bottom 0, n - 1, 0, n - 1while left < right and top < bottom:for col in range(left, r…

Flink checkpoint 源码分析- Flink Checkpoint 触发流程分析

序言 最近因为工作需要在阅读flink checkpoint处理机制&#xff0c;学习的过程中记录下来&#xff0c;并分享给大家。也算是学习并记录。 目前公司使用的flink版本为1.11。因此以下的分析都是基于1.11版本来的。 在分享前可以简单对flink checkpoint机制做一个大致的了解。 …

Qt:Qt框架的初步认识和基本使用

文章目录 Qt是什么Qt的优点Qt开发环境的基本使用对象树其他控件输入框按钮 本篇总结的是对于Qt框架的基本认识 Qt是什么 Qt框架是一个跨平台的C图形用户界面应用程序框架&#xff0c;框架是一群大佬发明出来的&#xff0c;帮助新手使用的一个内容&#xff0c;如果没有框架的存…

KUKA机器人如何给IO信号或寄存器添加中文注释信息?

KUKA机器人如何给IO信号或寄存器添加中文注释信息? 如下图所示,首先,我们需要登录专家以上用户权限(默认密码KUKA), 如下图所示,点击“投入运行”—“网络配置”, 如下图所示,此时机器人的IP地址为192.168.1.10, 如下图所示,用一根网线连接机器人控制柜到笔记…

使用ClassFinal实现springboot项目jar包加密

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

高级IO|从封装epoll服务器到实现reactor服务器|Part2

项目复习&#xff1a;从封装epoll_server到实现reactor服务器(part2) 项目复习&#xff1a;从封装epoll_server到实现reactor服务器(part2) 基本结构搭建好为什么上面我们写的epoll的recv是不正确的&#xff1f;sock要封装了&#xff0c;要维护缓冲区封装epoll(1)继续先写tcp_…

【算法刷题 | 贪心算法07】4.29(用最少数量的箭引爆气球、无重叠区间)

文章目录 12.用最少数量的箭引爆气球12.1题目12.2解法&#xff1a;贪心12.2.1贪心思路12.2.2代码实现 13.无重叠区间13.1题目13.2解法&#xff1a;贪心13.2.1贪心思路13.2.2代码实现 12.用最少数量的箭引爆气球 12.1题目 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面…

js之JSON

json 是一种轻量级的数据交换格式。 json 就是一种在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互。 let data {name:张三,age:18}; console.log(data); // 对象 let str JSON.stringify(data); console.log(str); // json 数据 l…

3D模型在线查看利器,支持多种模型格式!

作为3D设计师&#xff0c;你是否曾遇到过这样的烦恼&#xff1a; 客户想看设计好的3D模型作品&#xff0c;但是客户身边没电脑&#xff0c;或者电脑没有3D查看器&#xff0c;又不会使用三维软件&#xff0c;从而无法及时查看模型。 还有就是&#xff0c;自己累积了很多3D模型作…

网易云怎么改IP地址到其他城市

在数字音乐的时代&#xff0c;网易云音乐以其丰富的音乐库和个性化的推荐算法赢得了众多用户的喜爱。然而&#xff0c;有些用户可能会遇到一个问题&#xff1a;自己的IP地址显示的是家乡或当前所在的城市&#xff0c;但自己希望显示的是其他城市。那么&#xff0c;网易云音乐是…

解决TIVA飞控玄学类问题的通解,用魔法打败魔法

问题&#xff1a;我遭遇了玄学问题&#xff0c;出现飞机在起降过程中&#xff0c;位置晃动&#xff0c;突然出现的&#xff0c;昨天还好好的&#xff0c;位置地点都没换&#xff0c;今天中午测试了5、6次每次都这样&#xff0c;现在茫然无措&#xff0c;小哥救我&#xff1f; 这…

手写 轮播效果

此处只做了手动点击的效果,未处理自动轮播,基于vue2书写 , 逻辑: 点击左边的图标,进行上一个处理,若此时在第一项,则return,否则将当前所在数据-1;点击右边的图标,进行下一个处理,若此时在最后一项,则return,否则将所在数据1;当单独点击某数据时,若当前就是点击项,则return,否…

与 Apollo 共创生态:探索智能驾驶新时代

前言 随着百度Apollo的七周年大会在北京车展前夕成功举办&#xff0c;我们迎来了一场关于智能汽车未来的思想盛宴。在这次主题为“破晓•拥抱智变时刻”的盛会上&#xff0c;百度Apollo发布了一系列令人振奋的智能驾驶产品&#xff0c;从领航辅助驾驶到智能座舱&#xff0c;再到…

[C++][算法基础]区间覆盖(贪心 + 区间问题4)

给定 &#x1d441; 个闭区间 [&#x1d44e;&#x1d456;,&#x1d44f;&#x1d456;] 以及一个线段区间 [&#x1d460;,&#x1d461;]&#xff0c;请你选择尽量少的区间&#xff0c;将指定线段区间完全覆盖。 输出最少区间数&#xff0c;如果无法完全覆盖则输出 −1。 …

界面组件DevExpress中文教程 - 如何在Node.js应用中创建报表?

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 获取DevExpress Reporting最新正式版下载(Q技术…

电商日志项目(一)

电商日志项目 一、项目体系架构设计1. 项目系统架构2. 项目数据流程二、环境搭建1. NginxLog文件服务1.1. 上传,解压1.2. 编译安装1.3. 启动验证2. Flume-ng2.1. 上传解压2.2. 修改配置文件2.3. 修改环境变量2.4. 验证3. Sqoop3.1. 上传解压3.2. 配置环境变量3.3. 修改配置文件…

「玻尔曾孙」领衔!超辐射原子,重塑全球精准测时——

超辐射原子能够帮助我们以前所未有的精度测量时间。在哥本哈根大学最近的一项研究中&#xff0c;研究人员开发了一种新的测量时间间隔&#xff08;秒&#xff09;的方法&#xff0c;这种方法克服了目前最先进原子钟面临的一些限制。 这一成就有望在多个领域产生深远影响&#x…