• 【数据结构】优先级队列(堆)重点知识汇总(附有代码)


    目录

    思维导图:

    1.优先级队列:

    2.PriorityQueue特性:

    3.优先级队列的使用:


    思维导图

    1.优先级队列

    1. 队列的特点是先进先出,优先级队列中则是优先级高的元素先出队列
    2. 堆是将所有元素按完全二叉树的顺序存储方式存储到一个数组中,如果根节点的值大于孩子节点则称大根堆;若根节点的值小于孩子节点则称为小根堆堆总是一颗完全二叉树
    3. 完全二叉树可以采用层序遍历的规则按照顺序存储到数组中,而非完全二叉树则不适合,容易造成空间的浪费空间利用率低下
    4. 堆的创建,在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整,建堆的时间复杂度为O(n)。
      //建立大根堆,每一棵子树的调整属于向下调整
      public void createHeap(){
          for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {
              shiftDown(parent,usedSize);
          }
      }
      //向下调整
      public void shiftDown(int parent,int len){
          int child = parent*2 + 1;
          //parent和child只是下标
          while(parent < len){
              if(child+1 < len && elem[child] < elem[child+1]){
                  child++;
              }
              if(elem[child] > elem[parent]){
                  int tmp = elem[child];
                  elem[child] = elem[parent];
                  elem[parent] = tmp;
                  parent = child;
                  child = 2*parent + 1;
              }
              else{
                  break;
              }
          }
      }
      

       

       

       

    5. 堆的插入涉及到了向上调整,插入和删除元素的时间复杂度都为O(log2N)。
      //入堆,需要向上调整
      public void offer(int x){
          if(isFull()){
              elem = Arrays.copyOf(elem,elem.length*2);
          }
          this.elem[usedSize] = x;
          usedSize++;
          shiftUp(usedSize-1);
      }
      
      private boolean isFull() {
          return usedSize == elem.length;
      }
      
      //向上调整
      public void shiftUp(int child){
          int parent = (child-1)/2;
          while(child > 0){
              if(elem[child] > elem[parent]){
                  int tmp = elem[child];
                  elem[child] = elem[parent];
                  elem[parent] = tmp;
                  child = parent;
                  parent = (child-1)/2;
              }else{
                  break;
              }
          }
      }
    6. 优先级队列的删除操作,删除的是堆顶的元素,将要删除的堆顶元素和最后元素交换,然后让usedSize--,再让0下标向下调整即可;插入和删除元素的时间复杂度为O(log2N)。
      //出堆,需要向下调整
      public int poll(){
          if(isEmpty()){
              return -1;
          }
          int oldvalue = elem[0];
          int tmp = elem[0];
          elem[0] = elem[usedSize-1];
          elem[usedSize-1] = tmp;
          usedSize--;
          shiftDown(0,usedSize);
          return oldvalue;
      }
      private boolean isEmpty(){
          return usedSize == 0;
      }
    7. 优先级队列模拟实现时,就是将完全二叉树使用数组的形式存储起来
    1. class TestHeap{
    2. public int[] elem;
    3. public int usedSize;
    4. public TestHeap() {
    5. this.elem = new int[10];
    6. this.usedSize = usedSize;
    7. }
    8. public void initArray(int[] array){
    9. elem = Arrays.copyOf(array,array.length*2);
    10. usedSize = elem.length;
    11. }
    12. //建立大根堆
    13. public void createHeap(){
    14. for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {
    15. shiftDown(parent,usedSize);
    16. }
    17. }
    18. //向下调整
    19. public void shiftDown(int parent,int len){
    20. int child = parent*2 + 1;
    21. while(parent < len){
    22. if(child+1 < len && elem[child] < elem[child+1]){
    23. child++;
    24. }
    25. if(elem[child] > elem[parent]){
    26. int tmp = elem[child];
    27. elem[child] = elem[parent];
    28. elem[parent] = tmp;
    29. parent = child;
    30. child = 2*parent + 1;
    31. }
    32. else{
    33. break;
    34. }
    35. }
    36. }
    37. //入堆,需要向上调整
    38. public void offer(int x){
    39. if(isFull()){
    40. elem = Arrays.copyOf(elem,elem.length*2);
    41. }
    42. this.elem[usedSize] = x;
    43. usedSize++;
    44. shiftUp(usedSize-1);
    45. }
    46. private boolean isFull() {
    47. return usedSize == elem.length;
    48. }
    49. //向上调整
    50. public void shiftUp(int child){
    51. int parent = (child-1)/2;
    52. while(child > 0){
    53. if(elem[child] > elem[parent]){
    54. int tmp = elem[child];
    55. elem[child] = elem[parent];
    56. elem[parent] = tmp;
    57. child = parent;
    58. parent = (child-1)/2;
    59. }else{
    60. break;
    61. }
    62. }
    63. }
    64. //出堆,需要向下调整
    65. public int poll(){
    66. if(isEmpty()){
    67. return -1;
    68. }
    69. int oldvalue = elem[0];
    70. int tmp = elem[0];
    71. elem[0] = elem[usedSize-1];
    72. elem[usedSize-1] = tmp;
    73. usedSize--;
    74. shiftDown(0,usedSize);
    75. return oldvalue;
    76. }
    77. private boolean isEmpty(){
    78. return usedSize == 0;
    79. }
    80. }
    81. public class Main {
    82. public static void main(String[] args) {
    83. int[] elem = { 27,15,19,18,28,34,65,49,25,37 };
    84. TestHeap testHeap = new TestHeap();
    85. testHeap.initArray(elem);
    86. testHeap.createHeap();
    87. System.out.println("调试!");
    88. }
    89. }

    2.PriorityQueue特性:

    1. PriorityQueue底层是使用堆来实现的,默认情况为小堆。如果需要大根堆需要提供比较器。
    2. 优先级队列底层数组默认大小是11;没有容量限制,可以插入多个元素。如果容量小于64数组会按照2倍扩容。容量大于等于64数组会按照1.5倍扩容。
    3. PriorityQueue放的元素必须可以比较,不可以插入不能比较的元素和null,比较器需要自己传入如果不传入默认这个数据是可以比较的,默认实现Comparable接口的。
      //方法1
      class IntCmp implements Comparator{
      
          @Override
          public int compare(Integer o1, Integer o2) {
              //小根堆 return o1 - o2;
              //大根堆
              return o2 - o1;
          }
      }
      public static void main(String[] args) {
          PriorityQueue priorityQueue = new PriorityQueue<>(new IntCmp());       
      }
      
      //方法2
      PriorityQueue priorityQueue 
                          = new PriorityQueue<>(new Comparator() {
          @Override
          public int compare(Integer o1, Integer o2) {
              //大根堆
              return o2 - o1;
          }
      });

    3.优先级队列的使用:

    1. topK问题,时间复杂度为O(N log2N)
      top-K问题:
      1.求前k个最大的,建大小为k的小堆。拿剩下的元素和堆顶元素比较,剩下元素大于堆顶,则删除堆顶元素,入大的元素。
      2.求前k个最小的,建大小为k的大堆。拿剩下的元素和堆顶元素比较,剩下元素小于堆顶,则删除堆顶元素,入小的元素。
      3.求第k个最大的,建大小为k的小堆,堆顶元素就是第k个最大的。拿剩下的元素和堆顶元素比较,剩下元素大于堆顶,则删除堆顶元素,入大的元素。
      4.求第k个最小的,建大小为k的大堆,堆顶元素就是第k个最小的。拿剩下的元素和堆顶元素比较,剩下元素小于堆顶,则删除堆顶元素,入小的元素。
    2. 堆排序,若将一组数据从小到大排序,要求在原数组上进行调整,不得创建新的数组。解题思路:建立大根堆;将堆顶元素也就是最大值和最后一个下标元素交换交换后向下调整再让end--即可。此处留心shiftDown(array, 0, end); 后再 end--;

    top-k问题的代码实现:

    1. public int[] smallestK(int[] array,int k){
    2. int[] ret = new int[k];
    3. if(k == 0) return ret;
    4. PriorityQueue priorityQueue = new PriorityQueue<>(k, new Comparator() {
    5. @Override
    6. public int compare(Integer o1, Integer o2) {
    7. return o2 - o1;
    8. }
    9. });
    10. for (int i = 0; i < array.length; i++) {
    11. if(priorityQueue.size() < k){
    12. priorityQueue.offer(array[i]);
    13. }else{
    14. int top = priorityQueue.peek();
    15. if(array[i] < top){
    16. priorityQueue.poll();
    17. priorityQueue.offer(array[i]);
    18. }
    19. }
    20. }
    21. for (int i = 0; i < k; i++) {
    22. ret[i] = priorityQueue.poll();
    23. }
    24. return ret;
    25. }

    堆排序的代码实现:

    1. //堆排序
    2. public void shiftDown(int parent,int len){
    3. int child = 2*parent + 1;
    4. while(child
    5. if(child+1 < len && elem[child] < elem[child+1]){
    6. child++;
    7. }
    8. if(elem[child] > elem[parent]){
    9. int tmp = elem[child];
    10. elem[child] = elem[parent];
    11. elem[parent] = tmp;
    12. parent = child;
    13. child = 2*parent + 1;
    14. }else{
    15. break;
    16. }
    17. }
    18. }
    19. //建立一个大根堆
    20. public void createHeap(){
    21. for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
    22. shiftDown(parent,usedSize);
    23. }
    24. }
    25. //实现堆排序
    26. public void heapSort() {
    27. int end = usedSize - 1;
    28. while (end > 0) {
    29. swap(array, 0, end);
    30. shiftDown(array, 0, end);
    31. end--;
    32. }
    33. }

    如果对您有帮助的话,

    不要忘记点赞+关注哦,蟹蟹

    如果对您有帮助的话,

    不要忘记点赞+关注哦,蟹蟹

    如果对您有帮助的话,

    不要忘记点赞+关注哦,蟹蟹

  • 相关阅读:
    使用match-lstm和答案指针进行机器理解
    git密码提交切换SSH提交
    信创之国产浪潮电脑+统信UOS操作系统体验4:visual studio code中怎么显示中文
    Delaunay三角网之逐点插入法(优化版本三)
    Linux c编程之TCP通信
    统计单词数
    【贝叶斯分类2】朴素贝叶斯分类器
    数据建模设计
    二叉树的中序遍历
    掌握这些插件,分分钟提高你的办公效率90%!
  • 原文地址:https://blog.csdn.net/qq_68993495/article/details/127109886