データ構造パート - プライオリティキュー(スタック)

基本プロパティ

またバイナリヒープ、パイルとして知られているプラ​​イオリティキュー、(NOT IN-メモリヒープは、混乱していないものには、メモリの領域、データ構造です)。

基本的にヒープが分かれ、完全なバイナリツリー、次のとおりです。

  • 分ヒープ(ヒープ細根):ツリーは、各非リーフノードは、左と右の子ノードの値より大きくない場合、ルートノードが最小スタック図である(A)。

  • 最大ヒープ(ヒープ大根):木、各非リーフノードは、左と右の子ノードの値より小さくない、ルートノードは、最大スタックであり、図(B)。

基本操作

たとえば、大規模なルートですヒープ

ストレージ

スタックは、ストレージ・アレイを使用して、本質的に完全二分木であるから([1] \)\格納を開始し、添字のため\(K \)ノード\([K] \)でありますインデックスでその左の子\(2 * K \)ラベルの下で、右の子\(2 * K + 1 \) そしてかどうか\(kは\) \ lfloor K / 2左(もしあれば)奇数または偶数、父・ノードであることは$ \である \右\ rfloor $を。

上方修正

私たちは、ヒープに要素を挿入した場合、それはヒープ構造に残っています。どのように私はそれを行う必要がありますか?

最後に、完全なバイナリツリーノードされた後、最終的には、上方(heapinsert)調整、配列の要素に追加することができます。値がスタック以上までファザーノードの上部に到達するまでウェイト値が父のノードよりも大きい場合、常に父ノード接合と比較して上方調節を調整し、そのファザーノードを交換するために、比較が繰り返されます。以下に調整上向きの図:

コードの下に、時間の複雑さがある(O(logN個)\)\

void heapinsert(int* arr, int n) {
    int k = n;
    //如果 K 结点有父节点,且比父节点的权值大
    while (k > 1 && arr[k] > arr[k / 2]) {
        //交换 K 与父节点的值
        swap(arr[k / 2], arr[k]);
        k >>= 1;
    }
}

この要素を追加することは非常に簡単です

void insert(int* arr, int n, int x) {
    arr[++n] = x;//将x置于数组末尾
    heapinsert(arr, n);//向上调整x
}

下方修正

私たちは、ヒープ要素のヒープのトップを削除したい場合は、ヒープ構造に残っています。どのように私はそれを行う必要がありますか?

スタック要素の上部を除去した後、スタックの先頭に最後の可動要素、およびこの下方調節要素(heapify)は、常に下方修正ノードに調整することができる\(K \)を比較ノードの左の子に現在のノードの重量比で子供がある場合は\(K \)大は、その後の重量は、ノードに子ノードの最大になります\(K \)ノードまで、繰り返しの比較(\ K \)リーフノードまたはノードである(K \)\値は子ノードよりもはるかに大きいです。下記の下方修正図:

コードの下に、時間の複雑さがある(O(logN個)\)\

void heapify(int* arr, int k, int n) {
    //如果结点 K 存在左孩子
    while (k * 2 <= n) {
        int left = k * 2;
        //如果存在右孩子,并且右孩子的权值大于左孩子
        if (left + 1 <= n && arr[left] < arr[left + 1])
            left++; //就选中右孩子
        //如果节点 K 的权值已经大于左右孩子中较大的节点
        if (arr[k] > arr[left])
            break;
        swap(arr[left], arr[k]);
        k = left;
    }
}

ヒープ要素のような削除トップは非常にシンプルになります

void deleteTop(int* arr, int n) {
    arr[1] = arr[n--];//用最后一个元素覆盖第一个元素,并让n-1
    heapify(arr, 1, n);
}

内蔵ヒープ

トップダウンヒープ建設

トップダウンヒープ考えは最初から構築することです\(iは1 \ =)の要素を常に前に、上方に調整され、開始\(I \)スタック構造を維持するための要素。時間複雑\(O(nlogn)\)

void ArrayToHeap(int *a,int n) {
    for (int i = 1; i <= n; i++) {
        heapinsert(a, i);
    }
}

ボトムアップヒープ建設

ヒープアイデアのボトムアップ構造は端から開始することで、私は\左\ lfloor N = $ 2 / \右\ rfloorの$の要素を、後にいつも聞かせて、下方向に調整することが\(NI \)スタックを維持するための要素を構造。

void ArrayToBheap(int *a, int n) {
    int i = n / 2;
    for (; i >= 1; i--) {
        heapify(a, i, n);
    }
}

コード上の唯一の視覚的な観察は、バイナリヒープ構造が来る場合は、時間の複雑さがある\(O(nlogn)\)の結果。もちろん、この上限は正しいが、漸近的に制限はなく、それが実行中の異なるノードで観察することができるheapify高い時にノードツリー(木の高さは、最低レベルのリーフノードへのノードの値を参照し、深さ)、関連する混乱が、接合部の高さの大部分が非常に小さいことがないようにしてください。次のプロパティを使用すると、より正確な漸近的境界を取得することができます:

  • \(H \)であっ\(N- \)要素は、スタックが\(H = \ lfloor logN個\ rfloor \) アップを含む\(\ lceil \ FRAC {N } {2 ^ {K + 1 }} \ rceil \)\(K \)ノード

[絵画星の木は、アルゴリズムの具体的な証拠の概要を参照してください、試すことができます]

