【数据结构】堆&优先级队列

前端之家收集整理的这篇文章主要介绍了【数据结构】堆&优先级队列前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

说实话,之前看数据结构的时候,并没有更多的关注到堆,直到现在......

堆数据结构是一种数组现象,可以看成是一种完全二叉树。

堆的分类;

最大堆:每个父节点都大于其孩子结点。

最小堆:每个父节点都小于其孩子结点。

注意注意:区分与二叉排序树的区别!!!

堆也有很多应用,比如优先级队列,堆排序等等。再多的应用,都是先需要有堆。

堆的底层是一个数组,了解STL之后可以将底层写成vector,可以动态增容。

堆的创建:将一个数组中的元素进行向下调整,调整成大堆或者小堆。

向下调整算法:

  1. void _HeapDown(size_t index)
  2. {
  3. size_t parent = index;
  4. size_t child = 0;
  5. while (child < _heap.size())
  6. {
  7. child = parent * 2 +1 ;
  8. if (child + 1 < _heap.size() && _heap[child] < _heap[child + 1])
  9. ++child;
  10. if (child < _heap.size()&&_heap[parent] < _heap[child])
  11. {
  12. swap(_heap[parent],_heap[child]);
  13. parent = child;
  14. }
  15. else
  16. break;
  17. }
  18. }


只要一个结点有左右孩子,就得去比较,看是否需要交换。只能从倒数第一个非叶子结点开始。

堆也有自己的Push和Pop操作。Push操作就和栈,队列的Push一样,只是Push了之后需要调整成一个堆。Pop操作,不

删除最后一个元素,而是删除0号下标的元素。

Pop:将最后一个元素和0号下标的元素交换,删除最后一个元素,然后采用向下调整的算法进行调整。

Push:在数组的最后插入一个新的元素,采用向上调整的算法进行调整。

向上调整算法:

  1. void _HeapUp(size_t child)
  2. {
  3. assert(_heap.size()>0);
  4. size_t parent = 0;
  5. Compare com;
  6. while (child > 0)
  7. {
  8. parent = (child - 1) / 2;
  9. if (_heap[parent] < _heap[child])
  10. {
  11. swap(_heap[parent],_heap[child]);
  12. child = parent;
  13. }
  14. else
  15. break;
  16. }
  17. }


这个比起向下调整就比较简单了。从给定结点开始,只需比较他和他的parent的大小关系(大堆时,parent下标的

值小于child下标的值就进行交换,并记住child值的变动,小堆同理)。

有时候,我们既需要大堆,也需要小堆,当然可以实现两个类,大堆类和小堆类。然而我们又发现大堆和小堆最大的

区别就在于个结点与孩子结点的大小关系,其他的思路什么的都是一样的,两个类就达不到代码的复用性。

这里我们采用仿函数,又叫函数对象,通过它来实现代码复用。

