Python code example for implementing heap and index heap and python index heap

Source: Internet
Author: User

Python code example for implementing heap and index heap and python index heap

A heap is a Complete Binary Tree. A heap is divided into a large root heap and a small root heap. A large root Heap has a parent node greater than the left and right subnodes, and the left and right Subtrees also meet the full Binary Tree of this type. Opposite to the small root heap. You can use the heap to implement priority queues.

Because it is a complete binary tree, you can use an array to represent the heap. The index starts from 0 [0: length-1]. The left and right subnodes of node I are 2i + 1 and 2i + 2 respectively. The last non-leaf node of the tree whose length is length // 2-1. The parent node of the current node I is (I-1) // 2. // Indicates downgrading.

Example of a Large-root heap. When you insert or delete a heap, you need to adjust the heap structure to ensure that its features are not damaged. There are two types of adjustment: one is to sink a small number from top to bottom. One is to move up from the bottom to the upper limit.

The specific implementation is as follows:

First, write several magic methods. Including constructors, you can directly call len to return the data array length function, a function that prints data content

 def __init__(self, data=[]):    self.data = data    self.construct_heap() def __len__(self):    return len(self.data) def __str__(self):    return str(self.data)

Define a swap function to conveniently exchange the values of two indexes in the array.

  def swap(self, i, j):    self.data[i], self.data[j] = self.data[j], self.data[i]

