1. 程式人生 > >從遞迴(程式碼)的角度理解二叉樹的先序/中序/後序三種遍歷順序

從遞迴(程式碼)的角度理解二叉樹的先序/中序/後序三種遍歷順序

二叉樹的遍歷原理其實是很簡單的,就是從二叉樹的根節點開始,把它的左節點以及左節點下面的節點再當作一棵二叉樹,和右節點以及右節點下面的節點也再當作一棵二叉樹,依此類推,直到節點下面沒有節點為止。

二叉樹的有三種遍歷順序:先序遍歷、中序遍歷、後序遍歷,而這個順序都是以根節點作為參照的,最先遍歷根節點就是先序遍歷,最後遍歷根節點就是後序遍歷,根節點放在左右節點之間的情況就是中序遍歷。

先看程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct treenode {
	void *data;
	struct treenode *left;
	struct treenode *right;
} treenode;

typedef struct tree {
	treenode *root;  //根節點
	//其他屬性...
} tree;

treenode *make_node(void *data){
	treenode *node = malloc(sizeof(treenode));
	bzero(node, sizeof(*node));
	node->data = data;
	node->left = NULL;
	node->right = NULL;
	return node;
}

// 先序遍歷 根節點 -> 左節點 -> 右節點
void first_sort_var_dump(const treenode *node) {
	if (node == NULL) {
		return;
	}
	printf("node->data: %s\n", (const char *)node->data);

	first_sort_var_dump(node->left);
	first_sort_var_dump(node->right);
}

// 中序遍歷 左節點 -> 根節點 -> 右節點
void middle_sort_var_dump(const treenode *node) {
	if (node == NULL) {
		return;
	}
	middle_sort_var_dump(node->left);

	printf("node->data:%s\n", (const char *)node->data);

	middle_sort_var_dump(node->right);
}

// 後序遍歷 左節點 -> 右節點 -> 根節點
void last_sort_var_dump(const treenode *node) {
	if (node == NULL) {
		return;
	}
	last_sort_var_dump(node->left);
	last_sort_var_dump(node->right);

	printf("node->data:%s\n", (const char *)node->data);
}

int main(){

	tree *t = malloc(sizeof(tree));
	bzero(t, sizeof(*t));

	treenode *root = make_node("I am root...");
	treenode *node1 = make_node("I am node1...");
	treenode *node2 = make_node("I am node2...");
	treenode *node3 = make_node("I am node3...");
	treenode *node4 = make_node("I am node4...");
	treenode *node5 = make_node("I am node5...");
	treenode *node6 = make_node("I am node6...");
	treenode *node7 = make_node("I am node7...");
	treenode *node8 = make_node("I am node8...");
	treenode *node9 = make_node("I am node9...");
	treenode *node10 = make_node("I am node10...");
	treenode *node11 = make_node("I am node11...");

	t->root = root;

	root->left = node1;
	root->right = node2;

	node1->left = node3;
	node1->right = node4;

	node3->left = node5;
	node3->right = node6;

	node5->left = node8;
	node8->right = node9;

	node2->left = node10;
	node2->right = node7;

	node7->left = node11;

	printf("先序遍歷....\n");
	first_sort_var_dump(t->root);

	printf("\n中序遍歷...\n");
	middle_sort_var_dump(t->root);

	printf("\n後序遍歷...\n");
	last_sort_var_dump(t->root);

	return 0;
}

以上程式碼構造了一顆如下關係所示的二叉樹:


從程式碼中可以非常直觀的看到三種遍歷順序之間的不同,先序遍歷是先獲取根節點的值 然後對左右節點進行遞迴,中序遍歷是先遞迴左節點,然後獲取根節點的值,最後遞迴右節點,後序遍歷原理相同,然後結合遞迴的壓棧和彈棧順序,就可以分析出遍歷的順序了,所以說三種遍歷順序就是不同的遞迴呼叫順序的表現

所以表現出以下的列印結果:

