春招冲刺百题计划|堆

Java基础复习

  1. Java数组的声明与初始化
  2. Java ArrayList
  3. Java HashMap
  4. Java String 类
  5. Java LinkedList
  6. Java Deque继承LinkedList
  7. Java Set
  8. Java 队列
  9. 优先队列:第二题用到了

第一题:215. 数组中的第K个最大元素

在这里插入图片描述
可以直接使用Arrays.sort()快排,然后return nums[n-k];
或者手动实现快速选择的内容:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
在这里插入图片描述

class Solution {
    int quickselect(int[] nums, int l, int r, int k) {
        if (l == r) return nums[k];
        int x = nums[l], i = l - 1, j = r + 1; //选最左边的为基准。
        while (i < j) {
            do{ 
                i++; 
            }while(nums[i] < x);//左边的都小于x
            do{
                j--;
            }while(nums[j] > x);//右边的都大于x
            if (i < j){
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }
        }
        if (k <= j) return quickselect(nums, l, j, k); //一趟排序后,基准比第k大的大,那么在基准左边找。
        else return quickselect(nums, j + 1, r, k);//否则在基准右边找。
    }
    public int findKthLargest(int[] _nums, int k) {
        int n = _nums.length;
        return quickselect(_nums, 0, n - 1, n - k);
    }
}

另一种解法就是堆排序:参考:https://pdai.tech/md/algorithm/alg-sort-x-heap.html
逻辑结构是完全二叉树,存储结构是数组。之间的联系如下:
在这里插入图片描述算法实现步骤就是, ① 初始化堆: 将数列a[1…n]构造成最大堆。 ② 交换数据: 将a[1]和a[n]交换,使a[n]是a[1…n]中的最大值;然后将a[1…n-1]重新调整为最大堆。 接着,将a[1]和a[n-1]交换,使a[n-1]是a[1…n-1]中的最大值;然后将a[1…n-2]重新调整为最大值。 依次类推,直到整个数列都是有序的。

    public static void maxHeapDown(int[] a, int start, int end) {
        int c = start;            // 当前(current)节点的位置
        int l = 2*c + 1;        // 左(left)孩子的位置
        int tmp = a[c];            // 当前(current)节点的大小

        for (; l <= end; c=l,l=2*l+1) {
            // "l"是左孩子,"l+1"是右孩子
            if ( l < end && a[l] < a[l+1])
                l++;        // 左右两孩子中选择较大者,即m_heap[l+1]
            if (tmp >= a[l])
                break;        // 调整结束
            else {            // 交换值
                a[c] = a[l];
                a[l]= tmp;
            }
        }
    }

    /*
     * 堆排序(从小到大)
     *
     * 参数说明: 
     *     a -- 待排序的数组
     *     n -- 数组的长度
     */
    public static void heapSortAsc(int[] a, int n) {
        int i,tmp;

        // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
        for (i = n / 2 - 1; i >= 0; i--)
            maxHeapDown(a, i, n-1);

        // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
        for (i = n - 1; i > 0; i--) {
            // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
            tmp = a[0];
            a[0] = a[i];
            a[i] = tmp;
            // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
            // 即,保证a[i-1]是a[0...i-1]中的最大值。
            maxHeapDown(a, 0, i-1);
        }
    }

提交代码:

class Solution {
    public static void heapSortAsc(int[] a, int n){

        for(int i=n/2-1; i>=0; i--){
            maxHeapDown(a, i, n-1);
        }
        for (int i = n - 1; i > 0; i--) {
            // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
            int tmp = a[0];
            a[0] = a[i];
            a[i] = tmp;
            // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
            // 即,保证a[i-1]是a[0...i-1]中的最大值。
            maxHeapDown(a, 0, i-1);
        }

    }
    public static void maxHeapDown(int[] a, int start, int end) {
        int c = start;            // 当前(current)节点的位置
        int l = 2*c + 1;        // 左(left)孩子的位置
        int tmp = a[c];            // 当前(current)节点的大小

        for (; l <= end; c=l,l=2*l+1) {
            // "l"是左孩子,"l+1"是右孩子
            if ( l < end && a[l] < a[l+1])
                l++;        // 左右两孩子中选择较大者,即m_heap[l+1]
            if (tmp >= a[l])
                break;        // 调整结束
            else {            // 交换值
                a[c] = a[l];
                a[l]= tmp;
            }
        }

    }
    public int findKthLargest(int[] _nums, int k) {
        int n = _nums.length;
        heapSortAsc(_nums, n);
        return _nums[n-k];
    }
}