Define the float_up method so that large numbers in the heap can be floated. If the current node is not the root node, and the data size of the current node is greater than that of the parent node, the node goes up.

 def float_up(self, i):    while i > 0 and self.data[i] > self.data[(i - 1) // 2]:      self.swap(i, (i - 1) // 2)      i = (i - 1) // 2

Define the sink_down method to sink the small and medium numbers in the heap. When the current node is not a leaf node, if it is smaller than the data of the Left or Right child, it is a big change with the left and right children.

def sink_down(self, i):    while i < len(self) // 2:      l, r = 2 * i + 1, 2 * i + 2      if r < len(self) and self.data[l] < self.data[r]:        l = r      if self.data[i] < self.data[l]:        self.swap(i, l)      i = l

Implement the append method to dynamically add data. Add data at the end of the data array, and then move the data up.

  def append(self, data):    self.data.append(data)    self.float_up(len(self) - 1)

Implement the pop_left method to take the largest element in the heap, that is, the first element in the priority queue. Change the positions of the first element and the last element in the array, delete the last element, and sink the first element to the appropriate position.

  def pop_left(self):    self.swap(0, len(self) - 1)    r = self.data.pop()    self.sink_down(0)    return r

If you want to input data parameters to the constructor During Heap initialization, you need to build the entire heap at one time, instead of adding the data parameters one by one. The implementation is also very simple. From the last non-leaf node, execute the sink_down operation one by one.

  def construct_heap(self):    for i in range(len(self) // 2 - 1, -1, -1):      self.sink_down(i)

Such a basic heap code is compiled.

However, if we want to dynamically change the data, the current heap cannot meet our needs, because the index cannot always identify the same data, because the heap structure is constantly adjusted. We need to use the index heap.

In the index heap, we do not directly store data in the heap, but instead store data indexes in the heap.

If the input data arr is 45 20 12 5 35. Then arr [0] always points to 45, and arr [1] always points to 20, because we actually adjust the index array in the adjustment Heap Structure, without changing the array that actually stores data.

Therefore, we need to adjust our code. First, add an index array to the constructor. The subscript starts from 0 and corresponds to the subscript of the array where data is stored.

  def __init__(self, data=[]):    self.data = data    self.index_arr = list(range(len(self.data)))    self.construct_heap()

Then modify the magic function that returns the heap length.

  def __len__(self):    return len(self.index_arr)

Adjust the previously defined swap method. The original method is to exchange data directly, and now the index is exchanged.

 def swap(self, i, j):    self.index_arr[i], self.index_arr[j] = self.index_arr[j], self.index_arr[i]

Adjust the position of float_up and sink_down

  def float_up(self, i):    while i > 0 and self.data[self.index_arr[i]] > self.data[self.index_arr[(i - 1) // 2]]:      self.swap(i, (i - 1) // 2)      i = (i - 1) // 2        def sink_down(self, i):    while i < len(self) // 2:      l, r = 2 * i + 1, 2 * i + 2      if r < len(self) and self.data[self.index_arr[l]] < self.data[self.index_arr[r]]:        l = r      if self.data[self.index_arr[i]] < self.data[self.index_arr[l]]:        self.swap(i, l)      i = l

When append data is used, you must update index_arr accordingly.

  def append(self, data):    self.data.append(data)    self.index_arr.append(len(self))    self.float_up(len(self) - 1)

When removing data, we have mentioned that the array for storing data is stored in the append order. The operation is to adjust the order of index_arr.

If data_arr is 42 30 74 60, the corresponding index_arr should be 2 3 0 1

At this time, when we popleft out the maximum element, the 74 in data_arr is removed and changed to 42 30 60. The maximum index in the array is changed from 3 to 2, if the index array still uses the 3 index for index 30, index overflow will occur. The index of 74 is 2. We need to reduce the number of indexes after 2 by 1.

In summary, When deleting elements, we used to swap the first and last elements in data_arr, delete the end elements, and then perform the sink_down operation on the header elements. Now we need to replace the first and last elements in the index array, and then delete the end elements of the index array. At this time, we have not processed the data_arr that stores data. Therefore, the remaining elements of the index array still correspond to those of data_arr. After the sink_down operation is complete, delete the corresponding position element of data_arr. At last, the value of index_arr is greater than the element value in the original index_arr header by one.

  def pop_left(self):    self.swap(0, len(self) - 1)    r = self.index_arr.pop()    self.sink_down(0)    self.data.pop(r)    for i, index in enumerate(self.index_arr):      if index > r:        self.index_arr[i] -= 1    return r

The index heap adds an update operation to update the data in the index heap at any time. When updating data, update the data at the corresponding index in data_arr. Then, in index_arr, locate the index location of the newly updated data in data_arr, as with deleting, A traversal is required. After finding this position, you must perform a float_up operation and then a sink_down operation because the relationship with the front and back elements cannot be determined.

  def update(self, i, data):    self.data[i] = data    for index_index, index in enumerate(self.index_arr):      if index == i:        target = index_index    self.float_up(target)    self.sink_down(target)

It can be seen that this index heap is relatively fast when inserting elements, but when deleting elements and updating elements, in order to find the corresponding location index, it is traversed once, this is a very time-consuming operation. In order to quickly find the index value of index_arr which is the corresponding index value of data_arr to be updated, we open up a new array to_index to index index_arr.

For example, for an array of 75 54 65 90

In this case, its index_arr is 3 0 2 1. When you want to update the data [3], that is, the 90 element, You need to traverse the index_arr to find the 3 position, which is 0. We need to create a to_index. The elements in to_index [3] are 0.

Index_arr stores the following elements: 1 3 2 0.

First, change the swap array. When the index_arr element is exchanged, the index_arr index stored in to_index is also exchanged.

 def swap(self, i, j):    self.index_arr[i], self.index_arr[j] = self.index_arr[j], self.index_arr[i]    self.to_index[self.index_arr[i]], self.to_index[self.index_arr[j]] = self.to_index[self.index_arr[j]], \                                       self.to_index[self.index_arr[i]]

Then, in update, when we want to update an element whose position is I, we do not need to traverse it to find the index of this element in index_arr, instead, you can directly access the corresponding indexes in index_arr by accessing index_arr [I ].

  def update(self, i, data):    self.data[i] = data    target = self.to_index[i]    self.float_up(target)    self.sink_down(target)

Finally, change the corresponding code in pop_left. In this case, we need to maintain three Arrays: data_arr, index_arr, and to_index.

The first element of index_arr is first exchanged, and the elements at the end of pop are stored in I. Then, the Header element sink_down to the corresponding position, and then pop out the elements at data_arr index I. Then pop out the element whose index is I in to_index, and then adjust the index overflow element in index_arr.

  def pop_left(self):    self.swap(0, len(self) - 1)    r = self.index_arr.pop()    self.sink_down(0)    self.data.pop(r)    self.to_index.pop(r)    for i in range(r, len(self)):      self.index_arr[self.to_index[i]] -= 1    return r

The above is the python implementation method and the index heap method. I hope it will be helpful for everyone's learning, and I hope you can support the house of helping customers more.

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.