動態規划之最優二叉樹


原理來自於《算法導論》,其實和矩陣的動態規划基本一樣,所以這里就不作闡述了。

直接上代碼,通過構造了最優的root數組后,很容易再創建一個二叉樹(這一小部分大家可以自己理解后試試)。

關於代碼的說明,因為書上給出的是偽代碼,數組並沒有采用C語言格式,下標不是從0開始,所以算法和root數組我做了調整,讓其尊重了C語言數組格式。最后解釋最優二叉樹時,需要把C語言形式的root數組轉換為原來書上的數組格式,簡單的做法是下標加1后顯示就行了。

定多個預處理函數

#define LENGTHP (sizeof(p)/sizeof(p[0]))
#define LENGTHQ (sizeof(q)/sizeof(q[0]))
#define DYNAMICDEBUG(TY, NM, N)                                           \
            printf("Showing type:" #TY ", variable name:" #NM "\n");      \
            showbst<TY>((NM),(N))

optimalbst函數,生成二維數組root, p, q,並填充數據。注意:此時的root數組內容是下標從0開始的。

int **optimalbst(float *p, float *q, int lengthp, int lengthq, float ***e, float ***w) {//返回root,記錄着對應的索引
    if (lengthp <= 0 || lengthq <= 0) return NULL;
    int **root = new int *[lengthp], i, j;
    float t;
    *e = new float *[lengthq];
    *w = new float *[lengthq];
    for (i = 0; i < lengthp; i++)
        root[i] = new int[lengthp];
    for (i = 0; i < lengthq; i++) {
        (*e)[i] = new float[lengthq];
        (*w)[i] = new float[lengthq];
    }
    //初始化數據
    for (i = 0; i < lengthp; i++)
        for (j = 0; j < lengthp; j++)
            root[i][j] = -1;
    for (i = 0; i < lengthq; i++)
        for (j = 0; j < lengthq; j++)
            (*e)[i][j] = (*w)[i][j] = -1;
    //動態規划算法
    for (i = 0; i < lengthq; i++)
        (*e)[i][i] = (*w)[i][i] = q[i];
    for (int l = 1; l <= lengthp; l++)
        for (i = 1; i <= lengthp - l + 1; i++) {
            j = i + l - 1;
            (*w)[i - 1][j] = (*w)[i - 1][j - 1] + p[j - 1] + q[j];//i下標全部調整,j下標保持
            for (int r = i; r <= j; r++) {
                t = (*e)[i - 1][r - 1] + (*e)[r][j] + (*w)[i - 1][j];
                if ((*e)[i - 1][j] == -1.0f || t < (*e)[i - 1][j]) {
                    (*e)[i - 1][j] = t;
                    root[i - 1][j - 1] = r - 1;
                }
            }
        }
    return root;
}

printfbst函數,解釋root數組。注意:此時要將下標轉換為原始數據的標號!!!

void printfbst(int **root, int i, int j, int n) {
    if (i == 0 && j == n - 1)
        printf("k%d是根\n", root[i][j]+1);
    if (i < j)
    {
        int index = root[i][j];
        if (index != i)
            printf("k%d是k%d的左節點\n", root[i][index - 1] + 1, index + 1);
        printfbst(root, i, index - 1, n);
        if (index != j)
            printf("k%d是k%d的右節點\n", root[index + 1][j] + 1, index + 1);
        printfbst(root, index + 1, j, n);
    }
    else if (i == j)
    {
        printf("d%d是k%d的左節點\n", i, i + 1);
        printf("d%d是k%d的右節點\n", i + 1, i + 1);
    }
    else
        printf("d%d是k%d的右節點\n", j + 1, j + 1);
}

數據錄入,從這里就看的出p數組的下標不滿足C語言數組性質,所以我作了修改,讓其滿足,這樣錄入數據就非常方便了!!!

float p[] = { 0.15,0.1,0.05,0.1,0.2 }, q[] = { 0.05,0.1,0.05,0.05,0.05,0.1 }

完整的main函數,用於測試

int main()
{
    float p[] = { 0.15,0.1,0.05,0.1,0.2 }, q[] = { 0.05,0.1,0.05,0.05,0.05,0.1 }, **e, **w;//k1 - k5, d0-d5
    int **root;//root保存的是數組下標
    root = optimalbst(p, q, LENGTHP, LENGTHQ, &e, &w);
    DYNAMICDEBUG(float, e,LENGTHQ);
    DYNAMICDEBUG(float, w, LENGTHQ);
    DYNAMICDEBUG(int, root, LENGTHP);
    printf("解釋最優二叉樹,數組下標轉換為實際的k1-k5, d0-d5\n");
    printfbst(root, 0, LENGTHP-1, LENGTHP);
    freebst<int>(root, LENGTHP);
    freebst<float>(e, LENGTHQ);
    freebst<float>(w, LENGTHQ);
    return 0;
}

幾個輔助函數,用於測試

template <typename T>
void freebst(T **p, int n) {
    for (int i = 0; i < n; i++)
        delete[] p[i];
    delete[] p;
}

template <typename T>
void showbst(T **p, int n) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++)
            if ((float)p[i][j] != -1.0f)
                printf("%.2f ", (float)p[i][j]);
            else
                printf("%-05s", " ");
        printf("\n");
    }
    printf("\n");
}

測試結果:

上圖分別對應書上的結果圖

e數組, w數組,以及root數組。注意:再三強調,經過修改,root數組內容是指向數組下標的,而非實際的數據標號。

 

 

 

再看對應的二叉樹解釋

 

所有代碼經過測試,結果正確!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM