堆的实现【C++】

打印 上一主题 下一主题

主题 1025|帖子 1025|积分 3075

概念

介绍堆之前得说一下二叉树,由于堆的逻辑布局是二叉树,二叉树的树的子集,树只有一个根节点,向下衍生出了很多节点,而且这个节点之间相互没有连接,除非是父子节点,如果有连接了那就是图;如果拥有很多独立不连接的树,如许的数据布局称之为丛林。
如果对树作进一步的限定:每一个节点(包括根节点)最多只有两个子树,那么如许的树叫做二叉树,比如:

这种也是:

二叉树呢又可以分为完全二叉树和满二叉树:
完全二叉树就是每一个节点都有两个子树(子节点),例如:

满二叉树就是除了最后一层,也就是最底下的一层,其他的每层都是完全二叉树,而且最后一层的节点得从左到右存在,下面这种就属于满二叉树:

但是这种就不属于满二叉树:

堆的逻辑数据布局就是使用满二叉树实现的,为什么说是逻辑呢?这是由于我们在实际实现的时候使用的是数组来实现的,原因如下:
如果我们想使用数组来存储下面这个二叉树:

如许对吗?像如许写的话,岂不是没有二叉树的特点了,比如怎么判定左右子树呢?以是我们得从上到下,从左到右的添加进数组。

例如:

如许就可以轻松知道左右子树从而通过数组构建出二叉树了,但是这个数组的中央浪费了不少空间,怎样避免呢?
那就是使用满二叉树。
数组中的恣意一个节点怎么能够得到他的左右子节点和父节点呢?可以通过下面的几个公式:
如果想知道数组arr里面的节点i的左右子节点和父节点:


  • 父节点=arr[(i-1)/2]
  • 左节点=arr[i*2+1];
  • 右节点=arr[i*2+2];
概念说完了,我们接下来可以用这种数据布局来构建一个大堆和小堆,大堆的特点是父节点肯定大于子节点,但是左右节点没有讲求,小堆则相反。
使用巨细堆可以实现topk题目,也就是求出一堆数据中第几大(小)的数是哪一个。也可以实现堆排序。
实现

先实现一个大堆:

然后就得插入数据:

每次插入数据都是插入到数组的最后面,也就是二叉树的最下一层的最右边,此时需要维护大堆的规则,也就是父节点的值大于子节点的值,由于每次插入时都是在最下面,以是如果不符合规则,当前插入的值大于父节点,我们就使用adjustUp函数举行向上调整。如下:
  1. void Heap::adjustUp()
  2. {
  3.         int curIndex = _heap.size() - 1;
  4.         while (curIndex > 0)
  5.         {
  6.                 int parentIndex = (curIndex - 1) / 2;
  7.                 if (_heap[parentIndex] < _heap[curIndex])
  8.                 {
  9.             std::swap(_heap[parentIndex], _heap[curIndex]);
  10.             curIndex = parentIndex;
  11.                 }
  12.                 else
  13.                 {
  14.                         break;
  15.                 }
  16.         }
  17. }
复制代码
写一下测试跑跑看吧:
  1. #include "heap.h"
  2. int main() {
  3.     Heap heap;
  4.     heap.insert(1);
  5.     heap.insert(2);
  6.     heap.insert(3);
  7. }
复制代码
喔吼!拿不到数组的值,我们要得到堆顶的值,那就写一个吧:
  1. int Heap::top()
  2. {
  3.         if (_heap.size() > 0)
  4.         {
  5.                 return _heap[0];
  6.         }
  7.       
  8. }
复制代码
运行得到:

可以看到堆顶的数据3,数组下标0处的数据是3,可以我们在插入的时候,0处一开始插入的是1,但是结果是3,这就说明没有题目。
如果要实现topk题目,我们需要每次都将堆顶的元素给删除了,如果求第3大的数,我们就删除两次,然后此时堆顶的元素就是第3大的数了下面我们来实现删除功能:
在数组中如果直接删除下标0处的元素,在举行调整,如许会需要变动整个数组,效率低下;因此,我们选择将下标0处的元素与最后一个元素交换,然后直接删除最后一个元素,在举行向下调整即可:
  1. void Heap::pop()
  2. {
  3.         if (_heap.size() > 0)
  4.         {
  5.                 std::swap(_heap[0], _heap[_heap.size() - 1]);
  6.         _heap.pop_back();
  7.                 adjustDown();
  8.         }
  9. }
  10. void Heap::adjustDown()
  11. {
  12.         int size = _heap.size();
  13.         int curIndex = 0, leftIndex = 1;
  14.         while (leftIndex < size)
  15.         {
  16.                 if (leftIndex + 1 < size && _heap[leftIndex] < _heap[leftIndex + 1])
  17.                 {
  18.                         leftIndex++;
  19.                 }
  20.                 if (_heap[curIndex] < _heap[leftIndex])
  21.                 {
  22.             std::swap(_heap[curIndex], _heap[leftIndex]);
  23.                         curIndex = leftIndex;
  24.                         leftIndex = curIndex * 2 + 1;
  25.                 }
  26.                 else
  27.                 {
  28.                         break;
  29.                 }
  30.         }
  31.        
  32. }
