Python implements a two fork heap method

Source: Internet
Author: User
The binary heap is a special heap, the binary heap is a completely two-dollar Tree (binary tree) or is an approximate complete two-yuan tree (binary tree). There are two types of binary heaps: the maximum heap and the smallest heap. Maximum heap: The key value of the parent node is always greater than or equal to the key value of any child node; minimum heap: The key value of the parent node is always less than or equal to the key value of any one of the child nodes.

Implementation of two fork heap for priority queue

In the previous chapters we learned about the FIFO data structure of FIFO (): Queue ( Queue ). A variant of the queue is called a "priority queue" ( Priority Queue ). The priority queue's out-of-line ( Dequeue ) operation is the same as the queue, from the team's first team. But within the priority queue, the order of the elements is determined by the priority: the high-priority elements rank in the first line, and the lower-priority elements are behind. In this way, the queued () operation of the priority queue Enqueue is more complex and requires that the elements be prioritized to the front of the queue as much as possible. We will find that the priority queue in the graph algorithm for the next sectionto is a useful data structure.

It is natural for us to think of sorting algorithms and queuing methods to implement the priority queue. However, the time complexity of inserting an element in the list is the O(n) time complexity of sorting the lists O(nlogn) . We can use other methods to reduce the complexity of time. A classic way to implement a priority queue is to use a binary heap ( Binary Heap ). The binary stack can keep the queue and the team complexity of the priority queues O(logn) .

The interesting thing about binary stacks is that they are logically structured like binary trees, but are implemented using non-nested lists. There are two types of binary piles: The key is always the smallest row in the head of the team is called the "minimum Heap ( min heap )", conversely, the key value is always the largest row in the team first known as the "Maximum heap ( max heap )." In this section we use the minimum heap.

Operation of binary piles

The basic operation of the binary heap is defined as follows:

    1. BinaryHeap(): Create an empty two-fork heap object

    2. insert(k): Adding new elements to the heap

    3. findMin(): Returns the smallest item in the heap, and the smallest item remains in the heap

    4. delMin(): Returns the smallest item in the heap while removing from the heap

    5. isEmpty(): Returns whether the heap is empty

    6. size(): Returns the number of nodes in the heap

    7. buildHeap(list): Create a new heap from a list of included nodes

The code shown below is an example of a two-fork heap. You can see that regardless of the order in which we add elements to the heap, each time we remove the smallest element. We're going to implement this process next.


From pythonds.trees.binheap Import BINHEAPBH = Binheap () Bh.insert (5) Bh.insert (7) Bh.insert (3) Bh.insert (one) print ( Bh.delmin ()) print (Bh.delmin ()) print (Bh.delmin ()) print (Bh.delmin ())

To better implement the heap, we use a binary tree. We must always maintain the "balance" of the binary tree so that the operation remains at a logarithmic magnitude. The number of child nodes of the left and right subtree of the balanced two-fork tree node is the same. In the implementation of the heap, we use the structure of the "complete binary tree" to achieve the approximate "balance". A complete binary tree, which means that each internal node tree reaches its maximum value, except that the last layer can only be missing several nodes on the right. Figure 1 shows a complete binary tree.

Figure 1: Complete binary tree

The interesting thing is that we can implement a complete tree with a single list. We don't need to use nodes, references or nested lists. Because for a complete binary tree, if the node is labeled p in the list, the left child node is labeled 2p and the right node is 2p+1. When we want to find the parent node of any node, we can directly use the Python divide. If the node is labeled in the list n , then the parent node n//2 is labeled. Figure 2 shows a complete binary tree and a list representation of the tree. Note the relationship between the parent node and the child node 2p and 2p+1. The Complete tree list notation combines the characteristics of a complete binary tree, enabling us to efficiently traverse a complete tree using simple mathematical methods. This also enables us to efficiently implement two fork piles.

The nature of the heap order

The way we store elements in a heap depends on the order of the heap. The so-called heap order, refers to any node in the heap x, whose parent node P's key value is less than or equal to the key value of X. Figure 2 shows a complete binary tree with a heap order nature.

Figure 2: Full tree and its list notation

Implementation of Binary stack operation

Next we'll construct a two-fork heap. Because a list can be used to hold the heap's data, the constructor only needs to initialize a list and one currentSize to represent the current size of the heap. Listing 1 shows the Python code that constructs a two-fork heap. Note that the two-fork heap is heaplist not used, but we still keep it for the sake of the code that can be conveniently used to divide it.

Listing 1


Class Binheap:  def init (self):    self.heaplist = [0]    self.currentsize = 0

The next thing we want to do is insert approach. First, to satisfy the nature of the "full binary tree", the new key value should be added to the end of the list. However, the new key value is simply added at the end of the list, and the heap order is obviously not satisfied. But we can re-satisfy the heap order by comparing the parent node and the newly added element. If the newly added element is smaller than the parent node, you can swap the location with the parent node. Figure 3 shows a series of switching operations to "float" the newly added element to the correct position.

Figure 3: The new node "floats" to its correct position

When we let an element "float", we want to guarantee the heap order between the new node and the parent node and the other sibling nodes. Of course, if the new node is very small, we still need to swap it to another layer. In fact, we need to keep swapping until we reach the top of the tree. Listing 2 shows the "float" method, which "floats" a new node to its correct position to satisfy the heap order. This is a good representation of the importance of headlist element 0 that we did not use in the past. This simply divides the index of the current node by 2, and we can calculate the parent node of any node.

