数据结构与算法学习笔记(基础班四)---比较器与堆

数组实现堆

  • 利用位运算加快节点查找速度。根节点从1开始,0位置舍弃。
  • 从0位置开始,左孩子 =i2+1,右孩子=i2+2。

大根堆。

  1. 构建大根堆流程。
  • 在数组中依次加入元素,当前元素和和父亲节点比较
    1.如果当前节点小于父亲节点则停止。
    2.如果当前节点大于父亲节点,则和父亲节点交换,当前节点变为父亲节点。
    3.重复上述步骤直到小于父亲节点,或者当前节点就是父亲节点。
  1. 弹出堆顶元素。
  • 弹出堆顶元素,调整堆,使其重新变为大根堆。流程如下
    1.弹出堆顶元素,用数组中最后一个元素填补堆顶元素。
    2.重堆顶元素开始向下调整元素结构,如果堆顶元素大于等于其左右孩子的最大值则停止调整,若小于其左右孩子的最大值,则继续进行下面操作。
    3.把当前元素和左右孩子的最大值交换。
    4.重复2步骤直到不能交换为止。
  • 代码如下
public class Heap {

    // 数组代表堆结构
    private int[] heap;
    // 当前堆大小
    private int heapSize;
    // 堆得容量
    private int limit;
    public Heap(int limit){
        this.heap = new int[limit];
        this.limit = limit;
        this.heapSize = 0;
    }
    // 构建堆
    public void push(int value) throws Exception {
        heapInsert(value);
    }

    // 弹出堆顶元素
    public  int pop() throws Exception {
        if(heapSize == 0){
            throw new Exception("堆为空!");
        }
        int res = heap[0];
        // 交换堆顶元素和最后一个元素,堆大小减1
        swap(heap,0,--heapSize);
       heapIfy(heap,0);
        return res;
    }

    public  boolean isFull(){
        return this.heapSize == this.limit;
    }

    public boolean isEmpty(){
        return heapSize == 0;
    }

    // heapify
    private void heapIfy(int[] heap,int index){
        // 重新调整堆,使其变为大根堆
        // 计算其左孩子的下标
        int left = index * 2 + 1;
        // 有最孩子则继续调整
        while (left < heapSize){
            // 计算最有孩子中值最大的下标
            int largest = (left + 1 < heapSize) && heap[left] < heap[left + 1] ? left + 1 :left;
            // 比较父节点和左右孩子最大值谁大
            largest = heap[index] >= heap[largest] ? index : largest;
            if(largest == index){
                // 如果此时最大小标等于父节点下标,说明父节点值大,不用调整
                break;
            }
            // 交换
            swap(heap,largest,index);
            index = largest;
            left = index * 2 + 1;
        }
    }

    // heapInsert,给我一个元素,加入堆中,把堆调整为大根堆
    private void heapInsert(int value) throws Exception {
        if(heapSize == limit){
            throw new Exception("堆已满");
        }
        // 首先把元素加入数组中
        this.heap[heapSize++] = value;
        // 调整堆结构
        // 父元素下标
        int index = ((heapSize - 1) - 1) / 2;
        // 只要当前元素大于父元素就一直调整
        int cur = heapSize -1;
        while (heap[cur] > heap[index]){
            swap(heap,index,cur);
            cur = index;
            index = (index-1) / 2;

        }
    }

    private static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
     }
  
}

堆排序(时间复杂度O(nlogn),空间复杂度O(1))

  • 利用大根堆,每次都把堆顶元素放在未排好序区域的最后一个。
public class HeapSort {
    public void heapSort(int[] arr) throws Exception {
        if(arr == null || arr.length < 2){
            return;
        }
//        for (int i = 0; i < arr.length; i++) {
//            // 相当于一个一个加进去,调整为大根堆
//            // O(nlogn) n 个数每次调整代价都是log(n)
//            heapInsert(arr,i);
//        }

        for (int i = arr.length - 1; i >= 0 ; i--) {
            // O(n)
            heapIfy(arr,i,arr.length);
        }
        int size = arr.length - 1;
        while (size >= 0){
            swap(arr,0,size);
            heapIfy(arr,0,size--);
        }


    }

    private void heapInsert(int[] arr,int index) throws Exception {
        while (arr[index] > arr[(index - 1) / 2]){
            swap(arr,index,(index - 1) / 2);
            index = (index-1) / 2;

        }
    }

    private void heapIfy(int[] heap,int index,int heapSize){
        // 重新调整堆,使其变为大根堆
        // 计算其左孩子的下标
        int left = index * 2 + 1;
        // 有最孩子则继续调整
        while (left < heapSize){
            // 计算最有孩子中值最大的下标
            int largest = (left + 1 < heapSize) && heap[left] < heap[left + 1] ? left + 1 :left;
            // 比较父节点和左右孩子最大值谁大
            largest = heap[index] >= heap[largest] ? index : largest;
            if(largest == index){
                // 如果此时最大小标等于父节点下标,说明父节点值大,不用调整
                break;
            }
            // 交换
            swap(heap,largest,index);
            index = largest;
            left = index * 2 + 1;
        }
    }

    private void swap(int[]arr ,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

相关题目

  • 已知一个几乎有序的数组,几乎有序是指,如果把数组排好序的话,每个元素移动的距离一定不超过k,并且k相对于数组长度来说相对小,选择一个合适的排序策略对数组进行排序。
public class K_HeapSort {

    public static void sort(int[] arr,int k){
        if(arr == null || arr.length < 2 || k <= 0){
            return;
        }

        // 小根堆
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        // 先把前 k个数加入小根堆
        for (int i = 0; i < k; i++) {
            heap.add(arr[i]);
        }
        int index = 0;
        for (int i = k + 1; i < arr.length; i++) {
            // 堆顶弹出最小值,放入数组相应下标
            arr[index++] = heap.poll();
            heap.add(arr[i]);
        }

        while (!heap.isEmpty()){
            arr[index++] = heap.poll();
        }
    }
}

什么时候用系统堆,什么时候自己改写堆

  • 如果元素入堆以后只有加入和弹出操作,那么用系统堆就够了。
  • 如果元素入堆以后,会改变元素的值(引用类型),那么需要改写堆,支持重新调整堆结构。
    1.利用一张hash表记录入堆元素的下标,每次元素值改变是,通过hash表找到元素,进行堆结构调整。

比较器 Comparator 接口

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,635评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,543评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,083评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,640评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,640评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,262评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,833评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,736评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,280评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,369评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,503评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,870评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,340评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,460评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,909评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,512评论 2 359