微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

带有自定义比较器的 STL 优先级队列未按预期工作

如何解决带有自定义比较器的 STL 优先级队列未按预期工作

我正在尝试使用自定义运算符实现优先级队列。该算法试图找到要完成的最小增量,以便数组中没有两个相邻元素的绝对差 > 1。
为此,我获取数组中的最大元素“x”并将其邻居修改为 x-1,然后对其他元素重复相同的操作
代码如下:

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;

int arr[100],visited[100];
int sizeOfArray;
struct comp
{
public:
    bool operator() (int x1,int x2) {
    return arr[x1] < arr[x2];
    }
};  

int main(){
    cin>>sizeOfArray;
    priority_queue<int,vector<int>,comp> priorityQue;
    for(int i = 0; i < sizeOfArray; i++) {
        cin>>arr[i];
        priorityQue.push(i);
        visited[i]=0;
    }
    while(!priorityQue.empty()) {
        int index = priorityQue.top();
        priorityQue.pop();
        if(visited[index])   
            continue;
        visited[index]=1;

        cout<<"Currently at index: "<<index<<endl;

        int currentElement = arr[index];
        int dx[] = {-1,1};    // left and right neighbours
        for(int k = 0; k < 2; k++) {
            int nextIndex = index + dx[k];
            if( nextIndex >= 0 && nextIndex < sizeOfArray && 
                (currentElement - arr[nextIndex]) > 1 ) 
            {
                arr[nextIndex] = currentElement - 1;
                cout<<"Modifying index :"<<nextIndex<<endl;
                cout<<"New Array is: ";
                // print array 
                for(int z=0;z<sizeOfArray;z++)
                    cout<<arr[z]<<" ";
                cout<<endl;
                
                priorityQue.push(nextIndex);
                cout<<"Pushing index "<<nextIndex<<" to queue"<<endl;
            }
        }
    }
    return 0;
}

对于输入:

4
4 1 1 0

输出是:

当前位于索引:0
修改索引:1
新数组为:4 3 1 0
将索引 1 推送到队列
目前在索引:2
目前在索引:1
修改索引:2
新数组为:4 3 2 0
将索引 2 推送到队列
当前索引:3

我发现优先级队列没有按照比较器提取最大元素。访问索引 0 后,数组变为 4 3 1 0 因此索引 1 应该是下一个,但在这种情况下索引 2 被拾取。 我错过了什么??

解决方法

您在将项目放入队列后对其进行修改,并且仍然是该队列的一部分。这不受优先级队列的支持,并且会产生未指定的结果。 C++ STL 不提供可更新的优先级队列。

但是,看到您的用例,我怀疑这是竞争性算法编程。这个用例有不错的替代品。 我不会推荐它们用于生产代码(至少在没有适当抽象的情况下不会。

第一个“正确”的替代方法是改用 std::set<std::pair<...>>。您的代码将保持非常相似,但存在一些重要差异:

  • 您不需要自定义比较器,而是依靠对比较(您需要使用 std::greater 作为比较器才能将最大的项目“放在顶部”,
  • 你将把 {a[index],index} 作为这个集合的元素,
  • 您将使用 .begin() 而不是 .top()
  • 您需要在更新值之前.erase() 项,并使用新值再次插入它们。

我相信上面与优先队列的标准实现具有相同的复杂性。尽管更新看起来很慢,但我们只进行了两次 O(log n) 操作 - 这与堆结构中的实际更新具有相同的复杂性。

您可以像示例中那样使用间接比较器来实现它。通常它会起作用,但您仍然需要围绕更新进行擦除和插入流程。此外,您还需要比较比较器中的索引,以使具有相同优先级的项目不同,从而删除正确的项目。

此外,在许多常见情况下,我们还可以使用另一个技巧,例如 Dijkstra 或 Prim 的算法。在这些情况下,我们只会将优先级更新为更高(更低的值)。在这种情况下,我们可以忽略擦除项目,而只是添加重复项。这是有效的,因为单个查询/更新的时间复杂度变为 O(log n^2) = O(2 log n) = O(log n)。内存复杂度增加,但这通常不是问题。

在最后一种情况下,您可以使用其他容器来满足您的偏好,std::priority_queue<std::pair<...>>std::multimap<...> 都可以很好地工作。但在所有这些中,您需要将优先级作为插入项的一部分,而不是通过间接比较器使用它。

作为附录,这是您的代码,其中的更改按预期工作:

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;

int arr[100],visited[100];
int sizeOfArray;


int main(){
    cin>>sizeOfArray;
    priority_queue<std::pair<int,int>> priorityQue;
    for(int i = 0; i < sizeOfArray; i++) {
        cin>>arr[i];
        priorityQue.push({arr[i],i});
        visited[i]=0;
    }
    while(!priorityQue.empty()) {
        int index = priorityQue.top().second;
        priorityQue.pop();
        if(visited[index])   
            continue;
        visited[index]=1;

        cout<<"Currently at index: "<<index<<endl;

        int currentElement = arr[index];
        int dx[] = {-1,1};    // left and right neighbours
        for(int k = 0; k < 2; k++) {
            int nextIndex = index + dx[k];
            if( nextIndex >= 0 && nextIndex < sizeOfArray && 
                (currentElement - arr[nextIndex]) > 1 ) 
            {
                arr[nextIndex] = currentElement - 1;
                cout<<"Modifying index :"<<nextIndex<<endl;
                cout<<"New Array is: ";
                // print array 
                for(int z=0;z<sizeOfArray;z++)
                    cout<<arr[z]<<" ";
                cout<<endl;
                
                priorityQue.push({arr[nextIndex],nextIndex});
                cout<<"Pushing index "<<nextIndex<<" to queue"<<endl;
            }
        }
    }
    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。