【九度OJ】题目1078-二叉树遍历
生活随笔
收集整理的這篇文章主要介紹了
【九度OJ】题目1078-二叉树遍历
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目
這道題和后面的兩道題,題目1201和題目1009,主要內容是對遞歸的實現,在邏輯上,遞歸是容易理解的,這種實現方式和我們思考的方式是相吻合的。但是在轉換為計算機語言時,要明確告知計算機應該從哪里開始啟用新一層的遞歸,帶著什么參數去啟用新一層的遞歸,最終返回什么值。所謂的一層遞歸,界限在哪里,遞歸函數體中的形參和邏輯流程中的實參要區分清楚。這些問題都是在實現遞歸時需要好好琢磨的。
思路
- 手工解決重構二叉樹時,先在“先序序列”中確定根,再在“中序序列”中確定左右子樹。
- 轉換為代碼,用遞歸來實現上述過程。用結構體存放樹。 用結構體來定義節點,用靜態數組存放樹。
- 之所以用靜態數組來存放樹,是因為這樣簡化了內存操作。標準做法是動態申請、動態釋放,但是這樣做需要更小心。
- 按照初始狀態實現第一個遞歸實例
- “遞歸總是以一個函數的形態出現”,實現第一個遞歸實例時,并沒有意識到這一點,于是先在main中寫出了遞歸的函數體,而后才抽出來寫成新的遞歸函數體build()。
- 按照思路中直觀地實現,給函數傳遞的參數都是“字符串”,包括先序序列、中序序列、左子序列、右子序列。
- 然后就不知道怎么進行遞歸了:怎樣自己(build)調用自己(build)呢?被困在這一步。
實現遞歸
- 遞歸函數build的參數必須十分講究:這些參數必須便于描述清楚“某次調用,將操作哪些數據。”
- 反思:上面被困住的原因是,build的參數選擇不當,每次都要傳入字符串,而且會拆分字符串,第二層遞歸build調用了子串A,那么余下的子串B怎么處理?暫存?
- 正確做法是,原來的數據本身在內存中一個地方不能動(不可傳入遞歸做修改),能很方便地移動的是下標、指針。
- 遞歸的參數一般都是“下標”或者“指針”這類索引性質的變量,不能是數據本身!
- 反思:遞歸函數的參數是變量,上面實現第一個遞歸實例時的想法并沒有這么明確
- 只是想著先把第一次遞歸的操作實現出來。
- 下一步的思路應該是,將這次實現中用到的遞歸函數的參數都“抽象出來、提取出來”——形成形式參數。
- 遞歸函數體中的形式參數是每一層遞歸實例中要用來接收實參的變量,如何從具體實現中選取合適的提取對象(形參的候選者)是關鍵。
- 當遞歸函數的形式參數是下標或指針時,調用新一層遞歸,主要是如何對這些下標和指針進行運算,來傳入正確的實參
- 先具體:從第一次遞歸實例進入第二次遞歸實例,需要如何移動下標,加減下標,可以用一些具體數字計算
- 后抽象:將上面用的數字,逐個抽象為公式
- 類似于數學歸納。
調試中的小問題
- strlen()在標準C++中的頭文件是cstring,在Virsual Studio中包含string也可使用,但在標準C++中卻編譯出錯。
- if判斷中,是否添加一個等號,都至關重要,可能就是一個等號,邏輯出錯,而且不是語法錯誤,要選擇全面的測試數據、調試很久才能發現。
完整代碼
#include <stdio.h> #include <string> //二叉樹結點定義 typedef struct BTNode {char data;BTNode * lchild;BTNode * rchild; } BTNode; //二叉樹定義 /*若用靜態數組就不需要這個 typedef struct BTree {BTNode * root;int nodeNum; } BTree; */ BTNode tree[50]; int loc; //用于保存靜態數組中已分配出去的元素的個數 char pre[30]; char in[30]; //char ltemp[30];這是舊思路中使用的 //char rtemp[30]; //該函數根據前序和中序將序列分解 /*遞歸寫的不好,傳入的參數不對,返回值也不對*傳入子串,修改子串,是直觀的,與思路相吻合的,但是不利于遞歸*下面用下標來作為參數傳入該遞歸函數,以下標來表示子串,便于遞歸操作 void splice(char & preOrder, char & inOder, char & leftTemp, char & rightTemp) {int i;for (i = 0; inOder[i] != preOrder[0]; i++);for (int j = 0; j < i; j++) {leftTemp[j] = preOrder[j];}i++;for (int j = 0; preOrder[i] != 0; i++, j++) {rightTemp[j] = preOrder[i];} } */ //如果是動態申請內存就是為新節點申請內存,這里只需要loc++就表示占用預分配的數組中一個位置,產生一個新節點 BTNode * creat() {tree[loc].lchild = tree[loc].rchild = NULL;return &tree[loc++]; } //構建二叉樹 BTNode * build(int s1, int e1, int s2, int e2) {BTNode * ret = creat(); //最終要返回的指針ret->data = pre[s1];int rootIdx;//【錯誤1】for (int i = s2; i < e2; i++) { 若子樹中僅有一個節點,沒有=直接不進入循環,造成錯誤for (int i = s2; i <= e2; i++){ //找到根節點在中序序列中的下標if (pre[s1] == in[i]) {rootIdx = i; break;}}if (s2 != rootIdx) {ret->lchild = build(s1 + 1, s1 + (rootIdx - s2), s2, rootIdx - 1); //【重要】遞歸的核心在此處的參數如何計算}if (rootIdx != e2) {ret->rchild = build(s1 + (rootIdx - s2) + 1, e1, rootIdx + 1, e2);}return ret; } //后序遍歷 void postOrder(BTNode *root) {if (root->lchild)postOrder(root->lchild);if (root->rchild)postOrder(root->rchild);printf("%c", root->data); } int main() {while (scanf("%s%s", pre, in) != EOF) {int start1 = 0, start2 = 0;int end1 = strlen(pre) - 1, end2 = strlen(in) - 1;loc = 0;BTNode * BTRoot = build(start1, end1, start2, end2); //一次調用就構建了二叉樹postOrder(BTRoot);printf("\n");}return 0; }轉載于:https://www.cnblogs.com/tambura/p/5227597.html
總結
以上是生活随笔為你收集整理的【九度OJ】题目1078-二叉树遍历的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ZOJ1027 Travelling F
- 下一篇: 使用 Vagrant 打造跨平台开发环境