大顶堆:任意非叶子节点的值大于等于其子节点的值。
小顶堆:任意非叶子节点的值小于等于其子节点的值。
堆是完全二叉树,所以可以直接用数组存储。
堆初始化:
堆的初始化使用筛降法,从最后一个非叶子节点开始向下调整直到跟节点。需要建堆的数组长度为n,最后一个元素的下标为n-1,其父节点为 ((n-1) - 1) >> 1
堆顶元素的删除:
每次只能删除堆顶元素,删除完堆顶元素之后,将最后一个元素放在堆顶,此时的堆不满足堆的性质,需要进行调整。
/** * 向下调整 * * @param i 要调整的编号 */ private void siftDown(int i) { // 如果父节点比任意一个子节点要大,需要调整,和子节点中较小的那个节点进行互换 // 只有非叶子节点才需要调整 int value = nums[i]; while (2 * i + 1 < len) { int left = 2 * i + 1; int right = 2 * i + 2; // 有右节点,先比较两个左右节点 int minValueIdx = (right < len && nums[right] < nums[left]) ? right : left; // 子节点中比较小的和父节点比较 if (nums[minValueIdx] < value) { // 需要调整 nums[i] = nums[minValueIdx]; i = minValueIdx; } else { // 节点i比孩子节点都小,不用再调整了 break; } } nums[i] = value; }
向堆中添加元素:
每次向堆中添加元素时,直接将元素放在末尾,然后调整新放入元素的位置到正确的位置。
public void add(int i) { if (len < nums.length) { nums[len] = i; siftUp(len); len++; } } /** * 向上调整 * * @param i */ private void siftUp(int i) { // i = 0时是根节点, 不需要再调整了 int value = nums[i]; while (i > 0) { int p = (i - 1) >> 1; if (nums[p] > value) { // 父节点更大, 需要调整 nums[i] = nums[p]; i = p; } else { break; } } nums[i] = value; }