二叉樹的遍歷轉換(層序,中序轉先序)


  眾所周知二叉樹有四種遍歷,即先序(DLR),中序(LDR),后序(LRD)和層序。而層序和其它三種不同,因為它是用的BFS即廣度優先搜索。我們可以簡單證明中序遍歷和其它的任何一種遍歷可以確定一棵樹。常見的有已知先序中序求后序,已知中序后序求先序(比如noip2001PJ的那道水題)。還有一種不常見的有層序中序求先后序。

  前面兩種太水,遞歸二分很容易做,后面一種做的時候沒反應過來想了會兒,本來想用遞歸,但是如果用遞歸的話一會兒二分左子樹一會兒二分右子樹,不方便,所以我改用隊列做。

  我們用一個隊列來儲存當前要二分的中序遍歷,先將一開始的完整的中序遍歷加進去。

  然后查找當前要處理的中序遍歷,將他二分,將左右兩部分分別加入隊列。再如此處理二分的兩部分隊列。

  重復這些操作,直至隊列為空(或者說是層序遍歷查找完畢),下面是主要部分的源代碼;

void make_tree(string str1,string str2,queue <string> que){
    que.push(str1);
    int now=1;
    for(int i = 0; i < str2.length(); i++) {
		char &k = str2[i];
		string &s = que.front();
		string left,right;
		int j;
		for(j = 0; j < s.length(); j++) {
			if(s[j] == k) {
				tree[now] = k;
				for(int l = j + 1; l < s.length(); l++)
					right = right + s[l];
				que.push(left);
				que.push(right);
				que.pop();
				break;
			}
			else
				left = left + s[j];
		}
		now++;
		if(j == s.length()) {                   //如果能在s中找到k,則j必定小於lens,而找不到的情況只可能出現在該子樹大小為0的情況
			i--;
			que.push(left);                 //加入一個大小為0的樹的中序遍歷,防止now不按順序來,下同
			que.push(right);
			que.pop();
		}
	}
}

  觀察上面的源代碼,可以發現,我們是將整棵樹補齊了,也就是說最壞的情況就是如下圖:

其中空心圓表示無結點,實心圓表示結點。

對於N個結點(如圖),時間復雜度上界為O(2^n · n^2);(應該沒算錯吧??)

如果沒算錯,那么這樣只能最多處理n=19的情況,過大的會超時,那我們該怎么優化這個呢?

首先為什么我們會執行2^n次操作呢?是因為我們如果不存2^n次則無法將每個結點對號入座。

那么我們可不可以用一個隊列來存儲結點應該做哪個位置呢?答案是可以的。

下面是代碼:

#include<iostream>
#include<queue>
#include<string>
#include<sstream>
#include<cstring>
using std::cin;
using std::string;
using std::queue;
char tree[10000 + 20];
int first(int);

int main(){
    string str1;    //中序
    cin >> str1;
    string str2;    //層序
    cin >> str2;
    queue <string> que;
    queue <int> place; 
    que.push(str1);
    place.push(1);
    for(int i = 0; i <= 10000 + 19; i++)
        tree[i] = '#';
    for(int i = 0; i < str2.length(); i++) {
        char &k = str2[i];
        string &s = que.front();
        string left,right;
        int j;
        for(j = 0; j < s.length(); j++) {
            if(s[j] == k) {
                tree[place.front()] = k;
                for(int l = j + 1; l < s.length(); l++)
                    right = right + s[l];
                if(left.empty() == false) {
                    que.push(left);
                    place.push(place.front() * 2);
                }
                if(right.empty() == false) {
                    que.push(right);
                    place.push(place.front() * 2 + 1);
                }
                que.pop();
                break;
            }
            else
                left = left + s[j];
        }
        place.pop();
    }
    first(1);
    return 0;
} 

int first(int x) {
    printf("%c" , tree[x]);
    if(tree[x * 2] != '#') {
        first(x * 2);
    }
    if(tree[x * 2 + 1] != '#') {
        first(x * 2 + 1);
    }
}

主要的改變就是用了一個place隊列保存下一個元素要出現的位置,這樣就用空間換時間

這段代碼的時間復雜度為O(n^3)?數學沒學好算不出來,總之就是快了好多好多。

 


免責聲明!

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



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