ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

cpp语言

2021-11-25 18:58:38  阅读:169  来源: 互联网

标签:优先级 语言 img 复杂度 二叉 cpp 节点 png


# 优先级队列

优先级队列是根据优先级决定,队列就是先到的元素优先级最高,而栈就是后到的优先级最高。优先级队列维护的是一个偏序关系,优先级的词条(entry)是数据项,而关键码是优先级。

![优先级队列接口](./img/优先级队列接口.png)

```cpp
template<typename T> 
struct PQ {
  virtual void insert(T ) =0; // 插入 O(logn)

  virtual T getMax() =0;  // 获取优先级别最高的词条  O(1)
  virtual T delMax() =0;  // 删除优先级最高的词条    O(logn)
};
```

## 完全二叉堆

优先级队列高效的原因主要原因在于其不需要维护一个全序关系,仅仅需要维护一偏序关系。完全二叉堆是优先级队列的一种实现。 完全二叉堆具有两个条件

+ 结构性:形式上是完全二叉树
+ 堆序性:堆顶以外的每个节点都不会大于他的父节点-->大顶堆    

### 底层表示

完全二叉堆的底层实现方式是一个`vector`。底层借助`vector`使用`O(n)`的紧凑空间表示:完全二叉树从左到右层的次遍历次序和`vector`的元素内容一一对应。

![底层表示](img/完全二叉堆的底层表示.png)

由次,完全二叉堆中的任意节点v和其字节点以及父节点之间的位置关系,如下:

```
  将节点v的rank记作r(v),
  1) 如果有左孩子,则 r(lc) = 2*r(lc) +1; 
  2) 如果有右孩子,则 r(rc) = 2*r(lc) +2; 
  3) 如果有父节点,则 r(par)= (r(v)-1)/2 

  如果节点v是根节点
  r(v)   = 0
  r(lc)  = 1
  r(rc)  = 2
  r(par) = -1
```

因此,介于完全二叉堆的组成,由优先级队列和向量组成,因此可以如下定义:

```cpp
template<typename T> 
class PO_ComplHeap : public PQ<T>, public vector<T> { /**/ } ;
```

![类模板](./img/完全二叉堆接口.png)

可以把`PQ`当作接口,必须要实现的接口,其本质上还是向量vector。

### 堆的操作

堆的操作分为获取优先级最大者、插入和删除。

+ `getMax()` 即使堆顶元素,也就是`vector`的首元素,因此可以在`O(1)`时间复杂度内返回。

+ `insert()`:

    + 插入到底层`vector`的末尾,这个操作可以在`O(1)`的时间复杂度内完成

    + 将插入的节点 **上滤** 到合适的位置,以维护堆序性。

        ![插入模型](img/完全二叉堆的插入.png)

        **上滤**:如果插入的节点e和父节点p之间的有序性违反了堆序性,那么就互换二者位置,这`vector`的底层实现就是互换连个元素位置,`O(1)`的时间复杂度。如果互换之后还是违反,就继续重复这个上滤操作。直到满足堆序性。如上图,至多上滤到堆顶,也就是在最坏的情况下整个插入的过程需要`O(logn)`的时间复杂度。

        ![实例](./img/完全二叉堆插入实例.png)

        ```cpp
          template<typenmae T> 
          Rank PQ_CompleteHeap<T>::percolateUp(Rank i) { 
              //只要有父节点存在,最多到根节点
              while(parentValid(i)) { 
                Rank p = parent(i);
                if(ele_[i] < ele_[p]) break; // 不再逆序,上滤完成
                std::swap(ele_[i], ele_[p]);
                i = p;  // 从父节点继续考察是否需要上滤
              }
              return i;
          }
        ```

        上滤操作的时间复杂度最坏情况下是`O(logn)`.

+ `deleteMax()`:

    + 交换堆顶元素和堆的一个元素,就是交换`ele_`的首尾元素,删除尾部的元素

    + 将新的堆顶元素下滤,以维护堆序性

        ![完全二叉堆删除](./img/完全二叉堆删除.png)

        **下滤**:新的堆顶点e不满足堆序性,那么就需要和他的子节点中的较大者交换位置,如果还是与子节点违反堆序性,那么继续重复上面操作。这里与上滤不一样的地方是,上滤在交换位置时需要可能需要比较两次,而上滤只是需要比较一次。完全二叉树的结构,保证了下滤操作`O(logn)`

        ![完全二叉堆删除案例](./img/完全二叉堆删除实例.png)

          ```cpp
          // 对前n个词条中的第i个进行下滤
          template<typename T> 
          Rank PQ_CompleteHeap<T>::percolateDown(rank n, Rank i) { 
            Rank j;
            while(i != (j=properParent(ele_, n, i))) { 
              std::swap(ele_[i], ele_[j]); 
              i = j; 
            }
            retutn i;
          } 
          ```

          其中 `properParent(i)` 是比较节点`i`及其子节点的最大值,如果`j!=i`, 说明仍然需要下滤。退出循环条件:

          + 到最底层
          + `i == j`

          ![宏定义](./img/完全二叉堆宏定义.png)

+ 建堆:
    即将原始的`n`个数据变成一个完成二叉堆,那么最好的情况下也是需要`O(n)`的时间复杂度。建堆方法有两种:

    + 蛮力法:自上而下的上滤   
        这是利用插入排序的思想,每次都从原始数据里选取一个数据,然后调用`insert`接口,让数据自动上滤到合适的位置,这个算法的时间复杂度是`O(nlogn)`。

        这个算法失败的原因在于:使用的是深度和。每次插入一个新的节点,都是先放到最后一层,然后不断的上滤,最坏的情况需要上滤`O(logn)`。因此算法总体复杂度就是`O(nlogn)`。

        实际上,优先级队列只是维护一个偏序关系,但是这个时间复杂度足以将整个数据进行全排序,显然代价过于高昂。

        ![建堆_蛮力法](./img/完全二叉堆_建堆_蛮力法.png)

    + `Floyd算法`:自下而上的下滤  
        `Floyd`算法,是从底层开始:对于堆 ***`H0,H1`*** 以及给定的顶点`p` ,合并一个更高的堆。 这个操作类似于 `deleteMax`, 只是需要将`p`进行下滤操作即可。最初 ***`H0,H1`*** 就是叶节点,经过不断地合并,最终变成一个完全二叉堆。

          ![Floyd](./img/完全二叉堆_建堆_Floyd.png)

        ```cpp
          template<typename T> 
          void PQ_CompleteHeap()::heapify(Rank n) { 
            for(int i = lastInternal(n) ; InHeap(n, i); --i) 
                percolateDown(n, i);
          }
        ```

        算法复杂度是`O(n)`:  

          ![Floyd](./img/完全二叉堆_建堆_Floyd_复杂度.png)


## 左式堆

### 堆合并

为了提高堆的合并操作效率。 左式堆,可以在满足堆序性的条件下,附加新的条件,使得在堆合并的过程中,只需要调整少部分节点--> 时间复杂度O(logn)

对于堆,堆序性才是其本质要求,而结构性,即完全二叉树的结构并不是必须要的

空节点路径长度 Null Path Length: npl,npl(x) =0, npl(X) = 1+std::min();

npl(x) = x到外部节点的最近距离
npl(x) = 以x为根节点的最大满子树的高度

标签:优先级,语言,img,复杂度,二叉,cpp,节点,png
来源: https://blog.csdn.net/weixin_54529579/article/details/121544627

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有