本文共 4192 字,大约阅读时间需要 13 分钟。
1,普通队列:先进先出。
2,带优先级的队列:优先级高的先出队列,优先级一样时按照先进先出的规则。 3,带类型的队列 4,阻塞队列:线程安全版本的队列,具有一定的特性(队列为空,取元素发生阻塞;队列满,插入元素)。 5,无锁队列:线程安全版本的队列,不用加锁就可以保证线程安全。使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。
一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。
这种方式的主要用法就是堆的表示。
左子树下标 = 根节点下标 * 2 + 1 右子树下标 = 根节点下标 * 2 + 2 根节点下标 = (孩子节点下标 - 1) / 2
1,堆逻辑上是一棵完全二叉树
2,堆物理上是保存在数组中 3,满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆 4,反之,则是小堆,或者小根堆,或者最小堆 5,堆的基本作用是,快速找集合中的最值(TOP K问题)前提:左右子树必须已经是一个堆(完全二叉树),才能调整。
说明:
// 小堆 向下调整 // 通过 size 指定 array 中哪些元素是有效的堆元素 // index 表示从哪个位置的下标开始调整 // 左右子树都是堆, 才能进行这样的调整. public static void shiftDown(int[] array, int size, int index) { int parent = index; int child = 2 * parent + 1; // 根据 parent 下标找到左子树的下标 while (child < size) { // 比较左右子树, 找到较小值 if (child + 1 < size && array[child + 1] < array[child]) { child = child + 1; } // 经过上面的比较, 已经不知道 child 是左子树还是右子树了. // 知道的是 child 下标一定对应左右子树最小值的下标. // 拿 child 位置的元素和 parent 位置的元素进行比较 if (array[child] < array[parent]) { // 不符合小堆的规则, 就交换父子节点 int tmp = array[child]; array[child] = array[parent]; array[parent] = tmp; } else { // 调整完毕, 不需要继续了 break; } // 更新 parent 和 child, 处理下一层的数据(child位置可能破坏堆性质). parent = child; child = parent * 2 + 1; } }// 大堆 向下调整 public static void shiftDown1(int[] array, int size, int index){ int parent = index; int child = 2*parent +1 ; //左子树 while(child < size){ // 孩子节点存在 if(child + 1 < size && array[child +1] > array[child]){ child = child+1; //右子树存在且右子树大 } if (array[child] < array[parent]) { // 不符合大堆的规则, 就交换父子节点 int tmp = array[child]; array[child] = array[parent]; array[parent] = tmp; } else { // 父节点大不需要调整, 不需要继续了 break; } // 更新 parent 和 child, 处理下一层的数据. parent = child; child = parent * 2 + 1; } }
eg:小堆
注意:
从前向后遍历,向上调整 从后向前遍历,向下调整public static void createHeap(int[] array, int size) { //向下调整 需要从后向前遍历 for (int i = (size - 1 - 1) / 2; i >= 0; i--) { shiftDown(array, size, i); } }
PriorityQueue implements Queue
给定 100 亿数字,求前 1000 大的元素?( 400亿字节 ≈ 40G空间)
答:使用方案二。方案一:用一个数组保存刚才这些数字,直接在这个数组上建大堆。循环 1000 次进行取堆顶元素+向下调整,就可以得到前 1000 大的元素。
耗空间:空间不一定充足,内存可能发不下
耗时间:100 亿数字建堆肯定比 1000 个数字建堆慢
方案二:先取集合中前 1000 个元素放到一个数组中,建立一个小堆。再一个个遍历集合中的数字,依次和堆顶元素比较,只要比堆顶元素大,就删除堆顶元素(调整堆),再把当前元素入堆(调整堆)。当所有元素遍历结束时,堆中的元素就是前 1000 大的元素。
时间复杂度分析(N >> M)
方案一:O(N) + O(M * log(N)) //建堆 + 遍历*堆调整
方案二:O(M) + O(N * log(M)) // 建堆 + 遍历*堆调整
题目链接:
需要把数对放到一个类中,优先队列用来保存类的对象即可。
import java.util.ArrayList;import java.util.List;import java.util.PriorityQueue;class Pair implements Comparable{ public int n1; public int n2; public int sum; public Pair(int n1, int n2){ this.n1 = n1; this.n2 = n2; this.sum = n1+n2; } @Override public int compareTo(Pair o) { return this.sum-o.sum; }}public class Solution { public List
> kSmallestPairs(int[] nums1, int[] nums2, int k) { List
> result = new ArrayList<>(); if(nums1.length == 0 || nums2.length ==0 || k<=0){ return result; } // 前 k 小元素,建立小堆 PriorityQueue queue = new PriorityQueue<>(); // 默认小堆 // 采取方式1 for(int i = 0; i < nums1.length; i++){ for(int j = 0; j < nums2.length; j++){ queue.offer(new Pair(nums1[i],nums2[j])); } } // 一定要队列不为空才可以取 k有可能大于数对数 for(int i = 0; i < k && !queue.isEmpty(); i++){ Pair cur = queue.poll(); List l = new ArrayList<>(); l.add(cur.n1); l.add(cur.n2); result.add(l); } return result; }}
升序为例:
转载地址:http://nhjwi.baihongyu.com/