D-ary Heap implements a fast priority queue (C #)

Source: Internet
Author: User
D-ary Heap Introduction:


The D-ary heap is a generalized version of binary heap (d=2), and the D-ary heap has a maximum of D child nodes per non-leaf node.



The D-ary heap has the following properties:


    1. Similar to complete binary tree, except for the last layer of the tree, all the other layers fill the nodes, and the nodes are added from left to right.
    2. Like binary heap, it also has two types of maximum heap and minimum heap.


An example of the 3-ary heap is given below:


 
3-ary max heap - root node is maximum of all nodes
             10
       /      |     \
      7       9      8
  /  |  \    /
 4   6   5  7


3-ary min heap -root node is minimum of all nodes
             10
         /    |    \
       12     11    13
     / | \
    14 15 18 


The height of a full D-fork tree with n nodes is given by LOGDN.


Application of D-ary heap:

d-ary Heapis often used to further implement priority queues, d-ary heap implementation of priority queuebinary HeapThe implementation of the priority queue is more efficient in terms of adding new elements.binary heap:o (log2n) vs d-ary heap: O (LOGKN), when D > 2 o'clock,logkn < log2n . ButPriority queue implemented by the D-ary heapThe disadvantage is that the first element of the priority queue is extracted thanpriority queue implemented by binary heapNeed to consume more performance. binary heap: O (log2n) vs D-ary Heap:o ((d-1) LOGDN), when D > 2 o'clock,(d-1) logdn > log2n, by The logarithm of the bottom formula can be proven . The results look mixed, so what's the best use of d-ary heap ? The answer is the common pathfinding algorithm in the game. Take a * and Dijkstra algorithm for example. Both generally require a priority queue (some * algorithms do not apply priority queues, such as an iterative burn A *), and these algorithms tend to add more neighboring nodes to the queue when the first element of the queue is fetched. That is, adding nodes is much more than the number of fetches. Well,d-ary heap can complement each other. In addition, thed-ary heap is more cache-friendly than the binary heap , and more sub-nodes are adjacent. Therefore, the actual operating efficiency is often better.

d-ary Heap and Priority queue implementations:


We use an array to implement the d-ary heap, where the array starts with 0 and can get the following rule:


    • If the node is a non-root node, then using the node's index I can get its parent node index, the parent node is (i-1)/D;
    • If the index of the node is I, then its child node index is (d*i) +1, (d*i) +2 .... (d*i) +d;
    • If the heap size is N, the index of the last non-leaf node is (n-1)/D; (Note: the implementation given in this article does not use the rule)


Building the d-ary heap: The implementation in this paper focuses on the further implementation of priority queuing and the use of minimal heap (easy to fit the pathfinding algorithm). Therefore, to heap an input array is not a core operation, in order to facilitate the writing of code and enhance readability, the building of the heap algorithm from the root node to the next way, rather than from the last non-leaf node upward way. The advantages are obvious, the code is clear, there is no need to use recursion and there is no need for a large number of if else statements to find the smallest child node. As long as the child's node value is less than its parent, it can be exchanged. The disadvantages are obvious, and the number of exchanges increases to reduce efficiency.


 Public
public void BuildHeap() 
{ for (int i = 1; i < numberOfItems; i++) 
      { int bubbleIndex = i; ar node = heap[i]; while (bubbleIndex != 0) 
       { int parentIndex = (bubbleIndex-1) / D; if (node.CompareTo(heap[parentIndex]) < 0) 
           {
                    heap[bubbleIndex] = heap[parentIndex];

                    heap[parentIndex] = node;

                    bubbleIndex = parentIndex;  } else           { break;
                }
            }
        }
}


Push: adds a new element to the priority queue , throws an exception if the Add node is empty, and expands the space if there is not enough space. Finally, call the inner function Decreasekey Add a new node to the D-ary heap.


 
 
public void Push(T node) 
{
     if (node == null) throw new System.ArgumentNullException("node");

     if (numberOfItems == heap.Length) 
    {
         Expand();
     }

    DecreaseKey(node, (ushort)numberOfItems);
    numberOfItems++;
}


Decreasekey: The incoming index is the number of existing elements in the current queue. This function is private because there is no need to provide a change interface for the priority queue. Here we use an optimization technique where we don't save the nodes we want to add to the array until we find the right place in the array, which saves you from unnecessary swapping.


 
private void DecreaseKey (T node, ushort index)
{ if(index < numberOfItems)
            { if(node.CompareTo(heap[index]) > 0 )
                { throw new System.Exception("New node key greater than orginal key");
                }
            } int bubbleIndex = index; while (bubbleIndex != 0) 
        { // Parent node of the bubble node int parentIndex = (bubbleIndex-1) / D; if (node.CompareTo(heap[parentIndex]) < 0 ) { // Swap the bubble node and parent node // (we don't really need to store the bubble node until we know the final index though // so we do that after the loop instead) heap[bubbleIndex] = heap[parentIndex];
                    bubbleIndex = parentIndex;
                } else { break;
                }
            }

            heap[bubbleIndex] = node;
}




Pop: pops the priority queue T-op element, calling the intrinsic function extractmin.


 
public T Pop () 
{ return ExtractMin();
}


