1. 程式人生 > 其它 >【天梯pta】堆中的路徑 (25 分)

【天梯pta】堆中的路徑 (25 分)

求堆中的路徑,其實是可以看作是一道最小堆(小頂堆)的板子題。主要問題是解決建立最小堆,問題就可以迎刃而解了。

因此我們將此問題分解成兩個子問題,第一步是建立最小堆,第二步是輸出路徑。其中建立最小堆更為關鍵,輸出路徑則容易得多。

一、建立最小堆

【關鍵詞】最小堆,線上處理,一維陣列,遞迴,完全二叉樹

【知識點Tips】

  1.線上處理:每當輸入一個數字,就實時進行操作。

  2.完全二叉樹:一層一層按照順序填入的二叉樹。當不能全部填滿的時候,最後一層靠左填滿。(表達能力有限,類似下圖的既為完全二叉樹)

      

  3.最小堆:父結點的數字比它的左右子結點的數字小即可。至於左右子結點的大小與它們在左在右並無關係。

按照層的從上到下從左到右編號,即可獲取規律:左子結點的編號/2==父結點的編號,右子結點的編號/2==父結點的編號。例:2/2=1,3/2=1(整數相除則答案取整);6/2=3,7/2=3。

      

  

【最小堆建立的背景】給定數字個數與一列數字,要求輸出按照最小堆方法的陣列。

【例】

  5

  46 23 26 24 10

【思考步驟】

  第一步:獲取46後,因為是空堆,所以我們直接將其插入陣列第一位。a[1]=46。

  第二步:獲取23後,我們先將其直接接在陣列a的最後一個元素46後面。a[2]=23,陣列a暫時為46 23。我們要保證,每次新插入一個數字後的陣列是一個標準的最小堆陣列,因此要實時給出操作。所以我們要比較編號為2與其編號為2/2=1的父結點的大小。若比父結點小,則交換兩者位置,從而保證小的結點總在上面。

於是比較46與23的大小,23<46,則交換23與46的位置。因為23已經是陣列第一位了,則陣列a為23 46。

  第三步:獲取26後,我們先將其直接接在陣列a的最後一個元素46後面。a[3]=26,陣列a暫時為23 46 26。按照上面所講述原則,我們比較編號為3與其編號為3/2=1(至於為什麼3/2=1則參照上文Tips3)的父結點的大小。26>23,則我們不改變陣列位置。因為23已經是陣列第一位了,則陣列a為23 46 26。

  第四步:獲取24後,直接接在a陣列後面。a[4]=24,陣列a暫時為23 46 26 24。比較編號為4的24與其編號為4/2=2的父結點46的大小。24<46,則交換24與46的位置,陣列a暫時為23 24 26 46。

請注意,我們並沒有比較到陣列的開頭,因此依舊無法保證此陣列為目前的最小堆陣列。因此我們再次比較編號為2的24與其父結點2/2=1的23大小,得出不用改變,則陣列a為23 24 26 46。

  第五步:獲取10後直接接在a陣列後面,則陣列a暫時為23 24 26 46 10。比較編號為5的10與其編號為5/2=2的父結點24的大小。10<24,則交換10與24的位置,陣列a暫時為23 10 26 46 24。比較編號為2的10與其編號為2/2=1的父結點23的大小。10<23,則交換10與23的位置。因為此時已經比較到陣列的首位,所以陣列a結果為10 23 26 46 24。

【關鍵提要】

  可見,我們對於每一個新加入的數字進行的操作都是相同的,即:先接在陣列後面,再把新數字像“冒泡”一樣跟其父結點比較,若比父結點小則交換,直至陣列首位。以及子結點跟父結點有關係,則我們很容易想到用遞迴來解決此問題。

二、輸出路徑

  既然我們已經成功建立了最小堆的陣列,以及清楚了子結點與父結點的關係後,輸出路徑已經不是難事了。輸出即:子結點,每次的子結點的編號/2的結點值,直至編號到1.

  在這裡我用了一個小技巧。添加了一個bool值來將建立最小堆和輸出路徑在一個函式裡解決。建立的標誌是0,要輸出的標誌是1.要記得每次輸出要放在函式的開頭,要不然在結點編號為1的時候直接return則沒機會輸出了。

三、AC程式碼

#include <bits/stdc++.h>
using namespace std;

int n, m;
int a[100001];
bool flag;

void func(int index,bool f) {
    if (flag) {
        cout << a[index];
        if (index != 1) {
            cout << " ";
        }
    }
    if (index == 1) {
        return;
    }
    if (a[index / 2] > a[index]) {
        int t = a[index];
        a[index] = a[index / 2];
        a[index / 2] = t;
    }
    func(index / 2, flag);
}
int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; func(i, flag); } int t; flag = 1; while (m--) { cin >> t; func(t, flag); cout << endl; } return 0; }