前段时间在研究Astar算法,里面涉及到几个重要的知识点,链表数据结构、优先队列数据结构,在以前的工作中没有接触到,学习后发现真是好东西,对C++的认知更深了一步,不废话了,下面正文:
队列大家很熟悉,可以用vector数据结构存储,优先队列可能新手不怎么熟悉(老兵可以略过本文),先可以参考下面这个链接对优先队列进行初步熟悉:
1、c++ 优先队列(priority_queue)
问题:
1、上个参考链接中优先队列中用到的测试数据都是int类型,如果把优先队列中用自定义的类或者结构体进行替代可以吗,我们只需要按照结构体中的某个成员变量的值进行排序,按照最大或者最小放在第一个;
2、可以用vector进行替代queue包,并且实现优先队列功能吗?怎么写。
本文主要围绕这两个问题,给出了一个测试代码
测试自定义类说明
自定义了MyPoint结构体、Node类,分别如下
struct MyPoint
{
int x = -1;
int y = -1;
}
class Node
{
public:
Node() {
}
//这个是测试的初始化
Node(double Cost,MyPoint Pos)
{
m_estimatedCost = Cost;//估算成本(预计成本),以该值作为类优先队列的排序方式
m_position = Pos;
}
~Node(){
}
public:
double m_estimatedCost = 0;//估算成本(预计成本)
double m_nodeTotalCost = 1;//总成本
//estimatedCost = nodeTotalCost + estimatedCost;
// C = S + H;
bool m_bObstacle= false;//记录该节点是否是障碍物
Node* m_parent = NULL;//父节点,存储父节点的地址
MyPoint m_position = {-1,-1};
};
问题1的解决方案,vector自定义类排序
第一个问题实质上可以理解对vector容器中的自定义类/结构体进行排序,常用解法有两个:
解决方案1,使用匿名函数进行排序
匿名函数也叫lambda表达式,其介绍如下文章:
C++ Lambda表达式详解
主函数实现代码如下
//输出容器的点
void printfNodeVec(std::vector<Node> NodeVec){
for (int i = 0; i < NodeVec.size(); i++){
printf("(%d,%d)->", NodeVec[i].m_position.x, NodeVec[i].m_position.y);
}
printf("\n");
return;
}
int main()
{
//4个测试数据,第一个值进行初始化要排序的成员变量
//我的测试节点中,位置大小和真正对比的值大小一致,便于好看
Node P1(1, { 1,1 });
Node P2(3, { 3,3 });
Node P3(6, { 6,6 });
Node P4(4, { 4,4 });
//添加到vector中
std::vector<Node> NodeVec;
NodeVec.push_back(P1);
NodeVec.push_back(P2);
NodeVec.push_back(P3);
NodeVec.push_back(P4);
printf("\n vector添加的方法============================================================\n");
printfNodeVec(NodeVec);//把路径打印出来
//使用匿名函数,对vector里自定义类按m_estimatedCost成员变量的值进行排序
std::sort(NodeVec.begin(), NodeVec.end(), [&](Node node1, Node node2)
{
return node1.m_estimatedCost < node2.m_estimatedCost;
});
printf("\n 排序后============================================================\n");
printfNodeVec(NodeVec);//把路径打印出来
return 0;
}
运行结果如下
更换一下匿名函数中的符号,排序倒过来了,如下图:
解决方案2,在Node类中进行定义大于符号或小于符号
我们尝试一下把匿名函数删除掉,进行编译,让系统进行自动编译,代码如下:
int main()
{
//4个测试数据,第一个值进行初始化要排序的成员变量
//我的测试节点中,位置大小和真正对比的值大小一致,便于好看
Node P1(1, { 1,1 });
Node P2(3, { 3,3 });
Node P3(6, { 6,6 });
Node P4(4, { 4,4 });
//添加到vector中
std::vector<Node> NodeVec;
NodeVec.push_back(P1);
NodeVec.push_back(P2);
NodeVec.push_back(P3);
NodeVec.push_back(P4);
printf("\n vector添加的方法============================================================\n");
printfNodeVec(NodeVec);//把路径打印出来
使用匿名函数,对vector里自定义类按m_estimatedCost成员变量的值进行排序
//std::sort(NodeVec.begin(), NodeVec.end(), [&](Node node1, Node node2)
//{
// return node1.m_estimatedCost > node2.m_estimatedCost;
//});
std::sort(NodeVec.begin(), NodeVec.end());//删除掉匿名函数,让系统自己排序
printf("\n 排序后============================================================\n");
printfNodeVec(NodeVec);//把路径打印出来
return 0;
}
编译后有如下错误,通过错误可以判断,==符号和>符号没有找到
系统不知道怎么进行比较和排序,再具体点就是不知道用Node函数中哪个变量进行比较排序,这里只需要在Node类中进行定义比较运算符即可
bool operator<(const Node dstNode)const
{//定义它的小于符号
return this->m_estimatedCost < dstNode.m_estimatedCost;
}
bool operator>(const Node dstNode)const
{//定义它的大于符号
return this->m_estimatedCost > dstNode.m_estimatedCost;
}
bool operator ==(Node n)
{//重构等于等于操作符
if (this->m_estimatedCost == n.m_estimatedCost&& this->m_nodeTotalCost == n.m_nodeTotalCost&& this->m_bObstacle == n.m_bObstacle)
{
return true;
}
else
{
return false;
}
}
所以完整的Node类应该如此
class Node
{
public:
Node() {
}
//这个是测试的初始化
Node(double Cost,MyPoint Pos)
{
m_estimatedCost = Cost;//估算成本(预计成本),以该值作为类优先队列的排序方式
m_position = Pos;
}
~Node(){
}
bool operator<(const Node dstNode)const
{//定义它的小于符号
return this->m_estimatedCost < dstNode.m_estimatedCost;
}
bool operator>(const Node dstNode)const
{//定义它的大于符号
return this->m_estimatedCost > dstNode.m_estimatedCost;
}
bool operator ==(Node n)
{//重构等于等于操作符
if (this->m_estimatedCost == n.m_estimatedCost&& this->m_nodeTotalCost == n.m_nodeTotalCost&& this->m_bObstacle == n.m_bObstacle)
{
return true;
}
else
{
return false;
}
}
public:
double m_estimatedCost = 0;//估算成本(预计成本)
double m_nodeTotalCost = 1;//总成本
//estimatedCost = nodeTotalCost + estimatedCost;
// C = S + H;
bool m_bObstacle= false;//记录该节点是否是障碍物
Node* m_parent = NULL;//父节点,存储父节点的地址
MyPoint m_position = {-1,-1};
};
再编译那个删除了匿名函数的的主函数,发现可以编译通过。运行后结果如下:
注意:operator 这种写法同样适用于结构体中重写结构体的比较方法
问题2的解决方案,用vector重写一个优先队列的数据结构
我们写一个Node的优先队列,里面可以装很多Node类
问题分析:我们首先定义一个优先队列数据结构类(PriorityQueue),然后定义一个成员变量vector< Node>结构;再定义其追加Node方式,排序方式、删除方式、获取优先队列中第一个元素方式等等,后面可以更具实际情况进行自定义添加。在追加一个元素或者删除一个元素后对vector< Node>进行一次排序就行
下面我的写的一个PriorityQueue类如下
class PriorityQueue
{
public:
PriorityQueue();
~PriorityQueue();
std::vector<Node>nodes;
int Length(){
return nodes.size();
}
void Push(Node node){
this->nodes.push_back(node);
Sort_ToUp();//A星算法中,第一个Node应该是成本最低的Node, 所以一般的排序都是按照升序来
}
void Sort_ToUp(){//升序
std::sort(nodes.begin(), nodes.end(), [&](Node node1, Node node2)
{
return node1.m_estimatedCost < node2.m_estimatedCost;
});
}
void Sort_ToDown()
{//降序
std::sort(nodes.begin(), nodes.end(), [&](Node node1, Node node2)
{
return node1.m_estimatedCost > node2.m_estimatedCost;
});
}
void Remove(Node node)
{//根据位置定位元素,
//1、定位要删除的Node ID
auto key = std::find(nodes.begin(), nodes.end(), node);//这里有个排序,用的是
if (key != nodes.end())
{
nodes.erase(key, key+1);//删除指定元素
Sort_ToUp();//A星算法中,第一个Node应该是成本最低的Node,所以一般的排序都是按照升序来
}
else
{
printf("node:(%d,%d),Not in nodes!\n",node.m_position.x,node.m_position.y);
}
}
Node First()
{//获取第一个Node,A星算法中,第一个Node应该是成本最低的Node,所以一般的排序都是按照升序来
if (this->nodes.size() > 0)
{
return this->nodes[0];
}
else
{
return Node();
}
}
bool Contains(Node node)
{//检查队列中是否有某个节点
auto key = std::find(nodes.begin(), nodes.end(), node);
if (key != nodes.end()){//在队列里面
return true;
}
else{//不在队列里面
return false;
}
}
};
由于在进行节点(Node)删除的时候进行过对比MyPoint结构体,所以MyPoint结构体中也需要定义==符号操作,更新后的MyPoint结构体如下:
struct MyPoint
{
int x = -1;
int y = -1;
bool operator ==(MyPoint p2){//重构了等于等于符号
if (this->x == p2.x &&this->y == p2.y){
return true;
}
else{
return false;
}
}
bool operator !=(MyPoint p2){//重构了不等于符号
if (this->x == p2.x &&this->y == p2.y){
return false;
}
else{
return true;
}
}
};
测试添加元素的主函数:
int main()
{
//我的测试节点中,位置大小和真正对比的值大小一致,便于好看
Node P1(1, { 1,1 });
Node P2(3, { 3,3 });
Node P3(6, { 6,6 });
Node P4(4, { 4,4 });
//std::vector<Node> NodeVec;
PriorityQueue Queue1;//创建优先队列,使用vector添加方式进行添加点
Queue1.nodes.push_back(P1);
Queue1.nodes.push_back(P2);
Queue1.nodes.push_back(P3);
Queue1.nodes.push_back(P4);
printf("\n vector添加的方法============================================================\n");
printfNodeVec(Queue1.nodes);//把路径打印出来
printf("\n调用成员函数的添加方法============================================================\n");
PriorityQueue Queue2;//创建优先队列2,使用成员函数方式添加元素
Queue2.Push(P1);
Queue2.Push(P2);
Queue2.Push(P3);
Queue2.Push(P4);
printfNodeVec(Queue2.nodes);//把路径打印出来
printf("\n对Queue1升序排序后============================================================\n");
Queue1.Sort_ToUp();
printfNodeVec(Queue1.nodes);//把路径打印出来
printf("\n对Queue1降序排序后============================================================\n");
Queue1.Sort_ToDown();
printfNodeVec(Queue1.nodes);//把路径打印出来
printf("\n对Queue1删除(6,6)============================================================\n");
Queue1.Remove(P3);
printfNodeVec(Queue1.nodes);//把路径打印出来
printf("\n对Queue1删除(6,6)============================================================\n");
Node P5(8, { 8,8 });
Queue1.Remove(P5);
printfNodeVec(Queue1.nodes);//把路径打印出来
return 0;
}
运行结果如下,可以实现优先队列中添加元素
2023年第一篇文章,以备查阅。辉 2023.1.13