L2_006樹的遍歷(後序+中序->前序/層序)

weixin_34075268發表於2018-03-27

給定一棵二叉樹的後序遍歷和中序遍歷,請你輸出其層序遍歷的序列。這裡假設鍵值都是互不相等的正整數。
輸入格式:
輸入第一行給出一個正整數N(<=30),是二叉樹中結點的個數。第二行給出其後序遍歷序列。第三行給出其中序遍歷序列。數字間以空格分隔。
輸出格式:
在一行中輸出該樹的層序遍歷的序列。數字間以1個空格分隔,行首尾不得有多餘空格。
輸入樣例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
輸出樣例:
4 1 6 3 5 7 2


  • 後序+中序->前序
    因為輸出順序是根,左樹,右樹,就可以先處理根,然後遞迴的處理左樹,再遞迴的處理右樹
    每一步進行處理時先根據結點node在後序中的下標輸出node(下標時呼叫函式時就已知的),接下來遞迴,需要知道左,右子樹根在後序中的下標,
    左子樹根的下標=根後序下標-右子樹的長度-1
    右子樹的長度=樹中序中最後一個結點的下標-根的中序下標
    樹的中序下標,根據後序下標索引到值,然後在中序中for迴圈找,要注意這裡不是朝找整個中序陣列,而是以該點為根的樹在中序中的起始與結尾這段
    所以遞迴時還應該傳入以該節點為根的樹在中序中起始點與結尾點的下標
    左子樹的開始=它父節點為根的樹的開始
    左子樹的結束=父節點中序下標-1
    右子樹根的下標=父節點後序下標-1
    右子樹的開始=父節點中序下標+1
    右子樹的結束=它父節點為根的樹的結束
    注意在中序中找根時,若根中序下標等於start說明沒有左樹,若等於end說明沒有右樹
    那麼遞迴到下一輪的時候就會造成start>end就可以return了
    8505225-e6990b74698bf11d.jpg
    求引數示意圖
#include <iostream>
#define N 31
using namespace std;
int back[N], mid[N];
void front(int kb, int start, int end)
{
    if (start > end)
        return;
    cout << back[kb]<<" ";
    int i = start;
    for (; i <= end; ++i) {
        if (mid[i] == back[kb])
            break;
    }//如果沒有左子樹的話i就會等於start
    //沒有右子樹i等於end
    //就會造成遞迴呼叫左右子樹時的start>end
    int km = i;
    int lkb = kb - (end - km) - 1;
    int lstart = start;
    int lend = km - 1;
    int rkb = kb - 1;
    int rstart = km + 1;
    int rend = end;
    front(lkb, lstart, lend);
    front(rkb, rstart, rend);
}
int main()
{
    int n; cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> back[i];
    }
    for (int i = 0; i < n; ++i) {
        cin >> mid[i];
    }
    front(n-1, 0, n - 1);
    system("pause");
    return 0;
}

  • 後序+中序->層序
    在前面轉前序的基礎上就只要修改一些就先行了
    由於遞迴是處理完了左樹才處理右樹的
    但層序遍歷並不是輸完了左樹才樹右樹的
    所以下面這部很重要
    在以前序的順序訪問到每個點時,不將它輸出,將他們儲存起來並有一個標記可以標識等會兒的輸出順序
    這裡定義一個index的向量,index的索引代表滿二叉樹中層序時每個結點的編號,值就代表結點的值,一開始把值全初始化為-1表示該索引下還沒有值
    然後遞迴呼叫時再傳入該結點在這棵滿樹上應該放的位置的索引,然後利用後序下標把值賦給這個索引,計算左右子樹的索引就可以利用2i+1,2i+2(下標從0開始)
    最後在遍歷這個index遇到非-1的輸出,若輸出的數量達到n了就不再遍歷了
    8505225-f696fa90cc9dc4cf.jpg

#include <iostream>
#include <vector>
#define N 31
using namespace std;
int back[N], mid[N];
vector<int> index(100000,-1);
void front(int kb, int start, int end,int ind)
{
    if (start > end)
        return;
//=======修改部分=================================
    index[ind]=back[kb];
//===============================================
    int i = start;
    for (; i <= end; ++i) {
        if (mid[i] == back[kb])
            break;
    }//如果沒有左子樹的話i就會等於start
    //沒有右子樹i等於end
    //就會造成遞迴呼叫左右子樹時的start>end
    int km = i;
    int lkb = kb - (end - km) - 1;
    int lstart = start;
    int lend = km - 1;
    int rkb = kb - 1;
    int rstart = km + 1;
    int rend = end;
    front(lkb, lstart, lend,ind*2+1);
    front(rkb, rstart, rend,ind*2+2);
}
int main()
{

    int n; cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> back[i];
    }
    for (int i = 0; i < n; ++i) {
        cin >> mid[i];
    }
    front(n-1, 0, n - 1,0);
    int cnt=0;
    for(int i=0;i<index.size()&&cnt<=n;++i){
        if(index[i]!=-1){
            if(i)
                cout<<" ";
            cout<<index[i];
            ++cnt;
        }
    }
    return 0;
}

以上參考:https://www.liuchuo.net/archives/2093


  • 另一個思路
    宣告一個結構體node,成員有這個點的值value,以這個值為根的樹的開始中序中下標start,結束end,這個值的後序下標
    然後利用佇列
    一開始把後序中最後一個結點入隊,start是0,end是n-1,後序下標是n-1
    每出隊一個點就輸出這個點的value,並計算出它左右孩子的以上引數,並且入隊,計算方法和前面相同
    注意當根中序下標為start就沒有左孩子,為end就沒有有孩子,這兩種情況是不用入隊的
    因為層序遍歷本身就是採用佇列的,所以感覺還挺有道理的
    程式碼實現改天補

相關文章