數據結構與算法之堆

  • 二叉樹的順序存儲
  • 堆的應用

二叉樹的順序存儲

      使用數組保存二叉樹結構,方式即將二叉樹用層序遍歷方式放入數組中。 一般只適合表示完全二叉樹,因爲非完全二叉樹會有空間的浪費,這種方式的主要用法就是堆的表示。
 
下標關係:
已知雙親(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、堆排序問題*

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章