第二题:373. 查找和最小的 K 对数字

在这里插入图片描述
以下写法超出内存限制:

class Solution {
     public static void heapSortAsc(int[][] a, int n){
        for(int i=n/2-1; i>=0; i--){
            maxHeapDown(a, i, n-1);
        }
        for (int i = n - 1; i > 0; i--) {
            // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
            int[] tmp = a[0];
            a[0] = a[i];
            a[i] = tmp;
            // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
            // 即,保证a[i-1]是a[0...i-1]中的最大值。
            maxHeapDown(a, 0, i-1);
        }
    }
    public static void maxHeapDown(int[][] a, int start, int end) {
        int c = start;            // 当前(current)节点的位置
        int l = 2*c + 1;        // 左(left)孩子的位置
        int[] tmp = a[c];            // 当前(current)节点的大小

        for (; l <= end; c=l,l=2*l+1) {
            // "l"是左孩子,"l+1"是右孩子
            if ( l < end && (a[l][0] + a[l][1]) < (a[l+1][0] + a[l+1][1]))
                l++;        // 左右两孩子中选择较大者,即m_heap[l+1]
            if ((tmp[0]+tmp[1]) >=  (a[l][0] + a[l][1]))
                break;        // 调整结束
            else {            // 交换值
                a[c] = a[l];
                a[l] = tmp;
            }
        }

    }
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        
        int n = nums1.length*nums2.length;
        int[][] a = new int[n][2];
        int idx = 0;
        for(int i=0; i<nums1.length; i++){
            for(int j=0; j<nums2.length; j++){
                a[idx][0] = nums1[i];
                a[idx++][1] = nums2[j];
            }
        }
        heapSortAsc(a, n);
        List<List<Integer>> results = new ArrayList<List<Integer>>();   
        for(int i=0; i<k; i++){
                List<Integer> tmp = new ArrayList<Integer>();
                tmp.add(a[i][0]);
                tmp.add(a[i][1]);
                results.add(tmp);
        }
        return results;

    }
}

参考:灵茶山艾府的题解
在这里插入图片描述
觉得这样的大神,每次写得代码都很简洁。哭唧唧。我和这篇的问题,就在于,我使用的代码的堆,没办法随时调整。

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        List<List<Integer>> ans = new ArrayList<>(k); // 预分配空间
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
        pq.add(new int[]{nums1[0] + nums2[0], 0, 0});
        while (ans.size() < k) {
            int[] p = pq.poll();
            int i = p[1];
            int j = p[2];
            ans.add(List.of(nums1[i], nums2[j]));
            if (j == 0 && i + 1 < nums1.length) {
                pq.add(new int[]{nums1[i + 1] + nums2[0], i + 1, 0});
            }
            if (j + 1 < nums2.length) {
                pq.add(new int[]{nums1[i] + nums2[j + 1], i, j + 1});
            }
        }
        return ans;
    }
}

这里自己仿照源码写了一份优先队列

class Solution {
    class MyPriorityQueue{
        //仿照源码编写自己的
        private int[][] queue;
        private int size;
        MyPriorityQueue(){
            queue=new int[0][0];
            size = 0;
        }
        private int Mycompare(int[] a, int[] b){
            return a[0]-b[0];
        }
        public void offer(int[] e){
            int i = size;
            if (i >= queue.length)
                queue = Arrays.copyOf(queue, i+1);
                size = i + 1;
                if (i == 0)//队列原来为空,这是插入的第一个元素
                    queue[0] = e;
                else
                    siftUp(i, e);//调整
        }

        private void siftUp(int k, int[] x) { //找到x该待的位置
            while (k > 0) {
                int parent = (k - 1) >>> 1;//parentNo = (nodeNo-1)/2
                int[] e = queue[parent];
                if (Mycompare(x, e) >= 0) break;
                queue[k] = e;
                k = parent;
            }
            queue[k] = x;
        }

        public int[] peek() {
            if (size == 0)
                return null;
            return queue[0];//0下标处的那个元素就是最小的那个
        }

        public int[] poll() {
            if (size == 0)
                return null;
            int s = --size;
            int[] result = queue[0];//0下标处的那个元素就是最小的那个
            int[] x = queue[s]; //最后一个叶子结点
            queue[s] = null;
            if (s != 0) //队列不为空要调整
                siftDown(0, x);//调整
            return result;
        }