In Listing 3, we can already write insert the code of the method. inserta large part of the work is percUp done by the function. When the tree adds a new node, the call can place the percUp new node in the correct location.

Listing 2


def percup (self,i): While  I//2 > 0:   if self.heaplist[i] < Self.heaplist[i//2]:     tmp = Self.heaplist[i 2]     self.heaplist[i//2] = Self.heaplist[i]     self.heaplist[i] = tmp   i = I//2

Listing 3


def insert (self,k):  self.heapList.append (k)  self.currentsize = self.currentsize + 1  self.percup ( Self.currentsize)

We have written the insert method, then look at the delMin method. Heap order requires that the root node be the smallest element in the tree, so it is easy to find the smallest item. The hard part is how to keep the heap structure and the heap order after removing the elements of the root node, and we can go in two steps. First, replace the root node with the last node. Removing the last node preserves the nature of the heap structure. Such a simple substitution would still destroy the heap order. Then the second step is to "sink" the new node to restore the heap order. Figure 4 shows a series of swap operations to "sink" the new node to the correct location.

Figure 4: The replacement root node sinks

In order to maintain the heap order, we need to "sink" the new root node along a path until it is smaller than the two child nodes. When you select a sinking path, if the new root node is larger than the child node, select the smaller child nodes to swap with. Listing 4 shows the code required for the new node sinking percDown and the minChild method.

Listing 4


def percdown (self,i): While  (i * 2) <= self.currentsize:    mc = Self.minchild (i)    if self.heaplist[i] > SE LF.HEAPLIST[MC]:      tmp = self.heaplist[i]      self.heaplist[i] = SELF.HEAPLIST[MC]      SELF.HEAPLIST[MC] = tmp    i = mcdef minchild (self,i):  If I * 2 + 1 > self.currentsize:    return i * 2  else:    if self.heaplist[ I*2] < self.heaplist[i*2+1]:      return i * 2    else:      return i * 2 + 1

The code for the operation is shown in Listing 5 delMin . You can see that the more troublesome place is handled by an auxiliary function, that is percDown .

Listing 5


def delmin (self):  retval = self.heaplist[1]  self.heaplist[1] = self.heaplist[self.currentsize]  Self.currentsize = self.currentsize-1  self.heapList.pop ()  self.percdown (1)  return retval

The last part about the binary heap is finding a way to generate a "heap" from the unordered list. The first thing we think about is that each element in the unordered list is inserted sequentially into the heap. For a well-ordered list, we can use a binary search to find a suitable location, and then insert the key value into the heap at the next location, with a time complexity of O(logn) . In addition, inserting an element into the list requires moving some other elements of the list, freeing the new node for a location and time complexity O(n) . So insert the total cost of using the method is O(nlogn) . We can actually pile up the entire list and control the total overhead O(n) . Listing 6 shows a heap of operations.

Listing 6


def buildheap (self,alist):  i = len (alist)//2  self.currentsize = Len (alist)  self.heaplist = [0] + alist[:]
  while (i > 0):    self.percdown (i)    i = i-1

Figure 5: Generating a binary heap with the list [9, 6, 5, 2, 3]

Figure 5 shows the swap operations that are made by using the buildHeap method to move the nodes in the first tree [ 9, 6, 5, 2, 3] to the correct location. Although we start from the middle of the tree and then go back to the root node, the percDown method guarantees that the maximum child node is always "sinking". Because the heap is a fully binary tree, any nodes in the middle are leaf nodes, so there are no child nodes. Note that when i=1 we start sinking from the root node, we need to do a lot of switching. As you can see, in Figure 5, the rightmost two trees, first 9 moved from the root node position, moved to the next level, percDown further check its child node at this time, to ensure that it falls down to no longer, that is, down to the correct position. Then the exchange of the second exchange, 9 and 3. Since 9 has moved to the lowest level of the tree, it cannot be exchanged further. It is helpful to compare a series of exchanges between the list notation and the tree notation shown in Figure 5.


i = 2 [0, 9, 5, 6, 2, 3]i = 1 [0, 9, 2, 6, 5, 3]i = 0 [0, 2, 3, 6, 5, 9]

The code shown below is an implementation of the complete binary heap.


def insert (self,k):   self.heapList.append (k)   self.currentsize = self.currentsize + 1   self.percup ( self.currentsize)  def percdown (self,i): While   (i * 2) <= self.currentsize:     mc = Self.minchild (i)     if Self.heaplist[i] > SELF.HEAPLIST[MC]:       tmp = self.heaplist[i]       self.heaplist[i] = SELF.HEAPLIST[MC]       SELF.HEAPLIST[MC] = tmp     i = MC  def minchild (self,i):   If I * 2 + 1 > self.currentsize:     return i * 2< C14/>else:     if self.heaplist[i*2] < self.heaplist[i*2+1]:       return i * 2     else:       return i * 2 + 1  def delmin (self):   retval = self.heaplist[1]   self.heaplist[1] = self.heaplist[self.currentsize]   Self.currentsize = self.currentsize-1

The ability to O(n) generate a two-fork heap under the overhead looks a bit odd, proving beyond the scope of the book. However, it is important to understand that the O(n) cost of spending can be piled up because the logn factor is based on the height of the tree. And for buildHeap Many of the operations, the height of the tree is logn smaller.

Related Article

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.