先序遍歷....
node->data: I am root...
node->data: I am node1...
node->data: I am node3...
node->data: I am node5...
node->data: I am node8...
node->data: I am node9...
node->data: I am node6...
node->data: I am node4...
node->data: I am node2...
node->data: I am node10...
node->data: I am node7...
node->data: I am node11...

中序遍歷...
node->data:I am node8...
node->data:I am node9...
node->data:I am node5...
node->data:I am node3...
node->data:I am node6...
node->data:I am node1...
node->data:I am node4...
node->data:I am root...
node->data:I am node10...
node->data:I am node2...
node->data:I am node11...
node->data:I am node7...

後序遍歷...
node->data:I am node9...
node->data:I am node8...
node->data:I am node5...
node->data:I am node6...
node->data:I am node3...
node->data:I am node4...
node->data:I am node1...
node->data:I am node10...
node->data:I am node11...
node->data:I am node7...
node->data:I am node2...
node->data:I am root...

先序比較相對好理解,這裡以後序為例介紹一下遞迴呼叫棧的順序

1、對 node1 和 node2 的遞迴分別稱作 func1 和 func2,func1 執行完再執行func2,最後列印root節點的值,棧示意圖如下:


2、此時程式執行func1,func1 又對node3 和 node4 進行遞迴,把遞迴函式稱為 func3 和 func 4,先呼叫 func3, func3 進棧


3、依次類推,node5的遞迴func5 和 node8的遞迴func8 依次進棧


4、func8 中 又對node8的左右節點進行遞迴,由於node8沒有左節點 該遞迴我們記為 func8left,此時執行func8left, 也就是func8left進棧,由於func8left的引數為null,此時函式沒有後續遞迴呼叫直接返回,然後彈棧了

 

5、func8left執行完並彈棧,此時對node9的遞迴 func9 進棧


6、node9的左右子節點都為null,所有 func9left 進棧啥也不做然後彈棧,接著 func9right 進棧 也啥都不做彈棧,然後列印node9的值, func9執行結束彈棧

7、func8 列印 node8的值彈棧

8、func5 列印node5的值彈棧

9、由於先列印左右子節點,func5 程式碼之後執行 node6 的遞迴 func6,func6 進棧

10、func6的左右節點都為null,列印 node6的值, func6 彈棧, func3 列印node3的值彈棧

11、然後列印node4和node1的值,func1彈棧之後 ,執行二叉樹右半部分node2的遞迴 func2

12、右半部分執行完最後列印root的值

從以上看出,從遞迴的角度分析二叉樹的三種不同遞迴順序相對純理論的方式更形象,也更容易理解。

that's all!

相關推薦

recursion演算法與1

筆者按:曾經剛開始學習資料結構和演算法時,總會為簡潔雋永的遞迴程式碼而驚歎,也想寫出如此優雅的程式碼,但是思考過程真的實屬不易!!!那時候遞迴都會盡量用顯式棧來規避。 生活中的遞迴!        首先,對遞迴要有一個類似盜夢空間或者平行世界的認識,就

程式碼角度理解//順序

二叉樹的遍歷原理其實是很簡單的,就是從二叉樹的根節點開始,把它的左節點以及左節點下面的節點再當作一棵二叉樹,和右節點以及右節點下面的節點也再當作一棵二叉樹,依此類推,直到節點下面沒有節點為止。二叉樹的有三種遍歷順序:先序遍歷、中序遍歷、後序遍歷,而這個順序都是以根節點作為參照

——前、以及非寫法

#include <iostream> #include <stack> #include <queue> using namespace std; typedef struct Node{ int data; Node *l

經典演算法之非演算法實現前、

/************************ author's email:[email protected] date:2017.12.24 非遞迴演算法實現二叉樹前、中、後序遍歷 ************************/ #include<

劍指Offer:重建

中序 span 問題 找到 div 節點 left 假設 array 題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,

