最近寫一個矩陣連乘的問題,在列印括號的時候遇到了難題,不知道括號和序列怎麼輸出才能得到標準的加括號序列,在網上查到了一個部落格發現是對的,在此記錄一下,順便和floyed列印路徑的方式作對比,以此來加深對二叉樹遍歷的理解。
首先上矩陣連乘的圖和程式碼
void Traceback(vector<int>p,int m,int n) //表示的是矩陣的值,這裡好像沒什麼用,因為實際上用的是s矩陣
{
if (m == n) {
cout << "A"<<m; return;
}
cout << "(";
Traceback(p, m, s[m][n]);
Traceback(p, s[m][n]+1, n);
cout << ")";
}
這裡的s陣列就是指在哪個地方加上括號。畫個圖來方便理解:
兩個問題:一是遇到葉結點直接返回,只要輸出值即可。
二是左括號和右括號的時機,分別是在第一次遇到結點,和離開結點的時候加上。類似先序和後序遍歷。
em,想了半天也不知道啥時候該輸出值,啥時候該輸出括號。記住遇到葉結點的時候直接返回了,不會深入葉結點的空間,而且每個葉結點只遇見一次,所以在葉節點列印值是最合理的。
對於每一個開裂的非葉子空間就要在進入空間時加左括號,離開時加右括號。這個確實不知道怎麼切入。
void Traceback(vector<int>p,int m,int n)
{
if (m == n) {
cout << "A"<<m; return;
}
cout << "(";
Traceback(p, m, s[m][n]);
cout << ")";
cout << "(";
Traceback(p, s[m][n]+1, n);
cout << ")";
}
這裡給每個中序的地方加上了左括號和右括號,也是對的,但是出現了很多的冗餘,因為即使有一個元素也被加上了括號。
結果如下:
兩相對比,就發現這個加括號在我看來很古怪。你想啊,就單從函式本身來看,在兩個遞迴之間斷開加上括號不是很合理嗎,s[m][n]也確實是其中的一個分割點。但是這麼加就會有很多冗餘,反而不如第一種標準的加法,雖然用二叉樹的遍歷方式可以理解,但是在自己思考的時候還是覺得遞迴可讀性可理解性確實差。
Floyd演算法程式碼:
void floyed(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
p[i][j]=k; //記錄插點
}
}
}
}
//列印路徑的程式碼
void path(int i,int j)
{
if(p[i][j]==0) return ; //沒有插點就返回
int k=p[i][j];
path(i,k); //列印插點
cout<<"->"<<k<<"->";
path(k,j);
}
int main()
{
cout<<a<<"->"; 。//第一個點和最後一個點是不會被輸出的,記得加上
path(a,b);
cout<<b;
}
這裡的輸出路徑就是遞迴搜尋兩個點之間的插點,也就是中序遍歷了,看下面的圖也可以理解。
總結一下,這種遞迴二叉樹的遍歷最好還是畫個圖理解一下,搞清楚在葉結點和非葉子結點分別要幹什麼事,其實也不是很難( 不難才怪 ···)