extractmin: Returns the current root node, updates the Numberofitems, and re-stacks . Move the last leaf node to root node, and the node floats according to the rules. The same optimization techniques are used here. Instead of saving the last leaf node to the position of the array 0, wait until you determine its final position and then save it in the array. The benefits of doing so save the number of exchanges.


 
private T ExtractMin()
{
            T returnItem = heap[0];

            numberOfItems--; if (numberOfItems == 0) return returnItem; // Last item in the heap array var swapItem = heap[numberOfItems]; int swapIndex = 0, parent;  while (true) {
                parent = swapIndex; var curSwapItem = swapItem; int pd = parent * D + 1; // If this holds, then the indices used // below are guaranteed to not throw an index out of bounds // exception since we choose the size of the array in that way if (pd <= numberOfItems) 
           {  for(int i = 0;i<D-1;i++)
                    { if (pd+i < numberOfItems && (heap[pd+i].CompareTo(curSwapItem) < 0))
                        {
                            curSwapItem = heap[pd+i];
                            swapIndex = pd+i;
                        }

                    } if (pd+D-1 < numberOfItems && (heap[pd+D-1].CompareTo(curSwapItem) < 0)) 
                    {
                        swapIndex = pd+D-1;
                    }
                } // One if the parent's children are smaller or equal, swap them // (actually we are just pretenting we swapped them, we hold the swapData // in local variable and only assign it once we know the final index) if (parent != swapIndex) {
                    heap[parent] = heap[swapIndex];
                } else { break;
                }
            } // Assign element to the final position heap[swapIndex] = swapItem; // For debugging  Validate (); return returnItem;
}


Time Complexity Analysis:


  • For priority queues implemented with the D ary Heap, if the queue has n elements, its corresponding heap height is logdn and the new element is added with a time complexity of O (LOGDN)
  • For the priority queue implemented with the D ary Heap, if the queue has n elements, its corresponding heap height is logdn, in order to select the minimum or maximum node in the D child node, the layers are continuously floating. So delete the first element of the team time complexity is (d-1) Logdn
  • For converting an array into a D ary heap, the time complexity is O (n), from the last non-leaf node upward, and the analysis idea is the same as the binary heap. For example, for a 4 ary heap with n nodes, a height of 1 subtrees has (3/4) n, a height of 2 Shiyu (3/16) n ... A subtree with a height of 1 requires O (1), and a subtree with a height of 2 requires O (2) ... The summation formula is $\sum_{k=1}^{log_{4}^{n}}{\frac{3}{4^{k}}}nk$ and the infinite series is convergent according to the ratio convergence method, so the complexity is still O (n). So what is the complexity of the top-down approach given in this paper? The answer is O ($dlog _{d}^{n}n$), the specific operation process (see next), the theoretical time complexity is higher than the use of the last non-leaf node upward way. But the actual efficiency of the difference between the actual test needs to be carried out.
  • In this paper, the BUILDHEAP algorithm, the node I layer of the nodes at most need to compare and exchange I, and the first layer node Di, thus the time to get the statistical paradigm of $\sum_{i=1}^{log_{d}^{n}}{d^{i}}i$, taking d=4 as an example $\sum_{i=1}^{ log_{4}^{n}}{4^{i}}i$. Need to find the first and Si about I expression, si= 1*4 +2*42+3*43+.....+ i*4i, then 4si=1*42+2*43+......+i*4i+1, with 4si-si to offset subtraction, learned 3si=i*4i+1-(4+42+ ... +4i). Jolly, the latter was a geometric series. So the entire expression is finally expressed as $si=\frac{4}{9}+\frac{1}{3} (I-\frac{1}{3}) 4^{i+1}$, and we know that the I value is LOGDN, substituting an O ($dlog _{d}^{n}n$).
Summarize:


By using System.Diagnostics.Stopwatch for multiple tests, the push and pop performance is good when d=4 is found, and d=4 in many cases push is better than d=2. Push can determine that performance has actually improved, pop can not determine whether it is good or bad, the results of the experiment. In the final analysis, System.Diagnostics.Stopwatch is not a precise test, there is also. NET noise.


Appendix:


Priority Queue Complete Program


Q&a:


Q:



My pathfinding algorithm wants to use C + + or Java standard library priorityqueue, both do not provide decreasekey function, the problem is that I can not update the queue element key, there is no way to relax, how to deal with?



A:



The author of the article Decreasekey is also private, not provided to priorityqueue users. Why not offer it? Because even if the pathfinding algorithm is provided, how to give the index required by Decreasekey? We know that the elements that need to be updated are in the priority queue, but index does not know that to get index you need to search (or use additional data structures to assist). Using additional data structures to help determine that index is bound to take up more memory space, using search to determine that index must consume more time, especially when there are many elements in the queue. The trick doesn't change it at all. Instead, the new copy of the node (with a new, better cost) is added to the priority queue. Because of the lower cost, the new copy of the node will be fetched before the original copy in the queue, so it will be processed earlier. The duplicate nodes you encounter are ignored directly, and there are many cases where we have found the path before we can handle the duplicate nodes. The additional burden is that there are some extra objects in the priority queue. This burden is very small and easy to implement.


Reference documents:


www.geeksforgeeks.org/k-ary-heap/



Http://en.wikipedia.org/wiki/Binary_heap



En.wikipedia.org/wiki/d-ary_heap



Welcome to comment Area Exchange, criticism, correction ~



Original article, please indicate the source, thank you ~





Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.