下边给出代码

  1. template<typename T>
  2. struct Less
  3. {
  4. bool operator()(const T& l,const T& r)
  5. {
  6. return l < r;
  7. }
  8. };
  9. template<typename T>
  10. struct Greater
  11. {
  12. bool operator()(const T& l,const T& r)
  13. {
  14. return l > r;
  15. }
  16. };
  17.  
  18. template<typename T,typename Compare = Greater<T>>
  19. class Heap
  20. {
  21. public:
  22. Heap(T* _a = NULL,size_t size = 0)
  23. {
  24. for (size_t i = 0; i < size; ++i)
  25. {
  26. _heap.push_back(_a[i]);
  27. }
  28. for (int i = (size-2) / 2; i >= 0; --i)
  29. {
  30. _HeapDown(i);//向下调整
  31. }
  32. }
  33. void Show()
  34. {
  35. if (_heap.size())
  36. {
  37. for (size_t i = 0; i < _heap.size();++i)
  38. cout << _heap[i] << " ";
  39. cout << endl;
  40. }
  41. }
  42. void Push(const T& x)
  43. {
  44. _heap.push_back(x);
  45. _HeapUp(_heap.size()-1);
  46. }
  47. void Pop()//删除堆顶的元素
  48. {
  49. assert(_heap.size()>0);
  50. swap(_heap[0],_heap[_heap.size()-1]);
  51. _heap.pop_back();
  52. _HeapDown(0);
  53. }
  54. size_t Size()
  55. {
  56. return _heap.size();
  57. }
  58. const T& Top()
  59. {
  60. return _heap[0];
  61. }
  62. protected:
  63. void _HeapDown(size_t index)
  64. {
  65. size_t parent = index;
  66. size_t child = 0;
  67. Compare com;
  68. while (child < _heap.size())
  69. {
  70. child = parent * 2 +1 ;
  71. //if (child + 1 < _heap.size() && _heap[child] < _heap[child + 1])
  72. if (child + 1 < _heap.size() && com(_heap[child+1],_heap[child]))
  73. ++child;
  74. //if (child < _heap.size()&&_heap[parent] < _heap[child])
  75. if (child < _heap.size() && com(_heap[child],_heap[parent]))
  76. {
  77. swap(_heap[parent],_heap[child]);
  78. parent = child;
  79. }
  80. else
  81. break;
  82. }
  83. }
  84. void _HeapUp(size_t child)
  85. {
  86. assert(_heap.size()>0);
  87. size_t parent = 0;
  88. Compare com;
  89. while (child > 0)
  90. {
  91. parent = (child - 1) / 2;
  92. //if (_heap[parent] < _heap[child])
  93. if(com(_heap[child],_heap[child]);
  94. child = parent;
  95. }
  96. else
  97. break;
  98. }
  99. }
  100. private:
  101. vector<T> _heap;
  102. };
  103.  
  104. void testHeap()
  105. {
  106. int a[] = { 3,4,5,1,2,6,7 };
  107. //测试大堆
  108. Heap<int> h1(a,7);
  109. h1.Show();
  110. h1.Push(10);
  111. h1.Show();
  112. h1.Pop();
  113. h1.Show();
  114. //测试小堆
  115. Heap<int,Less<int>> h2(a,7);
  116. h2.Show();
  117. h2.Push(0);
  118. h2.Show();
  119. h2.Pop();
  120. h2.Show();
  121. }


这里就可以实现大小堆。

时间复杂度:

建堆:O(N*lgN)

插入:0(lgN)

删除:O(lgN)

优先级队列:

我们知道,队列是一种先进先出的数据结构,然而有时候先进先出并不能满足于我们。我们需要优先级最高的元素先

出队列,下边给出两种方法

Push:插入的时候就将插入的元素按照优先级放在合适的位置。时间复杂度O(N)

Pop:直接从队头删除。时间复杂度O(1)


Push:直接插在队尾。时间复杂度O(1)

Pop:找出优先级最高的元素进行删除时间复杂度O(N)

第一种方法比第二种更高效。

而这里,堆是实现优先级队列的一种更加高效的方法;

下边给出代码

  1. template<typename T,typename Compare = Greater<T>>
  2. class PriorityQueue
  3. {
  4. public:
  5. PriorityQueue(T* a,size_t size)
  6. :_q(a,size)
  7. {}
  8. void Pop()
  9. {
  10. _q.Pop();
  11. }
  12. void Push(const T& x)
  13. {
  14. _q.Push(x);
  15. }
  16. const T& Top()
  17. {
  18. return _q.Top();
  19. }
  20. void Show()
  21. {
  22. _q.Show();
  23. }
  24. private:
  25. Heap<T,Compare> _q;
  26. };
  27. void testQueue()
  28. {
  29. int a[] = { 3,7 };
  30. //测试小堆
  31. PriorityQueue<int,Less<int>> q1(a,7);
  32. q1.Show();
  33. q1.Push(0);
  34. q1.Show();
  35. q1.Pop();
  36. q1.Show();
  37. //测试大堆
  38. PriorityQueue<int> q2(a,7);
  39. q2.Show();
  40. q2.Push(10);
  41. q2.Show();
  42. q2.Pop();
  43. q2.Show();
  44. }


这里就可以高效的实现优先级队列。需要注意的是,构造函数中那个成员,必须用初始化列表完成。这里就涉及到必

须使用初始化列表的几种情况~~~~

猜你在找的数据结构相关文章