高さ\(H \)ノードの実行のheapifyコストである\(O(H)\) 我々は構築することができ、トップダウンスタック全体的な複雑さは次のように表現されている
\ [\和^ {H} _ {K = 0} \ lceil \ FRAC {N} {2 ^ {K + 1}} \ rceil O(H)= O(N \和^ {H} _ {K = 0} \ FRAC {K} {2 ^ {K}})\]
この式
\ [\和^ {H}
_ {K = 0} \ FRAC {K} {2 ^ {K}} \] 実際、評価の前に\(N- \)項目および、高校の数学の知識
\ [T(K)= \ FRAC {1} {2} + \ FRAC {2} {2 ^ 2} + \ FRAC {3} {2 ^ 3} + \ cdots + \ FRAC {K} {2 ^ K} \\\ FRAC { 1} {2} T(K)= \ FRAC {1} {2 ^ 2} + \ FRAC {2} {3 ^ 2} + \ FRAC {3} {2 ^ 4} + \ cdots + \ FRAC {K-1} {2 ^ K} + \ FRAC {K} {2 ^ K + 1} \\ T(K) - \ FRAC {1} {2} T(K)= \ FRAC {1} {2} + \ FRAC {1} {2 ^ 2} + \ FRAC {1} {2 ^ 3} + \ cdots + \ FRAC {1} {2 ^ K} - \ FRAC {K} { 2 ^ {K + 1}} \\\ FRAC {1} {2} T(K)= \ FRAC {\ FRAC {1} {2}(1 - (\ FRAC {1} {2})^ K) } {1- \ FRAC {1} {2}} - \ FRAC {K} {2 ^ {K + 1}} \\ T(K)= 2- \ FRAC {1} {2 ^ {K-1} } - \ FRAC {K} {
2 ^ {K}} \] ここでは限界、数学の知識を要求することができる(\ FRAC {1} {\ \ 2 ^ {K-1}})場合\(K \)限界は無限大である\(0 \) のための\(\ FRAC {K} { 2 ^ {K}} \) リミットは、病院ルールである\(0 \)

すなわち、場合\(\ H)無限大になる傾向があり、\(O(N \ SUM _ ^ {H} = {0} K \ K FRAC {} {} 2 ^ {K})= O(N \ CDOT 2)\) 定数項を削除し、スタックのボトムアップ構築の複雑\(O(N)\)

ヒープソート

思想ヒープソート:大きなヒープルートがあると\(N- \)要素の各\(1 \)素子、第\(N- \)スイッチング素子番目、最初の要素が下方調整されています(heapify)、ならびにその\(1-N-N- = \)まで(N = 1 \)\

void heapSort(int* arr, int n) {
    //先自底向上建堆
    int i = n / 2;
    for (; i >= 1; i--) {
        heapify(arr, i, n);
    }

    for (int i = 50; i > 1; i--) {
        swap(arr[1], arr[i]);
        heapify(arr, 1, i - 1);
    }
}

Kの大きな要素を探して

まず、アレイの最初のk個の要素を有する構築物細根をスタックし、その後現在の要素は、スタックの最上部よりも大きい場合、スタックの上部および残りの配列比較を横断する、要素は、スタックの一番上に現在位置を入れて、スタック(heapify)を調整します。端部を通過した後、ヒープの最上部は、アレイである最小最大k個の要素であり、であり、k番目の最大要素

void heapify(int* a, int index, int length) {
    int left = index * 2 + 1;
    while (left <= length) {
        if (left + 1 <= length - 1 && a[left + 1] > a[left])left++;
        if (a[index] > a[left])break;
        swap(a[index], a[left]);
        index = left;
    }
}

void ArrayToBheap(int* a, int length) {
    int i = length / 2 - 1;
    for (; i >= 0; i--) {
        heapify(a, i, length);
    }
}

void FindKMax(int* a, int k, int length) {
    ArrayToBheap(a, k);
    for (int i = k; i < length; i++) {
        if (a[i] > a[0]) a[0] = a[i];
        heapify(a, 0, k);
    }
}

複雑時間\(O(N)\)は、単なる一例です。

実際には、この問題のためにクイックソート、時間の複雑さを考えて、より高速なアプローチです\(O(LOGN)\)

int Search_K(int left, int right, int k) {
    int i = left, j = right;
    int p = rand() % (right - left + 1) + left;
    int sign = a[p];
    swap(a[p], a[i]);
    while (i < j) {
        while (i < j && a[j] >= sign)j--;
        while (i < j && a[i] <= sign)i++;
        swap(a[i], a[j]);
    }
    swap(a[i], a[left]);
    if (i - left + 1 == k)return a[i];
    if (i - left + 1 < k)return Search_K(i + 1, right, k - (i - left + 1));
    else return Search_K(left, i - 1, k);
}

ヒープより多くの時間、それが構築されたため、ヒープ\(O(N)\)調整\(O(logN個)\)あなたがする必要があるとき、順序一部のデータがソートよりも優れている得るために(\(O(nlogn)\ ))アルゴリズム、及びデータを動的と呼ばれる実装のヒープがあるC ++ STLにソートアルゴリズムに完全に優れていることがパイルのサイズを大きくした場合priority_queue

おすすめ

転載: www.cnblogs.com/czc1999/p/11823460.html