剑指offer 76 数组中的第K大的数字

看了好多面经 发现手写数组堆已经考了不少遍了!怎么能还不会呢?今天必须把它拿下!

快速回顾:1.堆是个完全二叉树 2.堆的父节点要小于左右子节点 (仅限最小堆,最大堆要 "大于")

首先我会通过通俗易懂的方式讲解堆的**主要**思路,切忌一上来就死怼代码!(欸欸,完全不了解堆的数据结构 赶快3w baidu.扛母 去啊!!!这里只是复习回顾哇)

  • 首先 仅针对这道题 往里加的情况 那么堆有两种情况:

1. 堆没满

2. 堆满了

明白以上两点,就理解了50%啦!怎么样 通俗易懂吧

  • 接着继续分析两种情况:

  1. 堆没满的话,直接从末尾(tail)加入一个新的值,通过 与 父节点比较谁小,小的话交换,大的话break, 我把这个过程称为 up

  1. 堆满的话,如果比首部大,则与首部(下标为0)直接交换,通过判断左右节点是否有比它小的,有就与更小的交换,没有就break, 我把这个过程称为 down

思路OK,代码的实现 岂不是信手拈来?

咦?就这

**面试前给自己试试手! 希望大家都能有个满意的offer~**

class Solution {
    public int findKthLargest(int[] nums, int k) {
        MyPriorityQueue pq = new MyPriorityQueue(k);
        for (int num : nums) {
            pq.add(num);
        }
        return pq.peek();
    }

    class MyPriorityQueue {
        private int[] tree;
        private int tail = -1; //尾指针,代表当前末尾

        public MyPriorityQueue(int k) { //最小堆!!!!所以以下思路都是以最小堆展开的,最大堆反过来就行
            tree = new int[k];
        }

        void add(int val) {
            if (tail == -1) {
                tree[++tail] = val; //一开始没数据 直接加
                return;
            }
            if (tail == tree.length - 1) {//正如上面分析 满了我就往下走(down),没满我就往上爬 (up)
                down(val); 
            } else {
                up(val);
            }
        }

        private void up(int val) {
            tree[++tail] = val;
            int index = tail;
            while(index != 0){ //爬到顶就没有了 因为自己就是万子之父
                int parent = getParent(index);
                if (tree[parent] <= tree[index]){
                    return;
                }
                swap(parent, index);
                index = parent;
            }
        }

        private void down(int val) {
            if (val < tree[0])  return;
            int n = tree.length, index = 0;
            tree[index] = val;

            while (true){
                int left = getLeft(index);
                int right = getRight(index);
                if (left >= n)  break; // 左节点下标都超了 直接返回,不理解这里的同学请注意:堆是个完全二叉树(敲黑板)
                if (right >= n){
                    if (tree[left] >= tree[index])    break; // 右节点超了只能对比左节点咯
                    swap(left, index);
                    break;
                }else {
                    int minIndex = tree[left] < tree[right]? left : right; //找出两个值最小的节点
                    if (tree[minIndex] >= tree[index])    break;//最小的还比我大,那我就没有必要进行往下了。
                    swap(minIndex, index);
                    index = minIndex;
                }
            }
        }
        int peek(){
            return tail >= 0? tree[0] : -1; 
        }
        private int getParent(int index){

            //return (index / 2) - ((index % 2) == 0? 1 : 0); 等价于下面的公式
            return (index >> 1) - ((index & 1) == 0? 1 : 0); //获取父节点下标
        }
        private int getLeft(int index){
            //return (index * 2) + 1; 等价于下面的公式
            return (index << 1) + 1; //获取左节点下标
        }
        private int getRight(int index){
            //return (index * 2) + 2; 等价于下面的公式
            return (index << 1) + 2;
        }

        private void swap(int index1, int index2){
            int t = tree[index1];
            tree[index1] = tree[index2];
            tree[index2] = t;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值