【堆】堆的实现(Java)

22 篇文章 2 订阅

  今天学习堆这种数据结构。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

  • 堆中某个结点的值总是不大于或不小于其父结点的值;
  • 堆总是一棵完全二叉树。

将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

1.用优先队列实现

一般我们直接用优先队列实现堆:

// 小顶堆,以 Integer 为例
PriorityQueue<Integer> pq = new PriorityQueue<>();
// 大顶堆
PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> o2 - o1);
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
2.用数组实现

我们可以通过自己定义数组以及调整方法来实现堆。对于完全二叉树,用数组存储时,数组中下标为 i 的元素,其左孩子是 2i,右孩子是 2i+1,于是可以得到下面的实现:

public class Main {
    private final static int len = 101;  // 堆的最大容量,下标从 1 开始,因此len 比元素总数多一个
    private static int n = 0;	// 堆中元素的个数
    private static int[] heap = new int[len];

    // 向下调整,时间复杂度 O(logn)
    public static void downAdjust(int low, int high) {
        int i = low, j = i * 2;
        while (j <= high) {
            if (j + 1 <= high && heap[j + 1] > heap[j]) {
                j = j + 1;      // 右孩子更大
            }
            if (heap[j] > heap[i]) {    // 孩子比父节点大
                int temp = heap[i];
                heap[i] = heap[j];
                heap[j] = temp;
                i = j;
                j = i * 2;
            } else {    // 不需要调整提前结束
                break;
            }
        }
    }

    // 建堆,时间复杂度O(n)
    public static void createHeap() {
        for (int i = n / 2; i >= 1; i--) {
            downAdjust(i, n);
        }
    }

    // 删除堆顶元素,时间复杂度O(logn)
    public static int deleteTop() {
        int top = heap[1];
        heap[1] = heap[n--];
        downAdjust(1, n);
        return top;
    }

    // 向上调整,时间复杂度O(logn)
    public static void upAdjust(int low, int high) {
        int i = high, j = i / 2;
        while (j >= low) {
            if (heap[j] < heap[i]) {    // 父节点更小,需要调整
                int temp = heap[i];
                heap[i] = heap[j];
                heap[j] = temp;
                i = j;
                j = i / 2;
            } else {
                break;
            }
        }
    }

    // 添加元素
    public static void insert(int x) {
        heap[++n] = x;
        upAdjust(1, n);
    }

    public static void main(String[] args) {

        int[] nums = new int[]{4, 3, 7, 8, 2, 1, 9, 6, 5};

        // 测试 insert
        n = 0;
        for (int num : nums) {
            insert(num);
        }
        while (n != 0) {
            System.out.print(deleteTop() + " ");
        }

        System.out.println();

        // 测试 createHeap
        n = nums.length;
        for (int i = 0; i < n; i++) {
            heap[i + 1] = nums[i];
        }
        createHeap();
        while (n != 0) {
            System.out.print(deleteTop() + " ");
        }
    }
}
3.练习:力扣373. 查找和最小的 K 对数字

给定两个以 升序排列 的整数数组 nums1 和 nums2 , 以及一个整数 k 。

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。

请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。

示例 1:

输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
     [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]

示例 2:

输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1]
解释: 返回序列中的前 2 对数:
     [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]

示例 3:

输入: nums1 = [1,2], nums2 = [3], k = 3 
输出: [1,3],[2,3]
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]

用优先队列实现堆:

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        // 定义一个堆,大小为 k
        PriorityQueue<int[]> heap = new PriorityQueue<>(k, new Comparator<int[]>(){
            @Override
            public int compare(int[] idx1, int[] idx2) {
                return nums1[idx1[0]] + nums2[idx1[1]] - nums1[idx2[0]] - nums2[idx2[1]];
            }
        });
        List<List<Integer>> result = new ArrayList<>();
        int m = nums1.length;
        int n = nums2.length;

        // 假设上一个被选择的是[mi, nj],由于 m 和 n 是递增的
        // 那么下一个一定是 [mi+1, nj] 或者 [mi, nj+1]
        // 所以先把 m 的每个元素和 n0 组合,加进去,下次加入只需要将 n 的下标加 1 即可
        for (int i = 0; i < Math.min(m, k); i++) {
            heap.offer(new int[]{i, 0});
        }
        while (k-- > 0 && !heap.isEmpty()) {
            int[] idx = heap.poll();
            result.add(Arrays.asList(new Integer[]{nums1[idx[0]], nums2[idx[1]]}));
            if (idx[1] + 1 < n) {
                heap.offer(new int[]{idx[0], idx[1] + 1});
            }
        }
        return result;
    }
}
Reference

[1]力扣373. 查找和最小的 K 对数字:https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/
欢迎关注。
在这里插入图片描述

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java堆排序的实现步骤如下: 1. 将待排序数组构建成一个大根堆(或小根堆)。 2. 将堆顶元素与堆底元素交换位置,并将堆底元素移出堆。 3. 对新的堆顶元素进行堆调整,使其满足堆的性质。 4. 重复步骤2和3,直到堆中只剩下一个元素。 Java代码实现如下: ```java public class HeapSort { public static void heapSort(int[] arr) { if (arr == null || arr.length < 2) { return; } // 构建大根堆 for (int i = arr.length / 2 - 1; i >= 0; i--) { heapify(arr, i, arr.length); } // 将堆顶元素与堆底元素交换位置,并将堆底元素移出堆。对新的堆顶元素进行堆调整,使其满足堆的性质。 for (int i = arr.length - 1; i > 0; i--) { swap(arr, 0, i); heapify(arr, 0, i); } } private static void heapify(int[] arr, int index, int size) { int left = index * 2 + 1; int right = index * 2 + 2; int largest = index; if (left < size && arr[left] > arr[largest]) { largest = left; } if (right < size && arr[right] > arr[largest]) { largest = right; } if (largest != index) { swap(arr, index, largest); heapify(arr, largest, size); } } private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } ``` 在该代码中,heapify方法用于将一个节点调整为符合堆性质的节点,swap方法用于交换两个元素的位置。在heapSort方法中,首先构建一个大根堆,然后依次将堆顶元素与堆底元素交换位置,并对新的堆顶元素进行堆调整,直到堆中只剩下一个元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值