javaleetcode617 合併Merge Two Binary Trees

題目描述: 給定兩個二叉樹,想象當你將它們中的一個覆蓋到另一個上時,兩個二叉樹的一些節點便會重疊。 你需要將他們合併為一個新的二叉樹。合併的規則是如果兩個節點重疊,那麼將他們的值相加作為節點合併後的新值,否則不為 NULL 的節點將直接作為新二叉樹的節點。 輸入: Tr

劍指offer系列4:重建

題目描述 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。 樣例分析 例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回{1,2,

劍指offer之 重建

題目: 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。 思路: 前序遍歷: 根左右 中序遍

PTA 資料結構與演算法題目集中文 6-9

6-9 二叉樹的遍歷(25 分)本題要求給定二叉樹的4種遍歷。函式介面定義:void InorderTraversal( BinTree BT ); void PreorderTraversal( Bi

PTA資料結構與演算法題目集中文4-9 (25分)

本題要求給定二叉樹的4種遍歷。 函式介面定義: void InorderTraversal( BinTree BT ); void PreorderTraversal( BinTree BT ); void PostorderTraversal( BinTree BT

劍指offerC++——對稱的

題目描述 請實現一個函式,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的映象是同樣的,定義其為對稱的。 struct TreeNode { int val; struct

函式和常用模組【day04】:

本節內容 作用域、區域性和全域性變數 遞迴 函數語言程式設計 高階函式和eval()函式 一、概述 在函式內部,可以呼叫其他函式。但是一個函式在內部呼叫自身,這個函式被稱為遞迴函式。 二、簡單介紹 那遞迴具體是怎麼實現的吶?下面我們就來看看如下程式碼:

的前和非版本

各位讀者週末愉快呀,今天我想來說說一道很常見的面試題目 —— 關於二叉樹前中後序遍歷的實現。本文將以遞迴和非遞迴方式實現這 3 種遍歷方式,程式碼都比較短,可以放心食用。 先簡單說明一下這 3 種遍歷方式有什麼不同 —— 對於每種遍歷,樹中每個結點都需要經過 3 次(對於葉結點,其左右子樹視為空子樹),但前

通過詳解,認識recursion

首先先對遞迴進行入門。 遞迴是以自相似的方式重複專案的過程。在程式語言中,如果程式允許您在同一函式內呼叫函式,則稱其為函式的遞迴呼叫。 簡而言之,遞迴就是函式的自身呼叫。可以看看下面的遞迴使用: void Recursive() { Recursive();//call itself

分別用、迴圈、bisect實現查詢python實現

1、遞迴實現二叉查詢 def binary_search_recursion(lst,target,low,high): if high < low: return None middle = (low + high)//2 if lst[middl

演算法解析XML思路 申請專利

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

的前+非

/** * 二叉樹節點類 * @author wj * */ class TreeNode{ int value; TreeNode left_Node; TreeNode right_Node; public TreeNode(int value) { this.value

鏈式 、非

參考部落格:click here! 鏈式二叉樹儲存結構: typedef int DataType; typedef struct BiNode { DataType data; struct BiNode *lc, *rc; // 左右子節點指標 int depth; } B

與非及C語言實現

二叉樹先序遍歷的實現思想是: 訪問根節點; 訪問當前節點的左子樹; 若當前節點無左子樹,則訪問當前節點的右子樹; 圖 1 二叉樹   以圖  1 為例,採用先序遍歷的思想遍歷該二叉樹的過程為: 訪問該二叉樹的根節點,找到 1; 訪問節點 1 的左子樹,找到節點 2; 訪問節點 2 的左子

資料結構作業8--棧的應用與選擇題

2-1令P代表入棧,O代表出棧。若利用堆疊將中綴表示式3*2+8/4轉為字尾表示式,則相應的堆疊操作序列是: (3分) A.PPPOOO B.POPOPO C.POPPOO D.PPOOPO 作者: DS課程組 單位: 浙江大學