数据结构与算法之堆

  • 二叉树的顺序存储
  • 堆的应用

二叉树的顺序存储

      使用数组保存二叉树结构,方式即将二叉树用 层序遍历 方式放入数组中。 一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费,这种方式的主要用法就是堆的表示。
 
下标关系:
已知双亲 (parent) 的下标,则: 左孩子(left)下标 = 2 * parent + 1 ; 右孩子(right)下标 = 2 * parent + 2 ;
已知孩子(不区分左右) (child) 下标,则: 双亲(parent)下标 = (child - 1) / 2 ;
 

堆:

堆的一些定义:

1. 堆逻辑上是一棵完全二叉树
2. 堆物理上是保存在数组中
3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆; 反之,则是小堆,或者小根堆,或者最小堆
5. 堆的基本作用是,快速找集合中的 最值
6. 粗略估算,建堆可以认为是在循环中执行向下调整,为 O(n * log(n)) ,实际上是 O(n)
 
堆的创建及操作:向下调整
 
1.前提:左右子树必须已经是一个堆,才能调整。
 
思路分析:
1)首先要有一个数组,数组进行赋值
2)进行向下调整(先找到最后一个父节点)
      定义一个child指向子节点中较大的元素,当child中的元素大于root中的元素,两者进行交换。注意不仅要比较子树,还有比          较子树的子树
 

public class TestHeap {
    public int[] elem;
    public int usedSize;

    public TestHeap() {
        this.elem = new int[10];
        this.usedSize = 0;
    }

    //创建一个最大堆
    public void creatHeap(int[] array) {
        //赋值
        for (int i = 0; i < array.length; i++) {
            this.elem[i] = array[i];
            this.usedSize++;
        }

        //向下调整的方式进行调整 i代表每一棵树的根节点
        for (int i = (this.usedSize-1-1)/2; i >= 0 ; i--) {
            //是每一棵树都按照向下调整的方式进行调整
            AdjustDown(i,this.usedSize);
        }
    }

    public void AdjustDown(int root, int length) {
        //root是向下调整的父节点 调整child是向下调整的子节点的最大值
        int child = 2*root+1;
        while (child < length) {
            if (child+1 < length && this.elem[child] < this.elem[child+1]) {
                child = child+1;
            }

            if (this.elem[child] > this.elem[root]) {
                int tmp = this.elem[child];
                this.elem[child] = this.elem[root];
                this.elem[root] = tmp;
                root = child;  //调整子树以下所有的子树
                child = 2*root+1;
            } else {
                break;
            }
        }
    }

    public void show() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i] +" ");
        }
        System.out.println();
    }
 }

堆的应用

1、优先级队列
思路分析:
a.找到最后一个父节点
b.进行比较,如果左节点和右节点中较大的值大于父节点,父节点和较大的子节点进行交换
c.注意要比较子节点和子节点的子节点  child = root      root = (child-1)/2;
 
入队列:
    //入队列
    public boolean isFull() {
        return this.usedSize == this.elem.length;
    }

    //开始向上调整
    public void AdjustUp(int child) {
        int root = (child-1)/2;
        while (child > 0) {
            if(this.elem[child] > this.elem[root]) {
                int tmp = this.elem[child];
                this.elem[child] = this.elem[root];
                this.elem[root] = tmp;
                child = root;
                root = (child-1)/2;
            }else {
                break;
            }
        }
    }
    
    public void pushHeap(int val) {
        if (isFull()) {
           this.elem = Arrays.copyOf(this.elem, this.elem.length*2);
        }
        this.elem[usedSize] = val;
        this.usedSize++;
        //开始向上调整
        AdjustUp(this.usedSize-1);
    }

出队列:

思路分析:
a.把队头元素和队尾元素交换
b.usedSize--;
c.进行向上调整

    //出队列
    public boolean isEmpty() {
        return usedSize == 0;
    }

    public void popHeap() {
        if (isEmpty()) {
            return;
        }
        int tmp = this.elem[0];
        this.elem[0] = this.elem[usedSize-1];
        this.elem[usedSize-1] = tmp;
        this.usedSize--;
        AdjustDown(0, this.usedSize);
    }

得到队列第一个元素:

    public int getHeapTop() {
        if(isEmpty()) {
            return -1;
        }
        return this.elem[0];
    }

堆排序:

   public void heapSort() {
        int end = this.usedSize-1;
        while (end > 0) {
            int tmp = this.elem[end];
            this.elem[end] = this.elem[0];
            this.elem[0] = tmp;
            AdjustDown(0,end);
            end--;
        }

java当中的优先级队列PriorityQueue implements Queue

        Queue<Integer> queue = new PriorityQueue<>();  //默认为最小根堆
        queue.offer(1);
        queue.offer(2);
        System.out.println(queue.poll()); //1
        System.out.println(queue.peek()); //2

2、TOP-K问题

Top-K问题又称海量数据问题:eg有一亿个数据,找前k个最大的/前k个最小的???

注意如果找前k个最大的,建立大小为k的小堆。找前k个最小的,建立大小为k的大堆。

思路分析:将待找元素序列前k个元素建立大堆/小堆(为例),每次和堆顶元素进行比较。如果数组元素大于堆顶元素,进行替换,接着进行向下调整(先出栈顶元素,然后进入堆尾)。

3、堆排序问题*

从小到大排序 ==》大堆,从大到小 ==》小堆

发布了51 篇原创文章 · 获赞 14 · 访问量 2305

猜你喜欢

转载自blog.csdn.net/qq_41185460/article/details/103647001