プライオリティキュー(ヒープ)

目次

1. ヒープの概念

2. ヒープストレージ構造

3. ヒープの実装

3.1 シフトダウン()

3.2 シフトアップ()

3.3 シフトダウンとシフトアップの時間計算量

4. ヒープソート


1. ヒープの概念

ヒープは、優先度キューなどのアプリケーションを実装するためによく使用され、優先度が最も高い (または最も低い) 要素をすぐに見つけて削除できます。ヒープ操作には、新しい要素の挿入、ヒープ内の最上位要素(つまり、最高値) の削除、およびヒープ順序プロパティを満たすための既存のヒープの調整が含まれます。一般的なヒープ調整操作は、「フロート」 (アップ フィルター) と「シンク」 (ダウン フィルター)です。

ヒープには次の 2 つの主な特徴があります。

  1.  ヒープは完全なバイナリ ツリーです
  2.  ヒープ内の各ノードの値は、その子ノードの値以上 (または以下) である必要があります。

2. ヒープストレージ構造

ヒープの概念から、ヒープはその論理構造において完全なバイナリ ツリーであることがわかり、層の順序のルールに従って順番に効率的に格納できます。シーケンス テーブル。理解するために絵を描きます。

 要素を配列に保存した後、完全なバイナリ ツリーのプロパティに従ってツリーを復元できます。i が配列内のノードの添字であると仮定すると、次のようになります。

  • i = 0 の場合、i で表されるノードはルート ノードです。それ以外の場合、ノード i の親ノードは (i - 1)/2 です。
  • 2 * i + 1 がノード数より小さい場合、ノード i の左の子の添字は 2 * i + 1 になります。それ以外の場合、左の子はありません。
  • 2 * i + 2 がノード数より小さい場合、ノード i の右側の子のインデックスは 2 * i + 2 になります。それ以外の場合、右側の子は存在しません。

3. ヒープの実装

ここで実装されているのは、大規模なルート ヒープです。

public class Heap {
    private int[] elem;
    private int usedSize;

    public Heap(int[] arr){
        elem = new int[arr.length];
        createHeap(arr);
    }

    //建堆
    public void createHeap(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            elem[i] = arr[i];
            usedSize++;
        }
        for (int parent = (usedSize-2)/2; parent >= 0; parent--) {
            shiftDown(parent,usedSize);
        }
    }

    //向下调整
    public void shiftDown(int parent, int len){}

    public void swap(int i, int j){
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

    //入堆
    public void push(int val){}

    public boolean isFull(){
        return usedSize == elem.length;
    }

    //向上调整
    public void shiftUp(int child){}

    //出堆顶元素
    public int poll(){
        if(isEmpty()){
            System.out.println("堆中没有元素");
            return -1;
        }
        swap(0,usedSize-1);//将头尾交换
        usedSize--;//去掉堆顶元素
        shiftDown(0,usedSize);//重新排序
        return elem[usedSize];//返回堆顶元素
    }

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

    //得到堆顶元素
    public int peek(){
        if(!isEmpty()){
            return elem[0];
        }
        System.out.println("堆中没有元素");
        return -1;
    }
}

ヒープ内のコア コードは、shiftDown() メソッドとShiftUp() メソッドの実装です。他のメソッドについては、それらを見れば理解できます。これら 2 つのメソッドの実装について詳しく説明します。

3.1 シフトダウン()

たとえば、配列 {27, 15, 19, 18, 28, 34, 65, 49, 25, 37} をヒープにしたいのですが、どのように実装すればよいでしょうか? アイデア: 最初に最後のサブツリーを見つけて、パイルを形成した後、最後のサブツリーのルート ノードがツリー全体のルート ノードになるまで、他のサブツリーを順番に走査します。以下の図を参照してください。 

コードは以下のように表示されます:

    public void shiftDown(int parent, int len){
        int child = 2*parent + 1;
        while(child < len){
            if(child+1 < len && elem[child] < elem[child+1]){
                child++;
            }
            if(elem[parent] < elem[child]){
                swap(parent,child);
                parent = child;
                child = 2*parent + 1;
            }else{
                break;
            }
        }
    }

3.2 シフトアップ()

このメソッドは、要素を挿入するときにヒープの形式で要素を挿入します。基本的に、shiftDown とShiftUp の考え方は似ています。

    public void shiftUp(int child){
        int parent = (child-1)/2;
        while(parent >= 0){
            if(elem[child] > elem[parent]){
                swap(child,parent);
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }

3.3 シフトダウンとシフトアップの時間計算量

 ​​​​​​​​​​​​​​

 

4. ヒープソート

たとえば、昇順にソートしたい場合は、最初に大きなルート ヒープを作成し、次にルート ノードを最後のノードと交換する必要があります。このとき、最後のノードが最大である必要があるため、shiftDown を実行し、次に、ルート ノードを最後から 2 番目のノードと交換します。コードは以下のように表示されます:

/**
     * 堆排序
     * 时间复杂度:O(N*logN)
     * 空间复杂度:O(1)
     * 不稳定
     */
    public void heapSort(int[] arr){
        createHeap(arr);
        int end = arr.length-1;
        while(end > 0){
            swap(arr,0,end);
            shiftDown(arr,0,end);
            end--;
        }
    }
    private void shiftDown(int[] arr, int parent,int len) {
        int child = 2*parent+1;
        while(child < len){
            if(child+1 < len && arr[child] < arr[child+1]){
                child++;
            }
            if(arr[child] > arr[parent]){
                swap(arr,child,parent);
                parent = child;
                child = 2*parent+1;
            }else{
                break;
            }
        }
    }
    public void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    public void createHeap(int[] array) {
        for (int parent = (array.length-2)/2; parent >= 0; parent--) {
            shiftDown(array,parent,array.length);
        }
    }

おすすめ

転載: blog.csdn.net/m0_74859835/article/details/132024358