开源优先队列FastPriorityQueue源码阅读

发布时间 2023-03-31 20:47:12作者: 昂流

FastPriorityQueue

  源码连接:

https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp  

  大致结构:

  1节点在内存中的结构还是数组,且首节点为无意义节点,有效节点从索引1开始。(见FastPriorityQueue的T[] _nodes)
  2维护的节点必须时固定的继承。(见FastPriorityQueueNode)
  3该队列为非线程安全的
  4不要对不在优先级队列中的节点调用UpdatePriority(),也不要对在优先级队列的节点调用Enqueue()。
  5一个节点只能在一个queue里面,如果要迁移节点到新的队列时应该先将其从原本的queue中移除并重置自身(ResetNode函数)后再加入新queue。

  如何实现判断界定是否在容器中。

  节点FastPriorityQueueNode中维护了一个数组的索引(见FastPriorityQueueNode的QueueIndex),该索引从1开始。
时间复杂度为O(1)。

  是否支持优先级动态变,变更后,如何排序?

  支持优先级动态变。(入口见FastPriorityQueueNode的UpdatePriority)
  变更后,需要维持大顶堆的大顶,对现有节点(包含新增或者变更节点)根据优先级触发节点上浮CascadeUp或节点下沉CascadeDown。
  采用大顶堆的方式,老样子,采用位运算(<<1)方式计算堆中父节点位置,但只维持了大顶,并没有完全排序,即数组中的元素优先级不是从大到小的。
如果是内部节点动态变更优先级,比如随着节点增加,可能构造这样一棵子树:父节点优先级为8,左右子节点的优先级分别为3和5,它本身就不是完全从大到小,当优先级3的节点优先级变成9,只会让它跟父节点交换,交换后依旧不是完全从大到小。
  此外,新增一个同优先级的节点是不会把原本节点顶后,而是对新增节点进行下沉操作。(见FastPriorityQueueNode的CascadeDown)

private void CascadeUp(T node)
{
    //aka Heapify-up
    int parent;
    if(node.QueueIndex > 1)
    {
        parent = node.QueueIndex >> 1;
        T parentNode = _nodes[parent];
        if(HasHigherOrEqualPriority(parentNode, node))
            return;

        //Node has lower priority value, so move parent down the heap to make room
        _nodes[node.QueueIndex] = parentNode;
        parentNode.QueueIndex = node.QueueIndex;

        node.QueueIndex = parent;
    }
    else
    {
        return;
    }
    while(parent > 1)
    {
        parent >>= 1;
        T parentNode = _nodes[parent];
        if(HasHigherOrEqualPriority(parentNode, node))
            break;

        //Node has lower priority value, so move parent down the heap to make room
        _nodes[node.QueueIndex] = parentNode;
        parentNode.QueueIndex = node.QueueIndex;

        node.QueueIndex = parent;
    }
    _nodes[node.QueueIndex] = node;
}
private void CascadeDown(T node)
{
    //aka Heapify-down
    int finalQueueIndex = node.QueueIndex;
    int childLeftIndex = 2 * finalQueueIndex;

    // If leaf node, we're done
    if(childLeftIndex > _numNodes)
    {
        return;
    }

    // Check if the left-child is higher-priority than the current node
    int childRightIndex = childLeftIndex + 1;
    T childLeft = _nodes[childLeftIndex];
    if(HasHigherPriority(childLeft, node))
    {
        // Check if there is a right child. If not, swap and finish.
        if(childRightIndex > _numNodes)
        {
            node.QueueIndex = childLeftIndex;
            childLeft.QueueIndex = finalQueueIndex;
            _nodes[finalQueueIndex] = childLeft;
            _nodes[childLeftIndex] = node;
            return;
        }
        // Check if the left-child is higher-priority than the right-child
        T childRight = _nodes[childRightIndex];
        if(HasHigherPriority(childLeft, childRight))
        {
            // left is highest, move it up and continue
            childLeft.QueueIndex = finalQueueIndex;
            _nodes[finalQueueIndex] = childLeft;
            finalQueueIndex = childLeftIndex;
        }
        else
        {
            // right is even higher, move it up and continue
            childRight.QueueIndex = finalQueueIndex;
            _nodes[finalQueueIndex] = childRight;
            finalQueueIndex = childRightIndex;
        }
    }
    // Not swapping with left-child, does right-child exist?
    else if(childRightIndex > _numNodes)
    {
        return;
    }
    else
    {
        // Check if the right-child is higher-priority than the current node
        T childRight = _nodes[childRightIndex];
        if(HasHigherPriority(childRight, node))
        {
            childRight.QueueIndex = finalQueueIndex;
            _nodes[finalQueueIndex] = childRight;
            finalQueueIndex = childRightIndex;
        }
        // Neither child is higher-priority than current, so finish and stop.
        else
        {
            return;
        }
    }

    while(true)
    {
        childLeftIndex = 2 * finalQueueIndex;

        // If leaf node, we're done
        if(childLeftIndex > _numNodes)
        {
            node.QueueIndex = finalQueueIndex;
            _nodes[finalQueueIndex] = node;
            break;
        }

        // Check if the left-child is higher-priority than the current node
        childRightIndex = childLeftIndex + 1;
        childLeft = _nodes[childLeftIndex];
        if(HasHigherPriority(childLeft, node))
        {
            // Check if there is a right child. If not, swap and finish.
            if(childRightIndex > _numNodes)
            {
                node.QueueIndex = childLeftIndex;
                childLeft.QueueIndex = finalQueueIndex;
                _nodes[finalQueueIndex] = childLeft;
                _nodes[childLeftIndex] = node;
                break;
            }
            // Check if the left-child is higher-priority than the right-child
            T childRight = _nodes[childRightIndex];
            if(HasHigherPriority(childLeft, childRight))
            {
                // left is highest, move it up and continue
                childLeft.QueueIndex = finalQueueIndex;
                _nodes[finalQueueIndex] = childLeft;
                finalQueueIndex = childLeftIndex;
            }
            else
            {
                // right is even higher, move it up and continue
                childRight.QueueIndex = finalQueueIndex;
                _nodes[finalQueueIndex] = childRight;
                finalQueueIndex = childRightIndex;
            }
        }
        // Not swapping with left-child, does right-child exist?
        else if(childRightIndex > _numNodes)
        {
            node.QueueIndex = finalQueueIndex;
            _nodes[finalQueueIndex] = node;
            break;
        }
        else
        {
            // Check if the right-child is higher-priority than the current node
            T childRight = _nodes[childRightIndex];
            if(HasHigherPriority(childRight, node))
            {
                childRight.QueueIndex = finalQueueIndex;
                _nodes[finalQueueIndex] = childRight;
                finalQueueIndex = childRightIndex;
            }
            // Neither child is higher-priority than current, so finish and stop.
            else
            {
                node.QueueIndex = finalQueueIndex;
                _nodes[finalQueueIndex] = node;
                break;
            }
        }
    }
}

  移除节点如何实现:

  先将移除节点A和末尾节点B做个数组位置上的交换。然后对交换后的B进行大顶堆的调整,并置空尾节点。

  是否支持元素查询

  元素的查询需要自行实现。