原理來自於《算法導論》,其實和矩陣的動態規划基本一樣,所以這里就不作闡述了。
直接上代碼,通過構造了最優的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數組內容是指向數組下標的,而非實際的數據標號。
再看對應的二叉樹解釋
所有代碼經過測試,結果正確!!!