DSAA之PRIORITY QUEUES (一)

1. Model

  • A priority queue is a data structure that allows at least the following two operations: insert, which does the obvious thing, and delete_min, which finds, returns and removes the minimum element in the heap.
  • The insert operation is the equivalent of enqueue, and delete_min is the priority queue equivalent of the queue’s dequeue operation.

  优先级堆最被关注的两个操作就是插入和删除了,也正是这两个操作实现了优先级较高的节点先输出的特点。区别于stack的FILO,与queue的FIFO。

2. Simple Implementations

  • There are several obvious ways to implement a priority queue. We could use a simple linked list, performing insertions at the front in O ( 1 ) and traversing the list, which requires O ( n ) time, to delete the minimum. Alternatively, we could insist that the list be always kept sorted; this makes insertions expensive ( O ( n ) ) and delete_mins cheap ( O ( 1 ) ) .
  • Another way of implementing priority queues would be to use a binary search tree. This gives an O ( l o g n ) average running time for both operations.
    • Using a search tree could be overkill because it supports a host of operations that are not required.二叉搜索树的实现方式并不被采用

  上面的时间复杂度都是之前分析过的,就不再赘述了。这里只是提了两个可能的优先级堆的实现方式,比如链表或者二叉搜索树,时间复杂度显而易见。但是一般的优先级堆(完全二叉堆)还是使用下面介绍的方式二叉树,以数组的方式实现。但左倾堆,或者斜堆都是以二叉树实现。

3. Binary Heap

3.1 Structure Property

  • A heap is a binary tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right. Such a tree is known as a complete binary tree.二叉堆是完全填充的,也就是除了底层,其他节点都应该有两个子代
  • It is easy to show that a complete binary tree of height h has between 2 h and 2 h + 1 1 nodes.
  • An important observation is that because a complete binary tree is so regular, it can be represented in an array and no pointers are
    necessary.这大概是最神奇的地方,用数组可以实现二叉完全树

  • For any element in array position i, the left child is in position 2i, the right child is in the cell after the left child (2i + 1), and the parent is in position i/2 .这个性质也是编程的核心性质

    • Thus not only are pointers not required, but the operations required to traverse the tree are extremely simple and likely to be very fast on most computers. The only problem with this implementation is that an estimate of the maximum heap size is required in advance, but typically this is not a problem.

这里写图片描述
这里写图片描述
  上图二叉完全树是理论模型,下面的数组是实际实现的方法,这两者并不冲突,二叉堆结构特性,数组能够很高效的实现。当然这里数组的第一个元素为空,和之前链表有个list_header很相似,不过在笔者自己的实现中,将延续以前的做法,不需要第一个元素为空。

3.2 Heap Order Property

  • The property that allows operations to be performed quickly is the heap order property.
    • Applying this logic, we arrive at the heap order property. In a heap, for every node X, the key in the parent of X is smaller than (or equal to) the key in X, with the obvious exception of the root (which has no parent)二叉堆的每个节点都应该比自己的子代key值小,所以root就是整个2叉堆中最小的节点。
  • Analogously, we can declare a (max) heap, which enables us to efficiently find and remove the maximum element, by changing the heap order property.如果找最大的节点,只要改变一下上面的性质,让每个节点的都比子代的节点的key值大。
  • Thus, a priority queue can be used to find either a minimum or a maximum, but this needs to be decided ahead of time.

3.3 Basic Heap Operations

3.3.1 Insert

  前面都没有过多的论述,笔者觉得英文比较清晰,这里在进行insert尝试前,需要先理解插入操作的规则:

  • To insert an element x into the heap, we create a hole in the next available location, since otherwise the tree will not be complete. 保证不破坏结构特性
  • If x can be placed in the hole without violating heap order, then we do so and are done. Otherwise we slide the element that is in the hole’s parent node into the hole, thus bubbling the hole up toward the root. We continue this process until x can be placed in the hole.保证不破坏顺序特性
  • This general strategy is known as a percolate up; the new element is percolated up the heap until the correct location is found.这种insert的策略又称为向上渗透

3.3.2 Delete_min

  • Since the heap now becomes one smaller, it follows that the last element x in the heap must move somewhere in the heap. If x can be placed in the hole, then we are done.这种情况下,二叉堆高度就是1!
  • This is unlikely, so we slide the smaller of the hole’s children into the hole, thus pushing the hole down one level. We repeat this step until x can be placed in the hole. Thus, our action is to place x in its correct spot along a path from the root containing minimum children.与前面的向上渗透策略相同,在这里称为向下渗透:可以根据空缺位置向下或者向上的走向来区分
  • A frequent implementation error in heaps occurs when there are an even number of elements in the heap, and the one node that has only one child is encountered. You must make sure not to assume that there are always two children, so this usually involves an extra test.在二叉堆的倒数第二层,并不能保证该层的节点就一定具有两个子代!
  • The worst-case running time for this operation is O ( l o g n ) . On average, the element that is placed at the root is percolated almost to the bottom of the heap (which is the level it came from), so the average running time is O ( l o g n ) .

  对于删除操作的时间复杂度稍微进行一些分析,最坏的向下渗透的操作,要进行操作二叉堆的次数为 f 次,假设该树的节点数为 N ,其为 2 0 + 2 1 + . . . . + 2 f a = N ,由等比公式可得: f = l o g ( N + 1 + a ) 1 = l o g ( N + 1 ) ,所以删除操作的时间复杂度为 O ( ( l o g ( N ) 1 ) 1 ) = O ( l o g N ) 。以上的化简过程都假设 N >> a > 1 a 为最后一层剩余没有填满的空间数。

3.3.3 Decrease_key

The decrease_key(x, , H) operation lowers the value of the key at position x by a positive amount . Since this might violate the heap order, it must be fixed by a percolate up. This operation could be useful to system administrators: they can make their programs run with highest priority.

  思考这里的时候稍微要绕个弯,首先降低key值相当于增加该节点的优先级,此时可能破坏二叉堆的顺序特性,就需要向上渗透。

3.3.4 Increase_key

The increase_key(x, , H) operation increases the value of the key at position x by a positive amount . This is done with a percolate down. Many schedulers automatically drop the priority of a process that is consuming excessive CPU time.

  比较简单的理解就是值越大,越往下沉,值越小,越往上漂。这里还是要区别插入和删除的时候的上下渗透。

3.3.5 Delete

The delete(x, H) operation removes the node at position x from the heap. This is done by first performing decrease_key(x, , H) and then performing delete_min (H). When a process is terminated by a user (instead of finishing normally), it must be removed from the priority queue.删除操作就是decrease_key先让该节点向上渗透到root,然后再delete_min。

3.3.6 Build_heap

  • The build_heap(H) operation takes as input n keys and places them into an empty heap.
    • Obviously, this can be done with n successive inserts. Since each insert will take O ( 1 ) average and O ( l o g n ) worst-case time, the total running time of this algorithm would be O ( n ) average but O ( n l o g n ) worst-case. Since this is a special instruction and there are no other operations intervening, and we already know that the instruction can be performed in linear average time, it is reasonable to expect that with reasonable care a linear time bound can be guaranteed.

  在这点时间复杂度上,中文DSAA的翻译如下,笔者认为该翻译是错的:
这里写图片描述
  从英文本来的意思及对时间复杂度基本概念的理解,笔者认为如果采用循环调用insert方式的Build_heap,时间复杂度为 O ( n l o g n ) ,然后DSAA指出来另外一种Build_heap的实现,其复杂度为 O ( n )

  • The general algorithm is to place the n keys into the tree in any order, maintaining the structure property. Then, if percolate_down(i) percolates down from node i, perform the algorithm in Figure 6.14 to create a heap-ordered tree.
  • one to find the smaller child and one to compare the smaller child with the node.

  也就是分为两步,首先满足结构特性,将二叉堆插入满。再考虑满足顺序特性,此时使用percolate_down函数,DSAA并没有对该函数进行详细论述。只是论证了使用percolate_downBuild_heap函数的时间复杂度为 O ( n ) 。具体这方面的细节就不考究了,下一篇笔者就实现时间复杂度分别为 O ( l o g n ) , O ( l o g n ) , O ( n l o g n ) 的插入、删除和构建。

猜你喜欢

转载自blog.csdn.net/lovestackover/article/details/80177841