复制代码
在adjustDown这个函数中,使用leftIndex + 1 < size && _heap[leftIndex] < _heap[leftIndex + 1]来判定左右节点哪一个更大一些,然后用较大值和父节点的值举行比较,如果父节点的值小于子节点的值,那么就要向下调整,也就是需要交换,之后更新当前的父节点和左节点,再次举行判定。
下面举行测试:

结果也没有题目。
然后我们根据这个举行堆排序的编写:
测试代码如下:
  1. int main() {
  2.     srand((unsigned int)time(NULL));
  3.     std::vector<int> v;
  4.     for (int i = 0; i < 100; i++)
  5.     {
  6.         v.push_back(rand()%100);
  7.     }
  8.     Heap heap;
  9.     heap.sort(v);
  10.     for (auto& e : v)
  11.     {
  12.         std::cout << e << " ";
  13.     }
  14. }
复制代码

结果也没有题目。
完整代码

  1. //heap.h
  2. #pragma once
  3. #include <vector>
  4. #include <iostream>
  5. #include <cassert>
  6. class Heap {
  7. public:
  8.         Heap();
  9.         ~Heap();
  10.     void insert(int value);
  11.         void adjustUp();
  12.         int top();
  13.         void pop();
  14.         void adjustDown();
  15.         void sort(std::vector<int>& v);
  16. private:
  17.         std::vector<int> _heap;
  18. };
复制代码
  1. //heap.cpp
  2. #include "heap.h"
  3. Heap::Heap()
  4. {
  5. }
  6. Heap::~Heap()
  7. {
  8. }
  9. void Heap::insert(int value)
  10. {
  11.         _heap.push_back(value);
  12.         adjustUp();
  13. }
  14. void Heap::adjustUp()
  15. {
  16.         int curIndex = _heap.size() - 1;
  17.         while (curIndex > 0)
  18.         {
  19.                 int parentIndex = (curIndex - 1) / 2;
  20.                 if (_heap[parentIndex] < _heap[curIndex])
  21.                 {
  22.                         std::swap(_heap[parentIndex], _heap[curIndex]);
  23.                         curIndex = parentIndex;
  24.                 }
  25.                 else
  26.                 {
  27.                         break;
  28.                 }
  29.         }
  30. }
  31. int Heap::top()
  32. {
  33.         if (_heap.size() > 0)
  34.         {
  35.                 return _heap[0];
  36.         }
  37. }
  38. void Heap::pop()
  39. {
  40.         if (_heap.size() > 0)
  41.         {
  42.                 std::swap(_heap[0], _heap[_heap.size() - 1]);
  43.                 _heap.pop_back();
  44.                 adjustDown();
  45.         }
  46. }
  47. void Heap::adjustDown()
  48. {
  49.         int size = _heap.size();
  50.         int curIndex = 0, leftIndex = 1;
  51.         while (leftIndex < size)
  52.         {
  53.                 if (leftIndex + 1 < size && _heap[leftIndex] < _heap[leftIndex + 1])
  54.                 {
  55.                         leftIndex++;
  56.                 }
  57.                 if (_heap[curIndex] < _heap[leftIndex])
  58.                 {
  59.             std::swap(_heap[curIndex], _heap[leftIndex]);
  60.                         curIndex = leftIndex;
  61.                         leftIndex = curIndex * 2 + 1;
  62.                 }
  63.                 else
  64.                 {
  65.                         break;
  66.                 }
  67.         }
  68.        
  69. }
  70. void Heap::sort(std::vector<int>& v)
  71. {
  72.         for (int i = 0; i < v.size(); i++)
  73.         {
  74.         insert(v[i]);
  75.         }
  76.     for (int i = 0; i < v.size(); i++)
  77.     {
  78.         v[i] = top();
  79.         pop();
  80.     }
  81. }
复制代码
  1. //main.c
  2. #include "heap.h"
  3. #include <ctime>
  4. int main() {
  5.     /*Heap heap;
  6.     heap.insert(1);
  7.     heap.insert(2);
  8.     heap.insert(3);
  9.     heap.pop();
  10.     std::cout << heap.top() << std::endl;
  11.     heap.pop();
  12.     std::cout << heap.top() << std::endl;*/
  13.     srand((unsigned int)time(NULL));
  14.     std::vector<int> v;
  15.     for (int i = 0; i < 100; i++)
  16.     {
  17.         v.push_back(rand()%100);
  18.     }
  19.     Heap heap;
  20.     heap.sort(v);
  21.     for (auto& e : v)
  22.     {
  23.         std::cout << e << " ";
  24.     }
  25. }
复制代码

            新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

石小疯

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表