天天看點

動态規劃--最優二叉查找樹

問題:假設n個已經排序的關鍵字{1,2,…,n},他們被搜尋的機率分别為{p1,p2,…,pn},其它搜尋值則被這些關鍵字分割,它們的機率為{q0,q1,…,qn}。q0代表搜尋值小于1的可能性,qn代表搜尋值大于n的可能性,qi(i=1,2,,…,n-1)代表搜尋之大于i小于i+1的可能性。建構二叉搜尋樹,使得值搜尋的期望代價最小。

分析:令a[i][j]代表i->j的最優二叉搜尋樹的期望代價。于是我們可以得到以下遞推式:

動态規劃--最優二叉查找樹

這個算法的複雜度為O(n^3)。

Knuth已經證明對于所有的1 <= i < j <= n,總存在最優子樹的根使得root[i][j-1] <= root[i][j] <= root[i+1][j]。是以隻要在i<j時,在求min操作中,使root[i][j-1] <= k <= root[i+1][j]。此時算法複雜度為O(n^2)。

代碼:

#include <iostream>

#define MAXLENGTH 100

#define MAX 100

#define LEFT false

#define RIGHT true

using namespace std;

/**

* @brief Create the optimal binary search tree. The time complexity is O(n^3).

*

* @param p[]: the possibility of inner nodes. The nodes are assumed to have been sorted by their key

* @param q[]: the possibility of outer nodes. These nodes have the keys who are not in the binary search tree

* @param n: the number of inner nodes

* @param a[][MAXLENGTH]: a[i][j] means the minimum cost of tree i->j

* @param root[][MAXLENGTH]: root[i][j] means the root of tree i->j

*/

void OptimalBinarySearchTree(double p[], double q[], int n, double a[][MAXLENGTH], int root[][MAXLENGTH])

{

double w[MAXLENGTH][MAXLENGTH];

//initial states

for (int i = 0; i <= n; ++i)

{

a[i + 1][i] = q[i];

w[i + 1][i] = q[i];

}

for (int len = 1; len <= n; ++len)

{

for (int i = 1; i <= n - len + 1; ++i)

{

int j = i + len - 1;

a[i][j] = MAX;

w[i][j] = w[i][j - 1] + p[j] + q[j];

//if use {for (int k = root[i][j-1]; k <= root[i+1][j]; ++k)}, the time complexity will be O(n^2)

//this method only applies to such a situation when 1 <= i < j <= n, so when len == 1, we should not use this method

for (int k = i; k <= j; ++k)

{

double temp = a[i][k - 1] + a[k + 1][j] + w[i][j];

//get the minimum cost

if (temp < a[i][j])

{

a[i][j] = temp;

root[i][j] = k;

}

}

}

}

}

/**

* @brief Print the tree

*

* @param root[][MAXLENGTH]: the root matrix

* @param start: the start position of the tree

* @param end: the end position of the tree

* @param k: the parent, -1 means root has no parent

* @param LeftOrRight: the left or right subtree of the parent

*/

void PrintTree(int root[][MAXLENGTH], int start, int end, int k, bool LeftOrRight)

{

if (k == -1)

{

cout << "the root is p" << root[start][end] << endl;

PrintTree(root, start, root[start][end] - 1, root[start][end], LEFT);

PrintTree(root, root[start][end] + 1, end, root[start][end], RIGHT);

}

else if (LeftOrRight == LEFT)

{

if (start <= end)

{

cout << "the left child of p" << k << " is p" << root[start][end] << endl;

PrintTree(root, start, root[start][end] - 1, root[start][end], LEFT);

PrintTree(root, root[start][end] + 1, end, root[start][end], RIGHT);

}

else

{

cout << "the left child of p" << k << " is q" << start - 1 << endl;

}

}

else

{

if (start <= end)

{

cout << "the right child of p" << k << " is p" << root[start][end] << endl;

PrintTree(root, start, root[start][end] - 1, root[start][end], LEFT);

PrintTree(root, root[start][end] + 1, end, root[start][end], RIGHT);

}

else

{

cout << "the right child of p" << k << " is q" << start - 1 << endl;

}

}

}

int main()

{

double p[MAXLENGTH];

double q[MAXLENGTH];

double a[MAXLENGTH][MAXLENGTH];

int root[MAXLENGTH][MAXLENGTH];

int n;

while (cin >> n, n != 0)

{

for (int i = 1; i <= n; ++i)

{

cin >> p[i];

}

for (int i = 0; i <= n; ++i)

{

cin >> q[i];

}

OptimalBinarySearchTree(p, q, n, a, root);

cout << a[1][n] << endl;

PrintTree(root, 1, n, -1, true);

}

繼續閱讀