        private void siftDown(int k, int[] x) {
            int half = size >>> 1; //size/2
            while (k < half) {
                //首先找到左右孩子中较小的那个,记录到c里,并用child记录其下标
                int child = (k << 1) + 1;//leftNo = parentNo*2+1
                int[] c = queue[child];
                int right = child + 1;
                if (right < size && Mycompare(c, queue[right]) > 0)
                    c = queue[child = right];
                if (Mycompare(x, c) <= 0)
                    break;
                queue[k] = c;//然后用c取代原来的值
                k = child;
            }
            queue[k] = x;
        }
        
    }


    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        List<List<Integer>> ans = new ArrayList<>(k); // 预分配空间
        MyPriorityQueue pq = new MyPriorityQueue();
        pq.offer(new int[]{nums1[0] + nums2[0], 0, 0});
        while (ans.size() < k) {
            int[] p = pq.poll();
            int i = p[1];
            int j = p[2];
            ans.add(List.of(nums1[i], nums2[j]));
            if (j == 0 && i + 1 < nums1.length) {
                pq.offer(new int[]{nums1[i + 1] + nums2[0], i + 1, 0});
            }
            if (j + 1 < nums2.length) {
                pq.offer(new int[]{nums1[i] + nums2[j + 1], i, j + 1});
            }
        }
        return ans;
    }
}

第三题:264. 丑数 II

在这里插入图片描述
首先明确一下质因子是什么?
质因子(或质因数)在数论里是指能整除给定正整数的质数。
只有两个正因数(1和它本身)的自然数即为质数。(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97…)
仔细想想还是不难的,就是用小顶堆,弹出一个x,添加2x,3x,5x进去。

class Solution {
    int[] NUMS = new int[]{2, 3, 5};
    class MyPriorityQueue{
        //仿照源码编写自己的
        private long[] queue;
        private int size;
        MyPriorityQueue(){
            queue=new long[0];
            size = 0;
        }
        private long Mycompare(long a, long b){
            return a-b;
        }
        public void offer(long e){
            int i = size;
            if (i >= queue.length)
                queue = Arrays.copyOf(queue, i+1);
                size = i + 1;
                if (i == 0)//队列原来为空,这是插入的第一个元素
                    queue[0] = e;
                else
                    siftUp(i, e);//调整
        }

        private void siftUp(int k, long x) { //找到x该待的位置
            while (k > 0) {
                int parent = (k - 1) >>> 1;//parentNo = (nodeNo-1)/2
                long e = queue[parent];
                if (Mycompare(x, e) >= 0) break;
                queue[k] = e;
                k = parent;
            }
            queue[k] = x;
        }

        public long peek() {
            return queue[0];//0下标处的那个元素就是最小的那个
        }

        public long poll() {
            int s = --size;
            long result = queue[0];//0下标处的那个元素就是最小的那个
            long x = queue[s]; //最后一个叶子结点
            queue[s] = -1;
            if (s != 0) //队列不为空要调整
                siftDown(0, x);//调整
            return result;
        }

        private void siftDown(int k, long x) {
            int half = size >>> 1; //size/2
            while (k < half) {
                //首先找到左右孩子中较小的那个,记录到c里,并用child记录其下标
                int child = (k << 1) + 1;//leftNo = parentNo*2+1
                long c = queue[child];
                int right = child + 1;
                if (right < size && Mycompare(c, queue[right]) > 0)
                    c = queue[child = right];
                if (Mycompare(x, c) <= 0)
                    break;
                queue[k] = c;//然后用c取代原来的值
                k = child;
            }
            queue[k] = x;
        }
        
    }
    public int nthUglyNumber(int n) {
        //我目前的思路就是得到一组序列,长度为n,就是从1开始算,找到第n个满足条件的就行。
        int[] result = new int[n+1];
        result[1] = 1;
        if(n==1){
            return result[n];
        }
        int sum=2;
        MyPriorityQueue queue = new MyPriorityQueue();
        Set<Long> had = new HashSet<>();
        queue.offer(2); had.add(2l);
        queue.offer(3); had.add(3l);
        queue.offer(5); had.add(5l);
        while(sum<=n){
            long tmp = queue.poll();
            result[sum] = (int)tmp;
            for(int i=0; i<NUMS.length; i++){
                if(!had.contains(tmp*NUMS[i])){
                    had.add(tmp*NUMS[i]);
                    queue.offer(tmp*NUMS[i]);
                }
            }
            sum++;
        }
        return result[n];
    }
}

第四题:218.天际线问题

在这里插入图片描述
算术评级是9,我就看一看,凑个热闹。
莫名想接雨水,但是接雨水好像是维护一个单调队列。
后期有时间再补。先把简单题和中等题搞明白。o(╥﹏╥)o。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值